1

我使用MVC 3中的EF4 POCO和UnitOfWork/repository模式。我想了解如何修改要插入的新记录。如何在EF4中提交之前更新新记录?

我的服务方法插入/更新看起来像这样(仓库在服务构造通过的IoC注入):

public void UpdateData(Guid id, int newValue) 
{ 
    MyPoco poco = _repository.FirstOrDefault(p => p.Id = id); 

    if (poco == null) 
    { 
     poco = new Poco 
     { 
      //set properties 
     }; 

     _repository.Add(poco); 
    } 

    poco.SomeFieldToUpdate = newValue; 
} 

我的变化获得通过我的UnitOfWork坚持上一个UseUnitOfWorkAttribute行为过滤我控制器:

void IResultFilter.OnResultExecuted(ResultExecutedContext filterContext) 
{ 
    var unitOfWork = IoCFactory.Instance.CurrentContainer.Resolve<IUnitOfWork>(); 
    unitOfWork.Commit(); 
} 

当然,如果这是有史以来打只有一次,对于现有的或新的数据能正常工作。如果它已经存在,它可以在多次通行证中正常工作。

但是,如果Guid值不存在于表中,那么它会尝试执行多次插入(如果多次调用)。

所以这是我的困境。我明白为什么这不起作用,我只是不确定解决问题的正确方法。基本上,我需要以某种方式获得对UnitOfWork中现有POCO的引用,并以某种方式更新它。但UnitOfWork在我的服务中没有(按设计) - 我甚至不知道如何将实体拉出UoW并进行更新。

我在谈论这个错误还是我忽略了一些简单的东西?或者我在设计这个方面存在根本性缺陷?我有一种感觉,我可能会比应该做得更难。

在此先感谢。

+0

你在哪里插入新的Poco到版本库中以及何时/何地调用SaveChanges?你在上面的代码中创建的Poco直接进入垃圾收集,对象没有任何反应。我认为有一些重要的代码片段不能理解问题。你能多加一点吗? – Slauma 2011-03-19 13:02:48

+0

是的,这只是我的一个疏忽(已经很晚了)。 :)它已被添加。 – 2011-03-19 14:46:48

回答

2

发生这种情况的原因是因为您的实体尚未保存,您执行查询来获取它。查询将无法在数据库中找到它并正确返回null。

您应该不需要使用存储库/工作单元/ ObjectContex作为服务调用中未保存的实体的内部存储。如果你需要它,你应该检查你的应用程序设计并重构它,因为可能有错误。

无论如何,你可以从上下文中得到没有保存的实体,但它不是很好的代码。您需要使用特殊的方法才能通过标识获取实体。您将使用它而不是调用FirstOrDefault。喜欢的东西:

public MyPoco GetById(Guid id) 
{ 
    MyPoco enity = context.ObjectStateManager.GetObjectStateEntries(EntityState.Added) 
     .Where(e => e.Entity != null && e.Entity.GetType() == typeof(MyPoco))) 
     .Select(e => (MyPoco)e.Entity) 
     .Where(p => p.Id == id) 
     .SingleOrDefault(); 

    if (entity == null) 
    { 
     entity = context.MyPocos.FirstOrDefault(p => p.Id == id); 
    } 
} 
+0

感谢您的帮助。所以在我的设计中,我主要将这个问题用于存储在我的数据库中的自定义会话数据,并且我试图在各个位置采取各种操作(登录,注销,登录操作过滤器),而这些操作是分散的在我的应用程序。我的问题是这些都是松散耦合的,所以我没有简单的方法来保持示波器连接。有什么建议么? – 2011-03-19 16:54:37

+0

根据您对“您不应该需要使用存储库/工作单元/ ObjectContex作为服务调用中未保存的实体的内部存储”的评论,我深入了解了我的设计,并决定将我的Session poco保留在我的自定义UserPrincipal。这不仅给了我交叉访问权限,而且它似乎也属于这里。感谢您挑战我的设计。 – 2011-03-21 06:07:49

0

你设置你进入UpdateData作为新的波苏对象的关键,像这样的ID:

poco = new Poco 
{ 
    Id = id; 
    //set properties 
}; 

如果是的话,你可以为对象查询不FirstOrDefault但通过在储存库方法使用TryGetObjectByKey

public Poco GetPocoByKey(Guid id) 
{ 
    EntityKey key = new EntityKey("MyEntities.Pocos", "Id", id); 
    object pocoObject; 
    if (context.TryGetObjectByKey(key, out pocoObject)) 
     return (Poco)pocoObject; 

    return null; 
} 

的优点是,TryGetObjectByKey着眼于先入ObjectConte xt如果它可以找到具有指定键的对象。只有没有,那么数据库将被查询。由于第一次找不到新的Poco时,TryGetObjectByKey应该在上下文中再次搜索具有相同键的对象时找到它,即使它尚未保存到数据库中。

编辑:此解决方案不起作用!

由于TryGetObjectByKey没有找到针对其在added状态在ObjectContext的对象键,即使不如果键不是DB生成的密钥和由应用程序提供(见下文评论)。

+0

我担心这不会与未保存的实体一起工作,因为它具有临时密钥。 – 2011-03-19 15:50:36

+0

啊,我明白了。对于不在数据库中自动生成的身份也是这种情况吗?这正是我想到的,因为poco似乎有一个“Guid”作为键(通常不是DB创建的,而是在应用程序中生成的)。 EF不使用此密钥作为最终的非临时密钥,因为它知道在数据库中不会分配另一个密钥? – Slauma 2011-03-19 15:59:53

+0

我只是试图调用GetObjectByKey为新的实体添加到上下文(但没有保存),我得到了一个异常,所以可能没有,但也许我做错了什么。 – 2011-03-19 16:04:07