【问题标题】:Downcasting with Entity Framework使用实体框架向下转型
【发布时间】:2011-11-08 04:08:53
【问题描述】:

我有一个项目,我在 EF 中将Employer 定义为User 的派生类。在我的过程中,我创建了一个用户,但不知道它最终是否会成为雇主(或其他类型的用户),然后我需要转换它。一开始我试过(智能感知表明存在显式转换):

Employer e = (Employer) GetUser();

但在运行时我得到了:

Unable to cast object of type 'System.Data.Entity.DynamicProxies.User_7B...0D' to type 'Employer'.

所以我尝试写一个转换器:

public partial class User
{
    public static explicit operator Employer(User u)
    {

但我得到了错误:

Error   21  'User.explicit operator Employer(User)': user-defined
conversions to or from a derived class are not allowed
C:\Users\..\Documents\Visual Studio 2010\Projects\..\Website\Models\EF.Custom.cs

很好。然后我重载了Employer 的构造函数,如下所示:

public partial class Employer
{
    public Employer(User u)
    {
        this.Id = u.Id;
        this.Claims = u.Claims;
        // etc.
    }
}

然后我想我可以这样做:

Employer e = new Employer(GetUser());

但是当我运行它时,我得到了错误:

System.InvalidOperationException was unhandled by user code
  Message=Conflicting changes to the role 'User' of the
  relationship 'EF.ClaimUser' have been detected.
  Source=System.Data.Entity
  StackTrace:
       [...]
       at Controllers.AuthController.Register(String Company, String GivenName, 
       String Surname, String Title, String Department) in C:\Users\..\Documents\
       Visual Studio 2010\Projects\..\Website\Controllers\AuthController.cs:line

作为最后的手段,我尝试写这个:

        Employer e = Auth.Claims("id")
            .Where(x => x.Value == Auth.NameIdentifier())
            .Select(x => x.User)
            .Cast<Employer>()
            .Single();

... GetUser() 返回一个 User 类型的对象,它不提供 .Cast&lt;&gt; 所以我使用直接查询到达那里...但我仍然得到动态代理对象异常的强制转换。

所以我的问题是:当对象通过 EF 具有持久性时,我该如何向下转型?

【问题讨论】:

  • Employer 是您的 EF 模型的一部分吗?如果您已将继承正确映射为 TPHTPC,则 EF 将实例化正确的子类代理
  • @Eranga,是的 Employer 是 EF 模型的一部分。我正在使用 TPT,所以我有一张名为 Users 的表和一张名为 Users_Employer

标签: c# entity-framework asp.net-mvc-3 casting downcast


【解决方案1】:

这是不可能的。您必须始终使用最终类型。一旦您将其创建为User,EF 将永远不允许您将其更改为派生实体类型。

顺便说一句。面向对象的方法也是不可能的。您不能将父类的实例转换为派生类的实例(除非它确实是派生类的实例) - 它会在运行时抛出异常。重现问题的非常简单的示例:

class X { } 

class Y : X { }

class Program 
{
    static void Main(string[] args) 
    {
        X x1 = new Y();
        Y y1 = (Y)x1;   // Works

        X x2 = new X();
        Y y2 = (Y)x2;   // InvalidCastException
    }
}

这样做的唯一方法是覆盖转换运算符,它将在内部创建派生类的新实例并将所有字段从旧父实例复制到新的派生实例。

实体框架需要完全相同的方法。如果您从 User 实体开始,现在要将其提升为 Employer 实体,则必须删除旧用户并创建新的 Employer

【讨论】:

  • 但我确实(尝试)覆盖了转换运算符(请参阅上面的错误);失败了,我尝试使用您提到的副本创建一个新对象,但也遇到了问题(如上所述)。您是否建议我需要执行以下操作:User u = db.Users.Find(...); Employer e = new Employer(u); db.Users.Remove(u); db.Employers.Add(e); db.SaveChanges()
  • 谢谢拉迪斯拉夫。如果您考虑需要对可能具有各种外部引用的对象进行深层复制,这似乎是一种过于惩罚性的方法……必须从数据库中获取所有内容,删除所有内容并重新创建它,而实际上,我所需要的只是在我的 Users_Employer 表中输入几个条目,但您的解决方案有效。当然,现在我遇到了不同的问题......请参阅:stackoverflow.com/questions/7276507/…
  • 经过一番努力,我得到了深层复制工作;可悲的是,只是再次发现自己在同一个地方我无法添加对象,因为 EF 认为我在它的鼻子底下摆弄了一些东西
  • 我发现了这个:stackoverflow.com/questions/5381690/…,它适用于 C++,但我将在 C# 中进行测试......我很难相信这个非常常见的用例很难实现!
【解决方案2】:

假设您的 Employer 实体只有可为空的属性,则可以转到数据库中的表并将鉴别器从 User 更改为 Employer。所有的关系都会被保留。反之亦然。

【讨论】:

    猜你喜欢
    • 2011-05-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多