无法保存Entity Framework Inherited类型

时间:2008-10-17 07:19:33

标签: .net entity-framework inheritance savechanges table-per-type

我在我的数据模型中实现了一些每类型的表继承(基本上有一个BaseEntity类型,包含我的项目的所有基本信息和一个继承自{{1}的Employer类型项目)。一切似乎都是正确设置的,当使用实体时(通过ADO.net数据服务或通过Linq到实体),我可以看到BaseEntity类型,事情看起来没问题。当我创建一个新的Employer实体并尝试保存它时,问题就开始了。

在看似不是Employer项目的上下文中(仅限.AddToEmployerAddObject)。

如果我使用AddToBaseEntity,我会收到错误消息:

  

找不到EntitySet名称'DataEntities.Employer'。

如果我使用AddObject("Employer", NewEmployer),我会收到错误消息:

  

无法确定相关操作的有效排序。由于外键约束,模型要求orstore生成的值可能存在依赖关系。

我是否错过了设置继承的步骤?是否有一些特定的方法来保存继承的对象?我究竟做错了什么?我认为基本问题是我应该有一个AddToBaseEntity(NewEmployer),我需要做些什么来揭露它?看起来很奇怪它不是一个选项,因为我可以在客户端看到雇主类型并且可以执行以下操作:

AddToEmployer - 这似乎表明我可以看到雇主类型很好。

5 个答案:

答案 0 :(得分:7)

我的名字是Phani,我在ADO.NET数据服务团队工作。

ResolveNameResolveType方法可帮助您自定义客户端在发送到服务器的有效负载中写入的类型信息,以及如何实现服务器的响应有效负载。

它们可以帮助您解决客户端上的类型,并且在许多情况下都很有用,例如:

  1. 与服务器相比,实体的类型层次结构与客户端不同。
  2. 服务公开的实体类型参与继承,您希望在客户端上使用派生类型。
  3. ResolveName用于在向服务器发出请求时更改我们在线路上放置的实体的名称。

    考虑这个数据模型: 在服务器上

    public class Employee {
        public int EmployeeID {get;set;}
        public string EmployeeName {get;set;}
    }
    
    public class Manager:Employee {
        public List<int> employeesWhoReportToMe {get;set;}
    }
    

    当您使用客户端处理Manager实体类型的实例时, 在将更改提交到服务器时,我们希望当实体参与继承时,类型信息将出现在有效负载中。

    context.AddObject("Employees",ManagerInstance ); <-- add manager instance to the employees set.
    context.SaveChanges();
    

    但是,当客户端序列化此有效负载时,它会将“Employee”作为类型名称 这不是服务器上的预期。 因此,您必须在客户端上提供名称解析器,

    context.ResolveName = delegate(Type entityType){
        //do what you have to do to resolve the type to the right type of the entity on the server
        return entityType.FullName;
    }
    

    类型解析器以相同的方式使用。

    context.ResolveType = delegate(string entitySetName){
        //do what you have to do to convert the entitysetName to a type that the client understands
        return Type.GetType(entitySetName);
    }
    

答案 1 :(得分:2)

嗯,你只获得一个实体集pr。基类所以.AddToBaseEntity就是这样的解决方案。

但听起来你的模型中存在循环依赖,因此实体框架无法确定要保存的顺序。

检查派生实体的基本实体是否具有外键并更新模型。

答案 2 :(得分:2)

我改变了一些事情并且能够让它发挥作用。我不是特别确定什么是基本问题,但想发布我做过的事情以供参考。

重建表:我从ID / Key列和单个数据列开始重建表。

删除了额外的自动递增字段:我在BaseEntity和雇主上有一个自动递增ID。我删除了雇主上的自动递增ID,只有Employer.BaseEntityID列和外键返回BaseEntity.BaseEntityID。 (这似乎是罪魁祸首,但我认为这是允许的)

不幸的是,这导致了一个问题,即实体框架中的enherited类不能具有导航属性(所有导航属性必须在基础实体上),因此继承将证明对我们的需求不可用。

答案 3 :(得分:1)

您没有将Employer定义为实体集,就像实体类型一样。这就是你在上下文对象中缺少AddToEntity的方式。对于一个类层次结构总是有一个实体集,在这种情况下它是BaseClass实体集。

如果要获取实体集'Employer',您可以尝试手动编辑edmx文件并添加新的实体集'Employer',然后将实体类型'Employer'设置为属于该实体集。这应该不难,我已经做了很多次了。

我不确定是否有更常规的解决方案。

答案 4 :(得分:1)

两年后,为了保持与搜索流量的相关性,这里有一种方法我在一个便利类中快速解决这个问题,我们用它来快速填充数据库中的暂存数据。

不确定早期版本,但Entity Framework 4允许您将对象作为基础对象转储到上下文中,然后框架会计算出服务器端引用。因此,您不会使用AddToInheritedObjects()(无论如何已弃用),而是使用ObjectSet&lt;&gt; .Add()方法。

这是一个帮助类示例:

public ContextHelper
{
        …
        _context = ModelEntities();

        public T Create<T>() where T : class
        {
            // Create a new context
            _context = new ModelEntities();

            // Create the object using the context (can create outside of context too, FYI)
            T obj = _context.CreateObject<T>();

            // Somewhat kludgy workaround for determining if the object is
            // inherited from the base object or not, and adding it to the context's
            // object list appropriately.    
            if (obj is BaseObject)
            {
                _context.AddObject("BaseObjects", obj);
            }
            else
            {
                ObjectSet<T> set = _context.CreateObjectSet<T>();
                set.AddObject(obj);
            }

            return obj;
        }
        …
}

因此,假设您有以下内容:

class ObjectOne : BaseObject {}
class ObjectTwo {}

您可以轻松地将实体添加到上下文中:

ContextHelper ch = ContextHelper()
ObjectOne inherited = ch.Create<ObjectOne>();
ObjectTwo toplevel = ch.Create<ObjectTwo>();
…

当然,记住ContextHelper应该有一个调用_context.SaveChanges()的公共Save()方法 - 或者你应该有其他方法将对象更改推送到数据存储区。

这可能不是对任何有关继承问题的直接回应,但希望能给人们一个回答具体细节的起点。