【问题标题】:EF 4.1 code-first: How to design and map these entities?EF 4.1 代码优先:如何设计和映射这些实体?
【发布时间】:2011-10-06 08:04:54
【问题描述】:

我有 3 个实体:MemberAuthenticationTokenEmail

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

所以我有:

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
}

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

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

这个设计对吗??? 如何设计我的表(和实体)以实现我的目的? 以及如何在代码优先中映射我的实体?请问你有什么想法吗?

【问题讨论】:

  • 电子邮件中的 ID 是否配置为不自动生成?
  • 是的!你觉得这个设计对吗?
  • 您是否还配置了不自动生成 EmailId 的 EF?
  • 使用 SQL 分析器并检查它在插入电子邮件时使用了什么插入命令。

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


【解决方案1】:

当我觉得默认约定不能理解我时,我个人使用 EF 4.1 中的 fluent API 来配置我的所有实体,因此我将使用 fluent 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 没有意义,除非它属于特定的MemberEmail 单独也没有任何意义,除非它属于 Member 或属于 AuthenticationToken。出于这个原因,AuthenticationToken 没有属性来找出它所附加的Member (要找到它,您首先需要一个Member 而不仅仅是查看它的集合)。本质上,一切都围绕Member 对象。如果没有 Member,则无法创建 AuthenticationToken。如果没有MemberAuthenticationToken,则无法创建Email

我不完全确定您对 EF 4.1 中的 fluent API 是否满意,所以如果您有任何问题,请发表评论,我会尽力回答。我还包含了一个小型示例应用程序,用于构建和验证我上面介绍的模型。如果您想运行程序(它是一个小型控制台应用程序),您只需修改 App.config 中的连接字符串以指向您的 SQL Server 实例。

我担心的一件事是Email 可以同时属于MemberAuthenticationToken。我的担忧来自于我必须设置一些有趣的级联删除。不过,我不知道您的所有要求,而且这个设置似乎工作得很好,所以这可能不是问题。

Example Console Application

【讨论】:

  • 刚刚意识到这是一个相当古老的问题。希望它会帮助某人。
猜你喜欢
  • 1970-01-01
  • 2012-03-12
  • 1970-01-01
  • 2011-12-10
  • 2011-12-24
  • 2011-08-08
  • 2014-08-31
  • 2011-10-06
  • 1970-01-01
相关资源
最近更新 更多