EF 4.1代码优先:如何设计和映射这些实体?

时间:2011-10-06 08:04:54

标签: entity-framework-4.1 ef-code-first entity-relationship navigation-properties

我有3个实体:MemberAuthenticationTokenEmail

  1. 每个Member可能有多个 AuthenticationTokens
  2. 每个AuthenticationToken可能一个或零 Email
  3. 每个Member可能有零或一个 PrimaryEmail(来自Emails表)。 PrimaryEmail真的是AuthenticationToken与之关联的Email
  4. 之一

    所以我有:

    public class Member {
        public int MemberId { get; set; }
        public int? PrimaryEmailId { get; set; }
        public virtual Email PrimaryEmail { get; set; }
        public virtual ICollection<AuthenticationToken> AuthenticationTokens { get; set; }
    }
    
    public class AuthenticationToken {
        public int AuthenticationTokenId { get; set; }
        public int MemberId { get; set; }
        public virtual Member Member { get; set; }
        public virtual Email Email { get; set; }
    }
    
    public class Email {
        public int EmailId { get; set; } // is same as AuthenticationTokenId that the email associated with it
    }
    

    通过上面我解释过的设计,我可以添加Member和AuthenticationToken,但是当我想将一封电子邮件附加到一个成员或AuthenticationToken(或两者)时,我给出了这个错误:

      

    INSERT语句与FOREIGN KEY约束等冲突。

    这个设计是否正确? 如何设计我的表(和实体)以实现我的目的? 我如何在代码优先映射我的实体?你有什么想法吗?

    enter image description here

1 个答案:

答案 0 :(得分:1)

我个人使用EF 4.1中的流畅API来配置我的所有实体,当我觉得默认约定不会理解我时,所以我将使用流畅的API来回答。

以下是我如何设置模型:

public class Member
{
    public Member()
    {
        AuthenticationTokens = new List<AuthenticationToken>();
    }

    public int MemberId { get; set; }

    public virtual Email PrimaryEmail { get; set; }
    public virtual ICollection<AuthenticationToken> AuthenticationTokens { get; set; }
}

public class AuthenticationToken
{
    public int AuthenticationTokenId { get; set; }

    public virtual Email Email { get; set; }
}

public class Email
{
    public int EmailId { get; set; }
}

这是我的背景和流畅的配置:

public class ExampleApplicationContext : DbContext
{
    public ExampleApplicationContext()
        : base("ExampleApplicationConnection")
    {

    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        // No cascade on delete because the primary email may be held by an authentication token.
        modelBuilder.Entity<Member>()
            .HasOptional(x => x.PrimaryEmail)
            .WithOptionalDependent()
            .Map(x =>
            {
                x.MapKey("EmailId");
            })
            .WillCascadeOnDelete(false);

        // Cascade on delete because an authentication token not associated with a Member makes no sense.
        modelBuilder.Entity<Member>()
            .HasMany(x => x.AuthenticationTokens)
            .WithRequired()
            .Map(x =>
            {
                x.MapKey("MemberId");
            })
            .WillCascadeOnDelete();

        // No cascade on delete because an email may be held by a Member.
        modelBuilder.Entity<AuthenticationToken>()
            .HasOptional(x => x.Email)
            .WithOptionalDependent()
            .Map(x =>
            {
                x.MapKey("EmailId");
            })
            .WillCascadeOnDelete(false);
    }

    public DbSet<Member> Members { get; set; }
}

我会尽我所能来解释为什么我这样设计它的原因。首先,似乎在您的模型中Member应该是根聚合(其他实体的老板)。我的意思是Authentication Token没有意义,除非它属于特定的Member。除非Email属于Member或属于AuthenticationToken,否则AuthenticationToken也没有任何意义。出于这个原因,Member没有属性来找出它所附加的Member(为了找到这个,你首先需要一个Member,而不仅仅是看它的集合)。从本质上讲,一切都围绕着一个Member对象。如果没有AuthenticationToken,则无法创建Member。如果没有AuthenticationTokenEmail,则无法创建Email

我不完全确定您在EF 4.1中使用流畅的API有多舒服,所以如果您有任何问题请发表评论,我会尽力回答。我还包括一个小样本应用程序,我用它来构建和验证我上面提到的模型。如果要运行程序(它是一个小型控制台应用程序),您只需修改App.config中的连接字符串以指向您的SQL Server实例。

我担心的一件事是Member可以同时属于AuthenticationToken和{{1}}。我担心的是,我必须设置一些有趣的级联删除。但是,我不知道您的所有要求,并且此设置似乎工作正常,因此可能不是问题。

Example Console Application