Asp.net 无法使用EF 6更新实体-ObjectStateManager错误

Asp.net 无法使用EF 6更新实体-ObjectStateManager错误,asp.net,asp.net-mvc,entity-framework,Asp.net,Asp.net Mvc,Entity Framework,我正在尝试使用实体框架版本6更新实体 我正在从数据库中选择实体,如下所示 public T Find<T>(object id) where T : class { return this._dbContext.Set<T>().Find(id); } 并像这样更新实体 public T Update<T>(T entity) where T : class { // get the primar

我正在尝试使用实体框架版本6更新实体

我正在从数据库中选择实体,如下所示

 public T Find<T>(object id) where T : class
    {
        return this._dbContext.Set<T>().Find(id);
    }
并像这样更新实体

  public T Update<T>(T entity) where T : class
    {
        // get the primary key of the entity
        object id = this.GetPrimaryKeyValue(entity);

        // get the original entry
        T original = this._dbContext.Set<T>().Find(id);


        if (original != null)
        {
            // do some automatic stuff here (taken out for example)

            // overwrite original property values with new values
            this._dbContext.Entry(original).CurrentValues.SetValues(entity);
            this._dbContext.Entry(original).State = EntityState.Modified;

            // commit changes to database
            this.Save();

            // return entity with new property values
            return entity;
        }

        return default(T);
    }
GetPrimaryKeyValue函数如下所示

 private object GetPrimaryKeyValue<T>(T entity) where T : class
    {
        var objectStateEntry = ((IObjectContextAdapter)this._dbContext).ObjectContext
            .ObjectStateManager
            .GetObjectStateEntry(entity);

        return objectStateEntry.EntityKey.EntityKeyValues[0].Value;
    }
为了清楚起见。我选择原来的条目,因为我需要执行我取出的一些并发逻辑。我没有在实体中发布该数据,需要再次从DB中手动选择该数据以执行检查

我知道,如果实体上有多个主键,则GetPrimaryKeyValue函数并不理想。我只是想让它暂时起作用

更新时,实体框架在尝试执行GetPrimaryKeyValue函数时会出现以下错误

ObjectStateManager不包含引用类型为“实体的名称”或“找不到”的对象的ObjectStateEntry

我以前写过很多存储库,但我从来没有遇到过这个问题,我似乎无法找到它为什么不起作用

任何帮助都将不胜感激

谢谢大家


Steve

似乎您在从传入的实体获取PK时遇到了问题。您可以使用它们的键属性,也可以创建自己的键属性,只使用反射来收集键名称,而不必尝试通过EF获取这些数据。这还允许您在需要时检索多个键。下面是我在LinqPad内部创建的一个示例,您应该能够将其设置为Program模式,并将其粘贴到中,然后查看它的工作情况。破解代码,尽你所能使用它。我实现了一个IEntity,但它不是必需的,您可以将该属性更改为任何真正的属性

结果如下:

Keys found: 
CustomIdentifier
LookASecondKey
代码如下:

// this is just a usage demo
void Main()
{
    // create your object from wherever
    var car = new Car(){ CustomIdentifier= 1, LookASecondKey="SecretKey", Doors=4, Make="Nissan", Model="Altima" };

    // pass the object in
    var keys = GetPrimaryKeys<Car>(car);

    // you have the list of keys now so work with them however
    Console.WriteLine("Keys found: ");  
    foreach(var k in keys)
            Console.WriteLine(k);   
}

// you probably want to use this method, add whatever custom logic or checking you want, maybe put
private IEnumerable<string> GetPrimaryKeys<T>(T entity) where T : class, IEntity
{
    // place to store keys
    var keys = new List<string>();

    // loop through each propery on the entity
    foreach(var prop in typeof(T).GetProperties())
    {
        // check for the custom attribute you created, replace "EntityKey" with your own
        if(prop.CustomAttributes.Any(p => p.AttributeType.Equals(typeof(EntityKey))))
            keys.Add(prop.Name);
    }

    // check for key and throw if not found (up to you)
    if(!keys.Any())
        throw new Exception("No EntityKey attribute was found, please make sure the entity includes this attribute on at least on property.");

    // return all the keys
    return keys;
}

// example of the custom attribute you could use
[AttributeUsage(AttributeTargets.Property)]
public class EntityKey : Attribute
{

}

// this interface is not NEEDED but I like to restrict dal to interface
public interface IEntity { }

// example of your model
public class Car : IEntity
{
    [EntityKey] // add the attribure to property
    public int CustomIdentifier {get;set;}

    [EntityKey] // i am demonstrating multiple keys but you can have just one
    public string LookASecondKey {get;set;}

    public int Doors {get;set;}
    public string Make {get;set;}
    public string Model {get;set;}
}

嗨,托尼。谢谢你的快速回复。我尝试了这个,但得到了以下错误。”一个实体对象不能被多个IEntityChangeTracker实例引用。“那么这告诉我它已经附加到上下文了吗?!”?!?是的,实体是有效的,但当调用SaveChanges时,它不会出错,但也不会更新数据库。抱歉,为了更正上述评论,当我试图从发布的实体中获取主键值时,我甚至没有得到原始值,因为它爆炸了。这应该是上下文的一部分,只是为了澄清一下。Save方法基本上执行dbContext.SaveChanges子项。我无法获取原始项,因为当我尝试从实体变量获取主键名称ID时,它给出了错误。ObjectStateManager不包含引用类型为“name\u of_entity\u it\u cannot\u FIND”的对象的ObjectStateEntry。我已更新了答案,我将删除上面的对话,因为SO正在抱怨我打开一个聊天窗口。我提供的答案是在不使用EF的情况下帮助获得PK,让我知道他们是否有助于你的更新。我对你的代码有点困惑。您的变更跟踪程序中是否有T实体?因为如果是,那么调用_dbContext.Set.Findid将不会进行数据库查询。它将在变更跟踪器中查找,看看它是否已经存在,然后给你相同的实体。因此,在这一点上,original和entity将是相同的引用。与上述注释相关,如果传递到更新方法中的T entity不在更改跟踪器中,则您不能对其调用CurrentValue。Hi Dismissible。甚至在我取出所有代码并只保留在“this.\u dbContext.EntryENTITY.State=EntityState.Modified;”中之后。我还是会出错的。”一个实体对象不能被多个IEntityChangeTracker实例引用。这是否意味着它已经是更改跟踪器的一部分?