【问题标题】:Map a navigation property to a instance var as Foreign key将导航属性作为外键映射到实例 var
【发布时间】:2013-12-12 23:33:07
【问题描述】:

我正在使用 .Net Framework 4.0 开发 Entity Framework Code First (v. 4.4.0.0) C# 库。

我不知道如何设置零对一的关系。我的模型如下:

Talk 只能由一个用户 (StarterUserId) 创建。
Talk 只能有一个收件人用户 (RecepientUserId) 或只有一个组 (RecipientGroupId)。
注意:这意味着如果RecipientGroupId不为空,则RecepientUserId为空;如果RecipientGroupId为空,则RecepientUserId不为空。

user 可以是零或 n Talks 的收件人,但 group 可以是零或一个 Talk

这是谈话课:

[DataContract]
public class Talk
{
    [DataMember]
    public int TalkId { get; set; }

    [DataMember]
    public int StarterUserId { get; set; }

    [DataMember]
    public int? RecipientUserId { get; set; }

    [DataMember]
    [ForeignKey("RecipientGroup")]
    public int? RecipientGroupId { get; set; }

    public DateTime DateUtcStarted { get; set; }

    [DataMember]
    public string DateStarted
    {
        get
        {
            return DateUtcStarted.ToString("dd/MM/yyyy HH:mm");
        }
        set
        {
            DateUtcStarted = DateTime.Parse(value);
        }
    }

    public User StarterUser { get; set; }
    public User RecipientUser { get; set; }

    public Group RecipientGroup { get; set; }
}

使用这个 TalkConfiguration 类:

class TalkConfiguration : EntityTypeConfiguration<Talk>
{
    public TalkConfiguration()
    {
        Property(t => t.StarterUserId).IsRequired();
        Property(t => t.RecipientUserId).IsOptional();
        Property(t => t.RecipientGroupId).IsOptional();
        Property(t => t.DateUtcStarted).IsRequired();

        Ignore(t => t.DateStarted);

        HasRequired(t => t.StarterUser).
            WithMany(u => u.TalksStarted).
            HasForeignKey(t => t.StarterUserId);
        HasOptional(t => t.RecipientUser).
            WithMany(u => u.InTalks).
            HasForeignKey(t => t.RecipientUserId);

        HasOptional(t => t.RecipientGroup).WithOptionalDependent(g => g.GroupTalk);
    }
}

这是Group 类:

[DataContract]
public class Group
{
    [DataMember]
    public int GroupId { get; set; }

    [ ... ]

    public Talk GroupTalk { get; set; }
}

还有GroupConfiguration 类:

class GroupConfiguration : EntityTypeConfiguration<Group>
{
    public GroupConfiguration()
    {
        [ ... ] // Nothing related to GroupTalk
    }
}

通过这些类和配置,我在数据库中得到了这个 Talk 表:

我想让Talk.RecipientGroupId 作为Group.GroupId外键。但是这个模型创建了另一个列,Talk.RecipientGroup_GroupId 作为 FOREIGN KEYGroup.GroupId。而且,我不希望那样。

我该怎么做?

【问题讨论】:

    标签: c# sql entity-framework database-design entity-relationship


    【解决方案1】:

    可选:可选的一对一关系映射为独立关联,而不是外键关联,这意味着您的模型类中不能有外键属性。这就是为什么你不能在WithOptionalDependent 之后链接HasForeignKey。而且我很确定RecipientGroupId 上的[ForeignKey] 属性被简单地忽略了,EF 将RecipientGroupId 视为没有关系目的的普通标量属性。

    在数据库模式本身中,关系有一个外键。这就是您看到的具有自动生成的默认名称的名称:RecipientGroup_GroupId。但不支持将此外键映射到属性。但是,我认为您可以使用 MapKey 重命名该列

    HasOptional(t => t.RecipientGroup)
        .WithOptionalDependent(g => g.GroupTalk)
        .Map(m => m.MapKey("RecipientGroupId"));
    

    如果这样做,您必须Talk 类中删除 RecipientGroupId 属性,否则 EF 将抱怨两个具有相同名称的列冲突。

    我相信, optional:optional 是唯一的一对一关系是独立关联,所有其他都是外键关联,其中外键属性同时是主键属性(根据 Arthur Vickers 的回答在this thread 的底部)。对于 optional:optional 关系,这是不可能的,因为主键属性不能为空。

    由于您的RecipientGroupId 具有[DataMember] 属性,因此看起来您希望通过某些服务边界传输值,因此出于某种原因需要外键作为属性值。在这种情况下,我会选择的解决方法是将Talk&lt;-&gt;Group 关系映射为一对多关系,Group 类中根本没有导航属性(然后使用无参数的WithMany() 调用映射它)或使用一个集合导航属性,然后在业务逻辑中确保此集合不能包含多个元素。

    【讨论】:

    • 感谢您的回答。我不确定我在做什么。我想表示一个谈话可以有零个或一个组;也许,可选的:可选的,并不代表那个。
    • @VansFannel:如果您还想表示Group 只能参加一次谈话或根本不参加谈话,您的意图是将其映射为可选:可选很好。但是,在这种情况下,EF 不支持将外键作为属性公开。如果Group 可以参与多个谈话,则将关系映射为一对多将更合适(无论如何在技术上更容易映射)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-19
    • 2016-07-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多