实体框架与视图上的多个(单独)键关联

Cly*_*yde 15 .net c# ado.net entity-framework-4 ef-code-first

我在设置Entity Framework 4模型时遇到问题.

Contact对象在数据库中公开为可更新视图.此外,由于数据库的历史记录,此联系人视图有两个不同的密钥,一个来自遗留系统.所以其他一些表引用了一个带有'ContactID'的联系人,而其他旧表引用了一个'LegacyContactID'.

由于这是一个视图,因此数据库中没有外键,我正在尝试在设计器中手动添加关联.但是,流畅的关联似乎没有提供指定引用哪个字段的方法.

我该如何建立这个模型?

public class vwContact
{
  public int KeyField { get; set; }
  public string LegacyKeyField { get; set; }
}

public class SomeObject
{
  public virtual vwContact Contact { get; set; }
  public int ContactId { get; set; } //references vwContact.KeyField
}

public class LegacyObject
{
  public virtual vwContact Contact { get; set; }
  public string ContactId { get; set; } //references vwContact.LegacyKeyField
}

ModelCreatingFunction(modelBuilder)
{
  // can't set both of these, right?
  modelBuilder.Entity<vwContact>().HasKey(x => x.KeyField);
  modelBuilder.Entity<vwContact>().HasKey(x => x.LegacyKeyField);

  modelBuilder.Entity<LegacyObject>().HasRequired(x => x.Contact).??? 
  //is there some way to say which key field this reference is referencing?
}
Run Code Online (Sandbox Code Playgroud)

one*_*mer 4

编辑 2:“新事物已经曝光,伙计” ——他的 Dudeness

经过更多的实验和新闻,我发现使用基类和具有不同键的子类本身无法工作。特别是在代码优先的情况下,如果基本实体没有显式映射到表,则必须定义一个键。

我留下了下面建议的代码,因为我仍然建议使用基类来实现 C# 的可管理性,但我在代码下面更新了我的答案并提供了其他解决方法选项。

不幸的是,事实揭示的是,由于 EF 4.1+ 代码优先的限制,如果不更改 SQL,您就无法实现您所寻求的目标。


基本接触类

public abstract class BaseContact
{
   // Include all properties here except for the keys
   // public string Name { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

实体类

如果您愿意,可以通过 Fluent API 进行设置,但为了便于说明,我使用了数据注释

public class Contact : BaseContact
{
   [Key]
   public int KeyField { get; set; }
   public string LegacyKeyField { get; set; }
}

public class LegacyContact : BaseContact
{
   public int KeyField { get; set; }
   [Key]
   public string LegacyKeyField { get; set; }    
}
Run Code Online (Sandbox Code Playgroud)

使用实体

  1. 引用或操作联系人对象的类应该像接口一样引用基类:

    public class SomeCustomObject
    {
       public BaseContact Contact { get; set; }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 如果稍后您需要以编程方式确定您正在使用的类型typeof()并相应地操作实体。

    var co = new SomeCustomObject(); // assume its loaded with data
    if(co.Contact == typeof(LegacyContact)
        // manipulate accordingly.
    
    Run Code Online (Sandbox Code Playgroud)

新选项和解决方法

  1. 正如我之前在评论中建议的那样,无论如何您都无法将它们映射到单个视图/表,因此您有几个选择:

    A。将您的对象映射到其基础表,并更改从连接视图中提取的存储库和服务类上的“获取/读取”方法 - 或 -

    b. 创建第二个视图并将每个对象映射到其适当的视图。

    C。将一个实体映射到其基础表,并将一个实体映射到视图。

概括

尝试(B),创建一个单独的视图,因为它需要对代码和数据库架构进行最少的更改(您不会摆弄基础表,也不会影响存储过程)。它还确保您的 EF C# POCO 具有同等功能(一个用于视图,一个用于表可能会导致异常)。米格尔下面的答案似乎与建议大致相同,所以如果可能的话我会从这里开始。

选项(C)似乎最糟糕,因为当映射到不同的 SQL 片段(表与视图)时,您的 POCO 实体的行为可能会出现不可预见的怪癖,从而导致编码问题。

选项(A),虽然它最符合 EF 的意图(实体映射到表),但这意味着要获得联接视图,您必须更改 C# 服务/存储库以与 EF 实体一起进行添加、更新、删除操作,但告诉类似拉/读的方法从联合视图中获取数据。这可能是您的最佳选择,但比(B)涉及更多工作,并且从长远来看也可能影响架构。复杂性越高,风险就越大。