实体框架中的多对多关系+ TPH继承问题6 [英] Issue with many-to-many relationship + TPH inhertitance in Entity Framework 6

查看:324
本文介绍了实体框架中的多对多关系+ TPH继承问题6的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在遇到与EF6有关的问题,但我确信这适用于以前支持这种映射类型的版本。我恐怕我知道手头的问题的答案,但我希望我做错了事情,或者比我在这里提出的更好的解决方法。为了清楚起见,所有类都被排除。



所以我有

  public abstract class SoftwareFirmware 
{
public long Id {get;私人集合}
public ICollection< DeviceType> DeviceTypes {get;私人集合}

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

  public class DeviceType 
{
public long Id {get;组; }
public virtual ICollection< Firmware> AvailableFirmwareVerions {get;私人集合}
public virtual ICollection< Software> AvailableSoftwareVerions {get;私人集合}

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

你可以看到有多对多的关系定义。我已经定义了两个派生自 SoftwareFirmware 的类,它们被正确命名为

  public class固件:SoftwareFirmware {} 

  public class软件:SoftwareFirmware {} 

m使用Table Per Hierarchy继承,所以软件固件存储在同一个表中,并带有一个标识符列。最后,我已经将派生的 DbContext OnModelCreating 方法与

  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'无效。
键入FromRole的$软件
'DeviceType_AvailableSoftwareVerions_Target'在AssociationType
'DeviceType_AvailableSoftwareVerions'必须与宣告此NavigationProperty的类型
'SoftwareFirmware'完全匹配。


从这里我收集一个继承自 SoftwareFirmware 的类型不够好NavigationProperty,它必须是一个 SoftwareFirmware 类型。如果我从 SoftwareFirmware 基类中的 DeviceType 集合中删除,并在每个派生类中复制它,则工作,但这肯定不太理想。



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






更新:所以看起来SQL Server Management Studio对我来说是错误的,因为我先前已经绘制出数据库,而没有重载版本的WithMany,它接受一个表达式,而且没有包含连接表。似乎SSMS似乎不会很好地改变模式,即增加新的图表,即使数据库已被删除并重新创建,它必须重新启动。主要疼痛,但我离题...



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

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

它生成这个模式,正是我想要的模式(忽略额外的属性): p>



但是,由于无参数调用 WithMany 只会在一侧挂接导航属性,更新到 Software.DeviceTypes Firmware.DeviceTypes 没有被EF跟踪,所以我回到了我开始的地方。

解决方案

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



你想要建模的是有点奇怪,因为你是亲切的将他们视为不同的关系,而不是。这里有两个选项...



选项1:两种不同的关系



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



这实际上是当您在SoftwareFirmware.DeviceTypes属性上放置Ignore并使用WithMany的空重载时,您正在做什么 - 这就是为什么它可以工作。你告诉EF有两种关系(一个软件 - > DeviceType和另一个固件 - > DeviceType),没有导航属性指向另一种方式。由于您忽略了SoftwareFirmware.DeviceTypes,它只是不属于您的模型。



选项2:这是一种关系



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

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

public virtual ICollection< SoftwareFirmware> AvailableVerions {get;私人集合}

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>();
}
}


I am running into an issue with EF6, though I'm fairly sure that this applies to previous versions that support this type of mapping. I fear I know the answer to the question at hand, but I hope that I am doing something wrong, or there is a better workaround than what I present here. All classes are gutted for clarity.

So I have

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

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

and

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>();
    }
}

which as you can see have a many to many relationship defined. I've defined two classes which derive from SoftwareFirmware, the aptly named

public class Firmware : SoftwareFirmware {}

and

public class Software : SoftwareFirmware {}

I'm using Table Per Hierarchy inheritance, so Software and Firmware are stored in the same table with a discriminator column. Finally, I've mapped the relationships in the derived DbContext's OnModelCreating method with

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

The problem at hand is that Entity Framework does not seem to support inheritance with this mapping, as I receive the following when EF tries to generate the database:

DeviceTypes: FromRole: NavigationProperty 'DeviceTypes' is not valid. Type 'Software' of FromRole 'DeviceType_AvailableSoftwareVerions_Target' in AssociationType 'DeviceType_AvailableSoftwareVerions' must exactly match with the type 'SoftwareFirmware' on which this NavigationProperty is declared on.

From this I gather that a type that inherits from SoftwareFirmware is not good enough for the NavigationProperty, it must be a SoftwareFirmware type. If I tear the DeviceType collection out of the SoftwareFirmware base class and duplicate it in each of the derived classes, things work, but that's certainly less than ideal.

So finally, my question is- is there another way to configure this so that I can keep my navigation property in my base class? If not, is there a cleaner workaround than what I've described?


UPDATE: so it would seem that SQL Server Management Studio did me wrong, as I had diagrammed out the database previously without the overloaded version of WithMany that takes an expression and it did not include the junction tables. It seems like SSMS doesn't play nice with schema changes in terms adding new diagramming even when the database has been dropped and recreated- it must be restarted. Major pain, but I digress...

As a last ditch effort, I reverted to the parameterless version of WithMany for the mappings, deleted and recreated the database by restarting the application, restarted SSMS, and lo! The junction tables were created. All I needed to do is add an Ignore for the base SoftwareFirmware class's DeviceTypes property and everything generated cleanly. So my FluentAPI mapping code looks like this:

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

which generates this schema- pretty much exactly the schema I wanted (ignore the extra properties):

However, since the parameterless call to WithMany only hooks up a navigation property on one side, updates to Software.DeviceTypes and Firmware.DeviceTypes aren't tracked by EF so I'm back where I started.

解决方案

The issue is that you have a single SoftwareFirmware.DeviceTypes property but you are then trying to use it as part of two separate relationships. SoftwareFirmware.DeviceTypes can't be the inverse of both DeviceType.AvailableFirmwareVerions and DeviceType.AvailableSoftwareVerions.

What you're trying to model is a bit strange because you're kind of treating them as distinct relationships, but also not. There are two options here...

Option 1: It's two separate relationships

Remove SoftwareFirmware.DeviceTypes and add a DeviceTypes property on Firmware and Software.

This is actually what you are doing when you put Ignore on the SoftwareFirmware.DeviceTypes property and use the empty overload of WithMany - which is why it works. You're telling EF that there are two relationships (one Software -> DeviceType and the other Firmware -> DeviceType) and that there is no navigation property that points back the other way. Since you ignored SoftwareFirmware.DeviceTypes it's just not part of your model.

Option 2: It's one relationship

Remove the two navigation properties on DeviceType and replace them with a single navigation to the SoftwareFirmware base class. You can always add some façade properties that filter the contents to Software and Firmware (as shown below)

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>();
    }
}

这篇关于实体框架中的多对多关系+ TPH继承问题6的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆