无法保存实体框架继承类型

发布于 2024-07-07 15:05:49 字数 946 浏览 10 评论 0原文

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

在似乎不是 .AddToEmployer 项的上下文中(仅限 AddObjectAddToBaseEntity)。

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

找不到 EntitySet 名称“DataEntities.Employer”。

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

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

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

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

I have implemented some table-per-type inheritance in my data model (basically have a BaseEntity type with all the base information for my items and a Employer type that inherits from the BaseEntity item). Everything appears to be set up correctly and when using the Entities (either via ADO.net Data Services or via Linq to Entities) I can see the Employer type and things appear to be fine. The issue starts when I create a new Employer entity and attempt to save it.

On the context that doesn't appear to be an .AddToEmployer item (only and AddObject or AddToBaseEntity).

If I use AddObject("Employer", NewEmployer) I get and error message of:

The EntitySet name 'DataEntities.Employer' could not be found.

If I use AddToBaseEntity(NewEmployer) I get an error message of:

Unable to determine a valid ordering for dependent operations. Dependencies may exist due to foreign key constraints, model requirements orstore generated values.

Have I missed a step in setting up the inheritance? Is there some specific way to save objects that are inherited? What am I doing wrong? I assume that the basic issue is that I should have an AddToEmployer, what do I need to do to get that exposed? It seems odd that it is not an option since I can see the Employer type on the client side and can do things such as:

var NewEmployer = new Employer() - which seems to suggest that I can see the Employer type fine.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

零度℉ 2024-07-14 15:05:50

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

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

My Name is Phani and I work on the ADO.NET Data Services team.

The ResolveName and ResolveType methods are to help you customize the type information that the client writes in the payload sent to the server and how the response payload from the server is materialized .

They help you resolve types on the client and are useful in many scenarios , a couple of examples are :

  1. The type hierarchy of entities are on the client different compared to the server.
  2. Entity Types exposed by the service participate in inheritance and you want to work with derived types on the client.

ResolveName is used to change the name of the entity that we put on the wire when making a request to the server.

Consider this data model :
On Server

public class Employee {
    public int EmployeeID {get;set;}
    public string EmployeeName {get;set;}
}

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

When you use the client to work with instances of the Manager Entity Type ,
upon submitting the changes to the server, we expect type information to be present in the payload when entities participate in inheritance.

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

However, when the client serializes this payload, it puts in "Employee" as the type name
which is not what is expected on the server.
Hence you have to provide a name resolver on the client,

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

a Type resolver is used in the same way .

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);
}
拍不死你 2024-07-14 15:05:50

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

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

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

Well you only get an entity set pr. base class so .AddToBaseEntity is the solution as such.

But it sounds like you have a circular dependency in your model, so that the Entity framework cannot figure out in which order to save.

Check that you have foreign keys to the baseentity on your derived entities and update your model.

橘亓 2024-07-14 15:05:50

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

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

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

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

I changed a couple of things and was able to get this to work. I am not particularly sure what was the base issue, but wanted to post what I did do for reference.

Rebuilt Tables: I rebuilt the tables starting with just the ID/Key columns and a single data column.

Removed extra auto incrementing fields: I had an auto-incrementing ID on the BaseEntity and on the Employer. I removed the auto-incrementing ID on the Employer and just had the Employer.BaseEntityID column and the foreign key back to BaseEntity.BaseEntityID. (this seems to have been the culprit, but I was under the impression this was permitted)

Unfortunately this then lead to the issue that enherited classes in the entity framework cannot have navigation properties (all navigation properties must be on the base entity) so inheritance is going to prove to be non-usable for our needs.

呆萌少年 2024-07-14 15:05:50

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

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

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

You don't have Employer defined as entity set, just like entity type. That is way you are missing AddToEntity in the context object. There is always one entity set for one class hierarchy, in this case it is BaseClass entity set.

If you want to get entity set 'Employer' you can try to manually edit edmx file and to add new entity set 'Employer', and then set entity type 'Employer' to belong to that entity set. It shouldn't be hard, I've done it a lot of times.

I'm not sure if there is some more regular solution.

所有深爱都是秘密 2024-07-14 15:05:50

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

不确定早期版本,但实体框架 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 应该有一个调用 _context.SaveChanges() 的公共 Save() 方法 - 或者您应该有一些其他方法将对象更改推送到数据存储。

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

Coming over two years later, but in the interest of keeping it relevant for the search traffic, here's a way I worked around this quickly in a convenience class that we were using to quickly populate our database with staging data.

Not sure about earlier versions, but Entity Framework 4 lets you dump the object into the context as a base object, and then the framework figures out the server-side references. Thus, you wouldn't used AddToInheritedObjects() (which is deprecated anyway) but rather the ObjectSet<>.Add() method.

Here's a helper class example:

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;
        }
        …
}

Thus, assuming you have the following:

class ObjectOne : BaseObject {}
class ObjectTwo {}

You could easily add entities to the context:

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

Remembering, of course, that ContextHelper should have a public Save() method which calls _context.SaveChanges()- or that you should have some other way of pushing the object changes up to the datastore.

This might not be a direct response to any given question about inheritance, but hopefully gives people a starting point to answer specifics.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文