EF 4.0 IsAttachedTo扩展方法和错误具有相同密钥的对象已经存在 [英] EF 4.0 IsAttachedTo extension method and error An object with the same key already exists

查看:139
本文介绍了EF 4.0 IsAttachedTo扩展方法和错误具有相同密钥的对象已经存在的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我收到一个错误


ObjectStateManager中已存在具有相同密钥的对象。
ObjectStateManager无法跟踪具有相同
键的多个对象。


在我googled之后,我发现 IsAttachedTo 扩展方法:



Is可以检查对象是否已经附加到实体中的数据上下文框架?



这是我的代码:

  foreach(类型中的字符串)
{
订阅订阅=新订阅{Id = Int32.Parse(s)};

if(service.repository._context.IsAttachedTo(subscription))
service.repository._context.Detach(subscription);

service.repository._context.AttachTo(订阅,订阅); // error here
horse.Subscriptions.Add(subscription);
}

但是当在foreach循环中出现具有相同键的订阅时,扩展方法 IsAttachedTo 每次都返回false,它不会检测到已经有这样的实体附加了。结果我得到相同的错误:


ObjectStateManager中已经存在具有相同密钥的对象。
ObjectStateManager无法跟踪具有相同
键的多个对象。


为什么会这样?

解决方案

我有一点代码对您的评论是因为您的示例代码吓倒我。



您可能已经阅读了很多关于花式设计模式和分层架构,并且您开始自己使用它们。不幸的是,你错过了要点。这是什么?

  service.repository._context.XXX 

如果不封装其逻辑,您为什么要打扰任何服务层或存储库层?为什么在服务上暴露存储库?没有人应该知道服务内部的实现?更糟糕的是,为什么在存储库上公开上下文?这损坏了存储库的全部内容!



编写高质量面向对象的代码有很多支持规则。其中一个规则称为 Demeter法案。你不必遵循每个规则,你也不必一直遵循规则,但是在分层架构的情况下,这个法律是必须的。



如果您有三层A - > B - > C,则层A可以调用B层上的方法,但不知道C并且无法达到方法。如果可以的话,它不是一个新层,而是与B相同,层A不需要通过B调用它,它可以直接调用它。



在你的例子中,你刚刚将D暴露给A,因为A是当前层,B是服务,C是存储库和D是上下文



还有一点关于你的代码有着名的命名约定。这些约定不是关于我喜欢这个,而是你喜欢的,而是关于你正在使用的框架严格遵循这些约定的事实,所以使用另外一个来混合你的命名约定和框架命名约定使你的代码看起来很乱。



我很抱歉,如果这只是一些使代码结构清晰的示例代码。我只需要描述这段代码的错误。






您从相关问题引用的方法将不适用于您的情况。我认为只有从数据库加载订阅,它才有效。原因是引用的方法使用 EntityKey (内部或直接)从上下文获取实体,但是您的新实体还没有实体密钥。我希望为您的实体调用 TryGetObjectStateEntry 将始终返回 Detached 。实体键在附加时创建,或者您必须手动构建。



如果您想要一些 IsAttachedTo 方法尝试这个:

  public bool IsAttachedTo< T>(此ObjectContext上下文,T实体)其中T:IEntity 
{
return context.GetObjectStateEntries(〜EntityState.Detached)
.Where(e =>!e.IsRelationship)
.Select(e => e.Entity)
.OfType< ; T>()
.Any(e => e.Id == entity.Id);
}

并确保您的实体实现帮助界面

  public interface IEntity 
{
int Id {get; }
}

但是要能够分离附件,您需要:

  public T GetAttached&T;(此ObjectContext上下文,T实体)其中T:IEntity 
{
返回上下文.GetObjectStateEntries(〜EntityState.Detached)
.Where(e =>!e.IsRelationship)
.Select(e => e.Entity)
.OfType< T>()
.SingleOrDefault(e => e.Id == entity.Id);
}

您将必须分离从此方法返回的实例。



无论如何,我会开始想,为什么你需要这个,因为它看起来像你的架构有另一个错误的概念。你为什么不直接使用附件?如果您不使用它们,为什么甚至可以保留上下文?


I was getting an error

An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key.

After i googled it i found IsAttachedTo extension method there:

Is is possible to check if an object is already attached to a data context in Entity Framework?

here is my code:

foreach (string s in types)
   {
    Subscription subscription = new Subscription { Id = Int32.Parse(s) };

    if (service.repository._context.IsAttachedTo(subscription))
        service.repository._context.Detach(subscription);

    service.repository._context.AttachTo("Subscriptions", subscription); //error here
    horse.Subscriptions.Add(subscription);
    }

But when the subscription with the same key appeared in the foreach loop the extension method IsAttachedTo returning false every time, it is does not detect that there is already such entity attached. And in result i am getting the same error:

An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key.

Why it is could be?

What can i do to fix that?

解决方案

I have little code review for you because your sample code scares me.

You probably read a lot about fancy design patterns and layered architectures and you started to use them yourselves. Unfortunately you missed the main point. What the hell is this?

service.repository._context.XXX

Why do you bother with any service layer or repository layer if they don't encapsulate their logic? Why do you expose repository on the service? Nobody should know about service internal implementation? Even worse why do you expose context on the repository? That spoiled the whole point of the repository!

There are a lot of supporting rules for writing high quality object oriented code. One of this rules is called Law of Demeter. You don't have to follow each rule, you also don't have to follow rules all the time but in case of layered architecture this law is a must.

If you have three layers A -> B -> C the layer A can call methods on the layer B but it doesn't know about C and it cannot reach its methods. If it can, it is not a new layer but it is the same layer as B and the layer A doesn't need to call it through B, it can call it directly.

In your example you have just exposed D to A because A is current layer, B is service, C is repository and D is context.

One more points about your code. There are well known naming conventions. These conventions are not about I like this and you like that but about the fact that framework you are using follow these conventions strictly so using another one to mix your naming convention with framework naming convention make your code look messy.

I'm sorry, If this was only some example code to make your code structuring clear. I just needed to describe how wrong this code is.


Now to your real problem. The method you have referenced from the related question will not work in your case. I think it will work only if you load the subscription from the database. The reason is that the referenced method uses EntityKey (either internally or directly) to get the entity from context but your new entity doesn't have the entity key yet. I expect that calling TryGetObjectStateEntry for your entity will always return Detached. Entity key it is created during attaching or you have to build it manually.

If you want some IsAttachedTo method try this:

public bool IsAttachedTo<T>(this ObjectContext context, T entity) where T : IEntity
{
    return context.GetObjectStateEntries(~EntityState.Detached)
                  .Where(e => !e.IsRelationship)
                  .Select(e => e.Entity)
                  .OfType<T>()
                  .Any(e => e.Id == entity.Id);
}

And make sure that your entity implements helper interface

public interface IEntity
{
    int Id { get; } 
} 

But to be able to detach attached entity you will need:

public T GetAttached<T>(this ObjectContext context, T entity) where T : IEntity
{
    return context.GetObjectStateEntries(~EntityState.Detached)
                  .Where(e => !e.IsRelationship)
                  .Select(e => e.Entity)
                  .OfType<T>()
                  .SingleOrDefault(e => e.Id == entity.Id);
}

You will have to detach instance returned from this method.

Anyway I would start to think why do you need that for the first place because it looks like your architecture has another wrong concept. Why don't you use attached entities directly? If you don't use them why do you even keep the context with them?

这篇关于EF 4.0 IsAttachedTo扩展方法和错误具有相同密钥的对象已经存在的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆