我正在使用 EF5 fluent-api 尝试在多个表之间建立关系/约束,并且我希望这些关系包括删除时的级联,我认为我遗漏了一些简单的东西,因为我在下面尝试过的会产生上述错误。很长的帖子,但 99% 的代码,并不复杂,但在尝试重新初始化我的模型时收到下面提到的错误 - 有一些预期的约束,但没有找到。真的为此挠头……任何方向都将不胜感激。
namespace Deals.Core.DataAccess.Entities
{
public abstract class Entity<TEntity> : IEntity<TEntity> where TEntity : Entity<TEntity>, new()
{
private bool isEmpty;
protected Entity()
{
this.isEmpty = false;
}
public static TEntity Empty
{
get
{
return new TEntity() { IsEmpty = true };
}
}
public bool Active { get; set; }
public bool Deleted { get; set; }
[NotMapped]
public bool IsEmpty
{
get
{
return this.isEmpty;
}
protected set
{
this.isEmpty = value;
}
}
public int Version { get; set; }
}
public class Site : Entity<Site>, ISite
{
public Guid Id { get; set; }
public virtual ICollection<User> Users { get; set; }
public virtual Survey Survey { get; set; }
}
public class Survey : Entity<Survey>, ISurvey
{
public Guid Id { get; set; }
public virtual Site Site { get; set; }
}
public class User : Entity<User>, IUser
{
public Guid Id { get; set; }
public virtual UserProfile UserProfile { get; set; }
public Guid SiteId { get; set; }
public virtual Site Site { get; set; }
}
public class UserProfile : Entity<UserProfile>, IUserProfile
{
public Guid Id { get; set; }
public virtual User User { get; set; }
}
}
namespace Deals.Core.DataAccess.Models
{
public class Context : DbContext, IContext
{
public DbSet<Site> Sites { get; set; }
public DbSet<Survey> Surveys { get; set; }
public DbSet<User> Users { get; set; }
public DbSet<UserProfile> UserProfiles { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
this.MapSite(modelBuilder);
this.MapSurvey(modelBuilder);
this.MapUser(modelBuilder);
this.MapUserProfile(modelBuilder);
Database.SetInitializer(new MigrateDatabaseToLatestVersion<Context, ContextConfiguration>());
base.OnModelCreating(modelBuilder);
}
protected virtual void MapSite(DbModelBuilder modelBuilder)
{
// Ignore the IsEmpty property.
this.MapEntity<Site>(modelBuilder);
modelBuilder.Entity<Site>().HasKey(p => p.Id);
modelBuilder.Entity<Site>().HasOptional(p => p.Survey);
modelBuilder.Entity<Site>().HasOptional(p => p.Users);
}
protected virtual void MapUser(DbModelBuilder modelBuilder)
{
// Ignore the IsEmpty property.
this.MapEntity<User>(modelBuilder);
modelBuilder.Entity<User>().HasKey(p => p.Id);
modelBuilder.Entity<User>().HasRequired(p => p.Site).WithMany(p => p.Users).HasForeignKey(p => p.SiteId);
}
protected virtual void MapUserProfile(DbModelBuilder modelBuilder)
{
// Ignore the IsEmpty property.
this.MapEntity<UserProfile>(modelBuilder);
modelBuilder.Entity<UserProfile>().HasKey(p => p.Id);
// Why does adding .WillCascadeOnDelete() look for a user_id constraint on UserProfile that does not exist?
//modelBuilder.Entity<UserProfile>().HasRequired(p => p.User).WithRequiredPrincipal(user => user.UserProfile);
//// .WillCascadeOnDelete();
modelBuilder.Entity<UserProfile>().HasRequired(p => p.User);
}
protected virtual void MapSurvey(DbModelBuilder modelBuilder)
{
// Ignore the IsEmpty property.
this.MapEntity<Survey>(modelBuilder);
modelBuilder.Entity<Survey>().HasKey(p => p.Id);
modelBuilder.Entity<Survey>().HasRequired(p => p.Site);
//modelBuilder.Entity<Site>().HasOptional(p => p.Survey).WithOptionalPrincipal().WillCascadeOnDelete();
modelBuilder.Entity<Survey>().Property(p => p.SurveyXml).HasColumnType("xml").IsRequired();
}
#region Generic Mapping
protected virtual void MapEntity<T>(DbModelBuilder modelBuilder) where T : Entity<T>, new()
{
// Ignore the IsEmpty property.
modelBuilder.Entity<T>()
.Ignore(p => p.IsEmpty);
}
#endregion Generic Mapping
}
}
生成的 SQL:
create table [dbo].[Sites] (
[Id] [uniqueidentifier] not null,
[Url] [nvarchar](max) null,
[Description] [nvarchar](max) null,
[Active] [bit] not null,
[Deleted] [bit] not null,
[Version] [int] not null,
primary key ([Id])
);
create table [dbo].[Surveys] (
[Id] [uniqueidentifier] not null,
[SurveyXml] [xml] not null,
[Active] [bit] not null,
[Deleted] [bit] not null,
[Version] [int] not null,
primary key ([Id])
);
create table [dbo].[Users] (
[Id] [uniqueidentifier] not null,
[UserName] [nvarchar](max) null,
[Password] [nvarchar](max) null,
[LastLogin] [datetime] not null,
[SiteId] [uniqueidentifier] not null,
[Active] [bit] not null,
[Deleted] [bit] not null,
[Version] [int] not null,
primary key ([Id])
);
create table [dbo].[UserProfiles] (
[Id] [uniqueidentifier] not null,
[FirstName] [nvarchar](max) null,
[LastName] [nvarchar](max) null,
[MiddleInitial] [nvarchar](max) null,
[Honorific] [nvarchar](max) null,
[Email] [nvarchar](max) null,
[Active] [bit] not null,
[Deleted] [bit] not null,
[Version] [int] not null,
primary key ([Id])
);
Сan't get "on delete cascade" 在这里:
alter table [dbo].[Surveys] add constraint [Site_Survey] foreign key ([Id]) references [dbo].[Sites]([Id]);
这很好:
alter table [dbo].[Users] add constraint [User_Site] foreign key ([SiteId]) references [dbo].[Sites]([Id]) on delete cascade;
无法在此处获得“删除级联”:
alter table [dbo].[UserProfiles] add constraint [UserProfile_User] foreign key ([Id]) references [dbo].[Users]([Id]);
如果我这样做:
modelBuilder.Entity<UserProfile>()
.HasRequired(p => // p.User)
.WithRequiredPrincipal(user => user.UserProfile)
.WillCascadeOnDelete();
// Instead of this:
modelBuilder.Entity<UserProfile>().HasRequired(p => p.User);
// The sql that is generated looks correct:
alter table [dbo].[Users] add constraint [UserProfile_User]
foreign key ([Id]) references [dbo].[UserProfiles]([Id]) on delete cascade;
但是,在运行测试时尝试重新初始化我的模型时出现此错误;FK_dbo.UserProfiles_dbo.Users_Id 怎么了?
Test Name: SiteRepository_Remove_TestPasses
Test FullName: Deals.Core.Tests.Deals.Core.DataLibrary.Tests.Integration.SiteRepositoryIntegrationTests.SiteRepository_Remove_TestPasses
Test Source: c:\Dev\Deals\Deals.Core.Tests\Deals.Core.DataLibrary.Tests\Integration\SiteRepository.Integration.Tests.cs : line 51
Test Outcome: Failed
Test Duration: 0:00:01.4034458
Result Message:
Initialization method Deals.Core.Tests.Deals.Core.DataLibrary.Tests.Integration.SiteRepositoryIntegrationTests.TestInitialize threw exception. System.Data.SqlClient.SqlException: System.Data.SqlClient.SqlException: 'FK_dbo.UserProfiles_dbo.Users_Id' is not a constraint.
Could not drop constraint. See previous errors..
Result StackTrace:
at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
at System.Data.SqlClient.SqlCommand.RunExecuteNonQueryTds(String methodName, Boolean async, Int32 timeout)
at System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(TaskCompletionSource`1 completion, String methodName, Boolean sendToPipe, Int32 timeout, Boolean asyncWrite)
at System.Data.SqlClient.SqlCommand.ExecuteNonQuery()
at System.Data.Entity.Migrations.DbMigrator.ExecuteSql(DbTransaction transaction, MigrationStatement migrationStatement)
at System.Data.Entity.Migrations.DbMigrator.ExecuteStatements(IEnumerable`1 migrationStatements)
at System.Data.Entity.Migrations.DbMigrator.AutoMigrate(String migrationId, XDocument sourceModel, XDocument targetModel, Boolean downgrading)
at System.Data.Entity.Migrations.DbMigrator.Upgrade(IEnumerable`1 pendingMigrations, String targetMigrationId, String lastMigrationId)
at System.Data.Entity.MigrateDatabaseToLatestVersion`2.InitializeDatabase(TContext context)
at System.Data.Entity.Internal.InternalContext.PerformInitializationAction(Action action)
at System.Data.Entity.Internal.InternalContext.PerformDatabaseInitialization()
at Deals.Core.DataAccess.UnitOfWorkCore.ForceDatabaseInititialization() in c:\Dev\Deals\Deals.Core.DataAccess\UnitOfWorkCore.cs:line 164
at Deals.Core.Tests.Deals.Core.DataLibrary.Tests.Integration.SiteRepositoryIntegrationTests.TestInitialize() in c:\Dev\Deals\Deals.Core.Tests\Deals.Core.DataLibrary.Tests\Integration\SiteRepository.Integration.Tests.cs:line 28