1

我遇到了 EF6 的问题,尽管我相当确定这适用于支持此类映射的先前版本。我担心我知道手头问题的答案,但我希望我做错了什么,或者有比我在这里提出的更好的解决方法。为了清楚起见,所有类都被删除了。

所以我有

public abstract class SoftwareFirmware
{
    public long Id { get; private set; }
    public ICollection<DeviceType> DeviceTypes { get; private set; } 

    public SoftwareFirmware()
    {
        DeviceTypes=new HashSet<DeviceType>();
    }
}

public class DeviceType
{
    public long Id { get; set; }
    public virtual ICollection<Firmware> AvailableFirmwareVerions { get; private set; }
    public virtual ICollection<Software> AvailableSoftwareVerions { get; private set; }

    public DeviceType()
    {
        AvailableFirmwareVerions = new HashSet<Firmware>();
        AvailableSoftwareVerions = new HashSet<Software>();
    }
}

如您所见,它定义了多对多关系。我已经定义了两个派生自SoftwareFirmware的类,恰当地命名为

public class Firmware : SoftwareFirmware {}

public class Software : SoftwareFirmware {}

我正在使用 Table Per Hierarchy 继承,因此SoftwareFirmware与鉴别器列存储在同一个表中。最后,我已经映射了 derivedDbContextOnModelCreating方法中的关系

modelBuilder.Entity<DeviceType>().HasMany(d => d.AvailableFirmwareVerions).WithMany(firmware=>firmware.DeviceTypes);
modelBuilder.Entity<DeviceType>().HasMany(d => d.AvailableSoftwareVerions).WithMany(sofware=>sofware.DeviceTypes);

手头的问题是实体框架似乎不支持此映射的继承,因为当 EF 尝试生成数据库时我收到以下信息:

DeviceTypes:FromRole:NavigationProperty 'DeviceTypes' 无效。AssociationType 'DeviceType_AvailableSoftwareVerions' 中 FromRole 'DeviceType_AvailableSoftwareVerions_Target' 的类型 'Software' 必须与声明此 NavigationProperty 的类型 'SoftwareFirmware' 完全匹配。

从这里我收集到一个继承自的类型SoftwareFirmware对于 NavigationProperty 来说不够好,它必须是一个SoftwareFirmware类型。如果我将DeviceType集合从SoftwareFirmware基类中取出并在每个派生类中复制它,那么一切正常,但这肯定不太理想。

最后,我的问题是 - 是否有另一种方法来配置它,以便我可以将导航属性保留在我的基类中?如果没有,是否有比我描述的更清洁的解决方法?


更新:所以似乎 SQL Server Management Studio 做错了,因为我之前绘制了数据库图表,而没有使用表达式的 WithMany 的重载版本,并且它不包括联结表。即使数据库已被删除并重新创建,SSMS 似乎在添加新图表方面也不能很好地处理模式更改 - 它必须重新启动。严重的痛苦,但我离题了......

作为最后的努力,我恢复到WithMany映射的无参数版本,通过重新启动应用程序删除并重新创建数据库,重新启动 SSMS,然后看!连接表已创建。我需要做的就是Ignore为基SoftwareFirmware类的DeviceTypes属性添加一个,并干净地生成所有内容。所以我的 FluentAPI 映射代码如下所示:

modelBuilder.Entity<DeviceType>().HasMany(d => d.AvailableFirmwareVerions).WithMany();
modelBuilder.Entity<DeviceType>().HasMany(d => d.AvailableSoftwareVerions).WithMany();
modelBuilder.Entity<SoftwareFirmware>().Ignore(s => s.DeviceTypes);

它生成了这个模式 - 几乎正是我想要的模式(忽略额外的属性):

在此处输入图像描述

但是,由于无参数调用WithMany仅在一侧连接了导航属性,因此更新Software.DeviceTypes并且Firmware.DeviceTypes不被 EF 跟踪,所以我回到了我开始的地方。

4

2 回答 2

3

这个问题听起来很熟悉。(检查我的电子邮件......是的,那是一年多前的事了!)我有人给我发了一个样本,其中流利的 api 关系失败了。他们没有多对多,但我认为这是同一个问题。我花了很长时间看它并询问 Rowan Miller(团队中的),他说流利的 api 无法理解来自基本类型的属性。

即流畅的API 在查看AvailableSoftwareVerions 或AvailableFirmwareVersions 时看不到DEVICETYPE 属性。(我不能告诉你为什么会这样。你认为它可以通过反射找到它,但也许它只是在设计时没有考虑到这种情况。)

这对我来说仍然没有意义,所以他进一步解释了(我会用你的类型更新他的解释,这有点令人困惑,因为你有额外的继承级别,而且事情的命名有点不一致......但我

从概念上讲,这些类并没有真正的意义,因为一个 DeviceType 可以有许多软件或固件……但是反向导航属性是在 SoftwareFirmware 上定义的。那么当不是固件或软件的东西具有 DeviceType 时会发生什么?它的逆配置为 > DeviceType.AvailableSoftwareVersions 但这不起作用。即使将 EF 排除在图片之外,正确的建模方法是让 Project 属性出现在 Report 上。

那是EF5。如果我的记忆是正确的并且是同样的问题,那么对于 EF6 来说可能没有改变。也许我们应该看看那里是否存在解决这个问题的问题。然而,他的进一步解释表明这不是一个错误,而是一种保护。

(我将 ping 他以验证我是否正确地推断出之前的问题)。

在那封电子邮件中,Rowan 还建议使用 getter 逻辑而不是导航属性作为解决方法。

于 2013-12-10T14:10:02.227 回答
3

问题是您有一个 SoftwareFirmware.DeviceTypes 属性,但您随后尝试将其用作两个独立关系的一部分。SoftwareFirmware.DeviceTypes 不能是 DeviceType.AvailableFirmwareVerions 和 DeviceType.AvailableSoftwareVerions 的倒数。

您尝试建模的内容有点奇怪,因为您将它们视为不同的关系,但也不是。这里有两个选择...

选项1:这是两个独立的关系

删除 SoftwareFirmware.DeviceTypes 并在固件和软件上添加 DeviceTypes 属性。

当您将 Ignore 放在 SoftwareFirmware.DeviceTypes 属性上并使用 WithMany 的空重载时,这实际上就是您正在做的事情——这就是它起作用的原因。您告诉 EF 存在两种关系(一个软件 -> 设备类型,另一个固件 -> 设备类型),并且没有指向另一种方式的导航属性。由于您忽略了 SoftwareFirmware.DeviceTypes 它只是不属于您的模型。

选项2:这是一种关系

删除 DeviceType 上的两个导航属性,并将它们替换为到 SoftwareFirmware 基类的单个导航。您可以随时添加一些过滤内容到软件和固件的外观属性(如下所示)

public class DeviceType
{
    public long Id { get; set; }

    public virtual ICollection<SoftwareFirmware> AvailableVerions { get; private set; }

    public virtual IEnumerable<Firmware> AvailableFirmwareVerions
    {
        get
        {
            return this.AvailableVerions.OfType<Firmware>();
        }
    }

    public virtual IEnumerable<Software> AvailableSoftwareVerions
    {
        get
        {
            return this.AvailableVerions.OfType<Software>();
        }
    }

    public DeviceType()
    {
        AvailableVerions = new HashSet<SoftwareFirmware>();
    }
}
于 2013-12-10T22:33:15.860 回答