这是我所拥有的最小测试案例。
public class Project
{
public int ProjectId { get; set; }
public string Name { get; set; }
}
public class Claim
{
[Key]
public int ClaimId { get; set; }
public int ProjectId { get; set; }
public virtual Project Project { get; set; }
public virtual CommentDiscussion Discussion { get; set; }
}
public class ForumThread
{
[Key]
public int ForumThreadId { get; set; }
public int ProjectId { get; set; }
public virtual Project Project { get; set; }
public virtual CommentDiscussion Discussion { get; set; }
}
public class CommentDiscussion
{
public int CommentDiscussionId { get; set; }
public int? ClaimId { get; set; }
public virtual Claim Claim { get; set; }
public int? ForumThreadId { get; set; }
public virtual ForumThread ForumThread { get; set; }
public int ProjectId { get; set; }
public virtual Project Project { get; set; }
}
modelBuilder.Entity<Claim>().HasRequired(c => c.CommentDiscussion).WithRequiredDependent(cd => cd.Claim);
modelBuilder.Entity<ForumThread>().HasRequired(c => c.CommentDiscussion).WithRequiredDependent(cd => cd.ForumThread);
我希望配置以下关系:Claim
只有一个CommentDiscussion
&amp; ForumThread
只有一个CommentDiscussion
。 CommentDiscussion
可以有Claim
或ForumThread
。
如果创建一个迁移,一切都很好。但是,如果我拆分为两个迁移,并首先创建除CommentDiscussion
之外的所有内容,则添加CommentDiscussion
的迁移将生成:
AddForeignKey("dbo.ForumThreads", "ForumThreadId", "dbo.CommentDiscussions", "CommentDiscussionId");
AddForeignKey("dbo.Claims", "ClaimId", "dbo.CommentDiscussions", "CommentDiscussionId");
这是非常错误的。即使我手动修复迁移,EF也会在加载时错误地映射所有内容。
答案 0 :(得分:-1)
你必须要照顾两件事。你想要存储什么以及什么是有效的。并非所有可能的值都有效。
在这种情况下,CommentDiscussion
必须包含Claim
或ForumThread
引用,但不得同时设置。
实体:
public class Claim
{
public int Id { get; set; }
public string Name { get; set; }
public CommentDiscussion CommentDiscussion { get; set; }
}
public class ForumThread
{
public int Id { get; set; }
public string Name { get; set; }
public CommentDiscussion CommentDiscussion { get; set; }
}
public class CommentDiscussion
{
public int Id { get; set; }
public int? ClaimId { get; set; }
public Claim Claim { get; set; }
public int? ForumThreadId { get; set; }
public ForumThread ForumThread { get; set; }
}
配置
public class ClaimConfiguration : EntityTypeConfiguration<Claim>
{
public ClaimConfiguration()
{
HasKey( e => e.Id );
Property( e => e.Name )
.IsRequired( )
.HasMaxLength( 100 );
}
}
public class ForumThreadConfiguration : EntityTypeConfiguration<ForumThread>
{
public ForumThreadConfiguration()
{
HasKey( e => e.Id );
Property( e => e.Name )
.IsRequired( )
.HasMaxLength( 100 );
}
}
public class CommentDiscussionConfiguration : EntityTypeConfiguration<CommentDiscussion>
{
public CommentDiscussionConfiguration()
{
HasKey( e => e.Id );
HasOptional( e => e.Claim )
.WithRequired( m => m.CommentDiscussion )
.Map( cfg => cfg.MapKey( nameof( CommentDiscussion.ClaimId ) ) );
HasOptional( e => e.ForumThread )
.WithRequired( m => m.CommentDiscussion )
.Map( cfg => cfg.MapKey( nameof( CommentDiscussion.ForumThreadId ) ) );
}
}
确保CommentDiscussion
的验证只能引用Claim
或ForumThread
:
public class ModelContext : DbContext
{
// Der Kontext wurde für die Verwendung einer ModelContext-Verbindungszeichenfolge aus der
// Konfigurationsdatei ('App.config' oder 'Web.config') der Anwendung konfiguriert. Diese Verbindungszeichenfolge hat standardmäßig die
// Datenbank 'ConsoleApp7.Model.ModelContext' auf der LocalDb-Instanz als Ziel.
//
// Wenn Sie eine andere Datenbank und/oder einen anderen Anbieter als Ziel verwenden möchten, ändern Sie die ModelContext-Zeichenfolge
// in der Anwendungskonfigurationsdatei.
public ModelContext()
: base( "name=ModelContext" )
{
}
protected override void OnModelCreating( DbModelBuilder modelBuilder )
{
modelBuilder.Configurations.Add( new ClaimConfiguration( ) );
modelBuilder.Configurations.Add( new ForumThreadConfiguration( ) );
modelBuilder.Configurations.Add( new CommentDiscussionConfiguration( ) );
base.OnModelCreating( modelBuilder );
}
protected override bool ShouldValidateEntity( DbEntityEntry entityEntry )
{
return base.ShouldValidateEntity( entityEntry );
}
protected override DbEntityValidationResult ValidateEntity( DbEntityEntry entityEntry, IDictionary<object, object> items )
{
if ( entityEntry.Entity is CommentDiscussion )
{
if ( !entityEntry.CurrentValues.GetValue<int?>( nameof( CommentDiscussion.ClaimId ) ).HasValue && !entityEntry.CurrentValues.GetValue<int?>( nameof( CommentDiscussion.ForumThreadId ) ).HasValue )
{
var list = new List<System.Data.Entity.Validation.DbValidationError>( );
list.Add( new System.Data.Entity.Validation.DbValidationError( nameof( CommentDiscussion.Claim ), "Claim or ForumThread is required" ) );
list.Add( new System.Data.Entity.Validation.DbValidationError( nameof( CommentDiscussion.ForumThread ), "Claim or ForumThread is required" ) );
return new System.Data.Entity.Validation.DbEntityValidationResult( entityEntry, list );
}
if ( entityEntry.CurrentValues.GetValue<int?>( nameof( CommentDiscussion.ClaimId ) ).HasValue && entityEntry.CurrentValues.GetValue<int?>( nameof( CommentDiscussion.ForumThreadId ) ).HasValue )
{
var list = new List<System.Data.Entity.Validation.DbValidationError>( );
list.Add( new System.Data.Entity.Validation.DbValidationError( nameof( CommentDiscussion.Claim ), "Only Claim or ForumThread is possible, not both" ) );
list.Add( new System.Data.Entity.Validation.DbValidationError( nameof( CommentDiscussion.ForumThread ), "Only Claim or ForumThread is possible, not both" ) );
return new System.Data.Entity.Validation.DbEntityValidationResult( entityEntry, list );
}
}
return base.ValidateEntity( entityEntry, items );
}
}
检查
using ( var context = new ModelContext() )
{
CommentDiscussion discussion = new CommentDiscussion( );
context.Set<CommentDiscussion>( ).Add( discussion );
try
{
context.SaveChanges( );
}
catch ( Exception ex )
{
Console.WriteLine( ex.ToString() );
}
Claim claim = new Claim( );
ForumThread forumThread = new ForumThread( );
claim.CommentDiscussion = discussion;
forumThread.CommentDiscussion = discussion;
try
{
context.SaveChanges( );
}
catch ( Exception ex )
{
Console.WriteLine( ex.ToString( ) );
}
}
<强>更新强>
这里为模型生成了迁移
public partial class InitialCreate : DbMigration
{
public override void Up()
{
CreateTable(
"dbo.Claims",
c => new
{
Id = c.Int(nullable: false, identity: true),
Name = c.String(nullable: false, maxLength: 100),
ClaimId = c.Int(nullable: false),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.CommentDiscussions", t => t.ClaimId)
.Index(t => t.ClaimId);
CreateTable(
"dbo.CommentDiscussions",
c => new
{
Id = c.Int(nullable: false, identity: true),
ClaimId = c.Int(),
ForumThreadId = c.Int(),
})
.PrimaryKey(t => t.Id);
CreateTable(
"dbo.ForumThreads",
c => new
{
Id = c.Int(nullable: false, identity: true),
Name = c.String(nullable: false, maxLength: 100),
ForumThreadId = c.Int(nullable: false),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.CommentDiscussions", t => t.ForumThreadId)
.Index(t => t.ForumThreadId);
}
public override void Down()
{
DropForeignKey("dbo.ForumThreads", "ForumThreadId", "dbo.CommentDiscussions");
DropForeignKey("dbo.Claims", "ClaimId", "dbo.CommentDiscussions");
DropIndex("dbo.ForumThreads", new[] { "ForumThreadId" });
DropIndex("dbo.Claims", new[] { "ClaimId" });
DropTable("dbo.ForumThreads");
DropTable("dbo.CommentDiscussions");
DropTable("dbo.Claims");
}
}