簡體   English   中英

我如何模擬實體框架的導航財產情報?

[英]How Do I Mock Entity Framework's Navigational Property Intelligence?

我正在嘗試使用內存中的模擬上下文來測試我的存儲庫。

正如大多數人所做的那樣,我使用內存中的字典來實現它。 這將我的存儲庫接口上的成員實現為使用內存中集合的Add,Remove,Find等。

這在大多數情況下工作正常:

[TestMethod]
public void CanAddPost()
{
    IRepository<Post> repo = new MockRepository<Post>();
    repo.Add(new Post { Title = "foo" });
    var postJustAdded = repo.Find(t => t.Title == "foo").SingleOrDefault();
    Assert.IsNotNull(postJustAdded); // passes
}

但是,我有以下測試,我無法通過模擬存儲庫(適用於SQL存儲庫)。

考慮我有三個存儲庫:

  1. 帖子 (處理用戶內容帖子,如StackOverflow問題)。
  2. 地點 (世界上的位置,如“洛杉磯”)。
  3. LocationPosts (用於處理帖子/位置之間的多個聯結的聯結表)。

帖子可以添加到任何地方,也可以添加特定的位置。

現在,這是我的測試:

[TestMethod]
public void CanAddPostToLocation()
{
   var location = locationRepository.FindSingle(1); // Get LA
   var post = new Post { Title = "foo", Location = location }; // Create post, with LA as Location.
   postRepository.Add(post); // Add Post to repository

   var allPostsForLocation = locationPostRepository.FindAll(1); // Get all LA posts.
   Assert.IsTrue(allPostsForLocation.Contains(post)); // works for EF, fails for Mock.
}

基本上,當使用“真正的”EF / SQL存儲庫時,當我向特定位置添加帖子時,EntityFramework足夠智能添加“LocationPost”記錄,因為EDMX中的關聯(“LocationPosts”導航屬性“發布“實體”

但是,我怎樣才能使我的Mock存儲庫足夠智能以“模仿”這種EF智能?

當我在我的模擬存儲庫上執行“添加”時,這只會添加到字典中。 它沒有聰明才能去“哦,等等,你有一個依賴關聯,讓我把它添加到OTHER存儲庫”。

我的模擬存儲庫是通用的,所以我不知道如何把智能放在那里。

我還看過創建一個FakeObjectContext / FakeObjectSet(正如Julie Lerman在她的博客上所建議的那樣),但是這仍然沒有涵蓋這個場景。

我有一種感覺,我的嘲弄解決方案還不夠好。 任何人都可以提供幫助,或者提供有關如何正確模擬覆蓋我的場景的Entity Framework 4 / SQL Server存儲庫的最新文章嗎?

問題的核心是我每個聚合根有一個存儲庫(這很好,但也是我的垮台)。

所以PostLocation都是聚合根,但都不是“擁有” LocationPosts

因此,它們是3個單獨的存儲庫,在內存中,它們是3個單獨的字典。 我想我在內存回購中錯過了它們之間的“粘合劑”。

編輯

問題的一部分是我使用Pure POCO (沒有EF代碼生成)。 我也沒有任何更改跟蹤(沒有基於快照的跟蹤,沒有代理類)。

我認為這是“聰明人”發生的地方。

目前,我正在探索代表選項。 我在我的Generic Mock Repository中暴露了一個事件(void,接受泛型T,作為Entity),我在“Add”之后調用它。 然后,我在“Post Repository”中訂閱此事件,我計划將相關實體添加到其他存儲庫。

這應該工作。 如果它這樣做,將作為答案。

但是,我不確定這是最好的解決方案,但是再一次,這只是為了滿足模擬(代碼不會用於實際功能)。

正如我在編輯中所說,我探索了委托選項,該選項已成功運作。

這是我如何做到的:

namespace xxxx.Common.Repositories.InMemory // note how this is an 'in-memory' repo
{
   public class GenericRepository<T> : IDisposable, IRepository<T> where T : class
   {
      public delegate void UpdateComplexAssociationsHandler<T>(T entity);
      public event UpdateComplexAssociationsHandler<T> UpdateComplexAssociations;

      // ... snip heaps of code

      public void Add(T entity) // method defined in IRepository<T> interface
      {
         InMemoryPersistence<T>().Add(entity); // basically a List<T>
         OnAdd(entity); // fire event
      }

      public void OnAdd(T entity)
      {
         if (UpdateComplexAssociations != null) // if there are any subscribers...
            UpdateComplexAssociations(entity); // call the event, passing through T
      }
   }
}

然后,在我的In Memory“Post Repository”(繼承自上面的類)。

public class PostRepository : GenericRepository<Post>
{
   public PostRepository(IUnitOfWork uow) : base(uow)
   {
      UpdateComplexAssociations += 
                  new UpdateComplexAssociationsHandler<Post>(UpdateLocationPostRepository);
   }

   public UpdateLocationPostRepository(Post post)
   {
      // do some stuff to interrogate the post, then add to LocationPost.
   }
}

您也可以認為“保持,PostRepository 派生自GenericRepository,那么為什么要使用委托,為什么不重寫Add?” 答案是“添加”方法是IRepository的接口實現 - 因此不能是虛擬的。

正如我所說,不是最好的解決方案 - 但這是一個嘲弄的場景(對代表來說是個好例子)。 我的印象並不是很多人在模擬,純粹的POCO和存儲庫/工作模式單元(沒有POCO的變化跟蹤)方面“走得這么遠”。

希望這有助於其他人。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM