2

私は 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])
);

ここで「カスケード削除」を取得できません:

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
4

2 に答える 2

0

これで直りました

私のデータベースは、テストの実行ごとに削除および再作成されていないようです...

私のDbContextで

protected override void OnModelCreating(DbModelBuilder modelBuilder)
      {
         Database.SetInitializer(new MigrateDatabaseToLatestVersion<Context, ContextConfiguration>());

         base.OnModelCreating(modelBuilder);
      }

私の移行構成

public class ContextConfiguration : DbMigrationsConfiguration<Context>
   {
      public ContextConfiguration()
      {
         this.AutomaticMigrationsEnabled = true;
         this.AutomaticMigrationDataLossAllowed = true;
      }
   }

私のテストで

[TestInitialize]
      public override void TestInitialize()
      {
         // Force model updates.
         using (var uow = UnitOfWorkFactory.Instance.Create<UnitOfWorkCore>(DefaultConnectionString))
         {
            uow.Database.Initialize(force: false);
         }

         // begin transaction
         this.transactionScope = new TransactionScope();

         this.unitOfWork = UnitOfWorkFactory.Instance.Create<UnitOfWorkCore>(DefaultConnectionString);
      }

これに変更しましたが問題ありません *これに変更しましたが問題ありません* これに変更しましたが問題ありません

私のテストで

[TestInitialize]
      public override void TestInitialize()
      {
         // Force model updates.

         // I CHANGED TO THIS:
         Database.SetInitializer(new DropCreateDatabaseAlways<Context>());

         using (var uow = UnitOfWorkFactory.Instance.Create<UnitOfWorkCore>(DefaultConnectionString))
         {
            uow.Database.Initialize(force: false);
         }

         // begin transaction
         this.transactionScope = new TransactionScope();

         this.unitOfWork = UnitOfWorkFactory.Instance.Create<UnitOfWorkCore>(DefaultConnectionString);
      }
于 2013-05-18T23:11:43.417 に答える
0

親オブジェクトが削除されたときに子オブジェクトを削除する場合は、関連付けの親側から構成する必要があります。最初Foreign Keyに子オブジェクトのプロパティを作成し[Required]、次に次のコードを作成します。

one-to-many relationshipsまた、one-to-one relationshipsカスケード削除とは異なり、必要な関係であっても、デフォルトでは有効になっていないことも知っておく必要があります。1 対 1 のリレーションシップのカスケード削除は、常に明示的に定義する必要があります。

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<UserProfile>()
                .HasRequired(u => u.User)
                .WithRequiredPrincipal(c => c.UserProfile)
                .WillCascadeOnDelete(true);

    modelBuilder.Entity<Survey>()
                .HasRequired(a => a.Site)
                .WithRequiredPrincipal(c => c.Survey)
                .WillCascadeOnDelete(true);
}
于 2013-05-17T15:57:00.713 に答える