2015-06-20 45 views
1

我写一些测试练习吧建在Telerik的OpenAccess的ORM库的库层和正在运行到一些问题,管理的语境。麻烦将对象附加到一个Telerik的OpenAccess的数据上下文

我创建一个新的RegionEntity对象,并将其添加到数据库中。我使用using语句,以便上下文清理完毕。另外,我还创建了添加的RegionEntity的Detached副本,以便稍后可以将其重新附加到上下文。

private RegionEntity AddTestRegionToTable() 
    { 
     String regionName = Guid.NewGuid().ToString(); 
     RegionEntity newRegion = new RegionEntity() { /*...property assignment goes here ...*/ }; 
     RegionEntity ret = null; 

     using (DbContext ctx = new DbContext()) 
     { 
      ctx.Add(newRegion); 
      ctx.SaveChanges(); 
      ret = ctx.CreateDetachedCopy<RegionEntity>(newRegion); 
     } 

     return ret; 
    } 

到目前为止......没问题。在我的TestMethod下面,我调用上面的方法并接收一个Detached RegionEntity。 (我已经撤消了我的断言,因为它们对于这个问题无关紧要)。然后我将实体传递给我想测试的Respository方法。

[TestMethod] 
    public void RemoveRegion_Success() 
    { 
     // 
     // Assemble 
     RegionEntity origEntity = AddTestRegionToTable(); 

     // 
     // Act 
     deletedEntity = RegionRepository.RemoveEntity<RegionEntity>(origEntity); 

     // 
     // Assert 
    /* asserts go here */ 

    } 

为了完整起见,下面我已经包括了所有剩余的代码,正是因为它出现在我的应用程序。存储库方法是通用的(再次...应该与问题无关)。第一种方法是由测试方法被称为一个,在该区域通过作为entityToRemove参数。这种方法,依次调用DBUtils方法,的getContext(),将无论是从实体检索的DbContext,还是......如果一个人不能够导出...创建一个新的上下文中使用。在我们的例子中,正在创建一个新的上下文。

public class RegionRepository 
    { 
     public static T RemoveEntity<T>(T entityToRemove) where T : class 
     { 
      T ret = null; 

      using (DbContext ctx = DbUtils.GetContext<T>(entityToRemove)) 
      { 
       ret = RemoveEntity<T>(ctx, entityToRemove); 
       ctx.SaveChanges(); 
      } 

      return ret; 
     } 

     public static T RemoveEntity<T>(DbContext ctx, T entityToRemove) where T : class 
     { 
      // 
      // first chcek to see if the listingToUpdate is attached to the context 
      ObjectState state = OpenAccessContext.PersistenceState.GetState(entityToRemove); 
      // 
      //If the object is detached then attach it 
      if (state.HasFlag(ObjectState.Detached)) 
      { 
       ctx.AttachCopy<T>(entityToRemove); 
      } 
      // 
      // confirm that the DETACHED flag is no longer present. 
      ObjectState state2 = OpenAccessContext.PersistenceState.GetState(entityToRemove); 

      if (state2.HasFlag(ObjectState.Detached)) 
      { 
       throw new Exception("Unable to attach entity to context"); 
      } 

      ctx.Delete(entityToRemove); 
      return entityToRemove; 
     } 
} 


public class DBUtils 
{ 
     public static DbContext GetContext<T>(T entity) 
     { 
      DbContext ret = OpenAccessContextBase.GetContext(entity) as DbContext; 

      if(ret == null) 
      { 
       ret = new DbContext(); 
      } 

      return ret; 

     } 

    } 

无论如何,该方法然后将该上下文和实体作为参数传递给重载。此方法将DbContext作为附加参数(允许在多步骤工作流程中使用单个上下文)。使得在使用应该还是我们从实体中提取的一种或在我们的的getContext()方法创建的上下文。然后检查该实体是否附加到上下文中。在这种情况下我得到“独立式”的标志作为国家标志之一(其余MaskLoaded | MaskManaged | MaskNoMask)所以后来的进程中高度的实体范围内并且在第二次检查我确认独立标志已不存在。

事实证明该实体没有附着......和异常被抛出。

我已经阅读拆卸和连接对象的背景下Telerik的文档... Attaching and Detaching Objects

回答

1

通过设计ObjectState是标志枚举同时包含表单数据访问的持久状态的基本价值和持久状态他们自己。

在此枚举,Detached是参与三个分离的永久状态的值:DetachedCleanDetachedDirty,并DetachedNew。您可以在this article中找到有关值和状态的更多信息。

当您从上下文中分离的对象,它的状态是DetachedClean。如果此时您更改了任何属性,对象的状态将变为DetachedDirty。如果您将对象附加回来,它将保持在附件之前的状态。简而言之,附加对象的动作不会改变其状态。

换句话说,检查Detached就是为什么你会得到“无法连接实体上下文”异常的原因。该值将始终在您的对象的状态下可用。

正如我读的代码前,在这条线:

ctx.Delete(entityToRemove); 

你会得到一个异常,无论如何,因为数据访问不允许删除通过上下文的另一个实例中检索对象。唯一的例外是:

出现InvalidOperationException:两个不同的对象范围之间的对象引用是不允许的。

我希望这会有所帮助。

- =编辑= -

当您将某个对象的上下文的实例,并调用调用SaveChanges()方法,数据访问会自动决定是否在数据库中插入新行或更新现有的行。就此而言,插入和更新场景由Attach/Detach API处理。

关于删除的情况下,你有两个选择:

  1. 从数据库中检索对象,并通过删除()方法来删除它(和调用的SaveChanges()),就像这样:

    var myObj = ctx.RegionEntities.First(r => r.Id == entityToRemove.Id); 
        ctx.Delete(myObj); 
        ctx.SaveChanges(); 
    
  2. 要使用BulkDelete功能是这样的:

    var myObj = ctx.RegionEntities.Where(r => r.Id == entityToRemove.Id); 
        int deletedObjects = myObj.DeleteAll(); 
    

有些事情,你需要用第一个选项要考虑的是是否要调用的SaveChanges(),则连接对象之后。如果在删除对象之前想要保留更改,那么这样做是个好主意。此外,在使用上下文的Delete()方法时,需要在处理上下文的当前实例之前通过SaveChanges()方法提交更改。如果你不这样做,事务将被回滚,这意味着对象不会被删除。有关交易处理的详细信息,请参阅here

第二个选项,批量删除,在该呼叫到DeleteAll()方法执行在一个单独的事务的任何删除操作。因此,其他未提交的更改不受影响。不过,在附加对象后,您需要考虑调用SaveChanges(),特别是如果附加对象和删除的对象是同一个对象。

+0

谢谢您的回应。我不会说谎......这对我来说似乎有些复杂。如果我必须使用原始上下文来删除或更新现有记录,那么我将如何去更新/删除一个我不再参考原始上下文的实体(记录)?我认为CreateDetachedCopy的整个意义在于减轻依赖;使其基本上成为一个自由实体,我可以将其附加到任何我可能具有的环境中。 –

+0

我正在编辑我的原始答复,以解决您的问题。 –

+0

谢谢,这是真正优秀的信息。我所经历的问题是,如果我不能将它重新附加到路上的另一个实例上,那么分离实体的意义何在?在我的OP中,我使用上下文的实例“A”创建了一个区域。我的期望是我可以分离它,对实体实例进行一些更改,然后使用上下文的实例“B”保存这些更改。由于它是分离的,我所需要做的就是将其附加到上下文的另一个实例。我相信我只是没有“得到它”。我错过了什么? –