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

时间:2014-09-19 12:22:22

标签: asp.net asp.net-mvc entity-framework

我尝试使用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 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;
    }

为了清晰起见。我选择原始输入,因为我需要执行一些并发逻辑(我已经取出)。我没有在实体上发布该数据,需要再次从数据库中手动选择它来执行检查。

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

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

ObjectStateManager不包含ObjectStateEntry,该ObjectStateEntry引用了类型为&#39; NAME_OF_ENTITY_IT_CANNOT_FIND&#39;

的对象

我以前写了很多资料库而且我从来没有遇到过这个问题,我似乎无法找到它为什么不起作用(因此发帖)。

非常感谢任何帮助。

谢谢你们!

史蒂夫

1 个答案:

答案 0 :(得分:0)

看起来你从传入的实体获取PK时遇到了问题。你可以使用他们的Key属性或者创建自己的Key属性而不是尝试通过EF来获取这些数据,而只是使用反射来收集关键名称是。这还允许您在需要时检索多个密钥。下面是我在LinqPad中创建的一个例子,您应该可以将其设置为&#34; Program&#34;模式并将其粘贴并查看它是否有效。破解代码并使用您可能使用的代码。我实现了一个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;}
}