4

我已经在我的数据模型中实现了一些 table-per-type 继承(基本上有一个BaseEntity包含我的项目的所有基本信息的Employer类型和一个从BaseEntity项目继承的类型)。一切似乎都设置正确,并且在使用实体时(通过 ADO.net 数据服务或通过 Linq to Entities)我可以看到Employer类型并且事情似乎很好。当我创建一个新Employer实体并尝试保存它时,问题就开始了。

在似乎不是.AddToEmployer项目的上下文中(只有 and AddObjector AddToBaseEntity)。

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

找不到实体集名称“DataEntities.Employer”。

如果我使用AddToBaseEntity(NewEmployer),我会收到以下错误消息:

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

我是否错过了设置继承的步骤?是否有一些特定的方法来保存继承的对象?我究竟做错了什么?我认为基本问题是我应该有一个AddToEmployer,我需要做什么才能暴露它?这似乎很奇怪,因为我可以在客户端看到 Employer 类型并且可以执行以下操作:

var NewEmployer = new Employer()- 这似乎表明我可以很好地看到 Employer 类型。

4

5 回答 5

7

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

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

它们可以帮助您解析客户端上的类型,并且在许多场景中都很有用,例如:

  1. 与服务器相比,实体的类型层次结构在客户端上是不同的。
  2. 服务公开的实体类型参与继承,您希望在客户端上使用派生类型。

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 Entity Type 的实例时,在将更改提交到服务器时,我们希望当实体参与继承时类型信息会出现在有效负载中。

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);
}
于 2008-12-15T19:45:39.717 回答
2

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

但是听起来您的模型中有循环依赖,因此实体框架无法确定以哪个顺序保存。

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

于 2008-10-17T09:44:29.517 回答
2

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

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

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

不幸的是,这会导致实体框架中的继承类不能具有导航属性(所有导航属性都必须在基础实体上)的问题,因此继承将被证明无法满足我们的需求。

于 2008-10-22T04:19:52.213 回答
1

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

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

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

于 2008-10-17T07:43:49.183 回答
1

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

不确定早期版本,但 Entity Framework 4 允许您将对象作为基础对象转储到上下文中,然后框架计算出服务器端引用。因此,您不会使用 AddToInheritedObjects() (无论如何都已弃用),而是使用 ObjectSet<>.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 应该有一个公共的 Save() 方法,该方法调用 _context.SaveChanges()- 或者您应该有一些其他方式将对象更改推送到数据存储区。

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

于 2010-12-23T16:41:24.150 回答