我如何模拟实体框架的导航属性智力? [英] How Do I Mock Entity Framework's Navigational Property Intelligence?

查看:213
本文介绍了我如何模拟实体框架的导航属性智力?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图测试使用的是内存中的模拟方面,我国资源库。



我在内存词典实现这一点,因为大多数人做的。这实现了我的仓库界面成员添加,删除,查找等,与内存收集工作。



这工作正常,在大多数情况下:



  [TestMethod的] 
公共无效CanAddPost()
{
IRepository<邮政和GT;回购=新MockRepository<邮政>();
repo.Add(新邮{标题=富});
VAR postJustAdded = repo.Find(T => t.Title ==富)的SingleOrDefault()。
Assert.IsNotNull(postJustAdded); //把
}



不过,我有以下的测试,我不能得到通过与模拟库(正常工作的SQL资料库)



考虑我有三个存储库:




  1. 文章(处理用户的内容的信息,就像一个计算器的问题)。

  2. 位置(在世界中的位置,如洛杉矶)。

  3. LocationPosts (结表来处理帖子/地区之间的多对多)。

  4. < /醇>

    帖子可以添加到不通,或者它们也可以与一个特定的位置加入。



    现在,这里的我的测试:

      [TestMethod的] 
    公共无效CanAddPostToLocation()
    {
    变种位置= locationRepository.FindSingle(1); //获取LA
    无功后=新邮{标题=富,位置=位置}; //为位置创建后,与洛杉矶。
    postRepository.Add(岗位); //添加后到存储库

    VAR allPostsForLocation = locationPostRepository.FindAll(1); //获取所有LA帖子。
    Assert.IsTrue(allPostsForLocation.Contains(后)); //为EF的作品,失败模拟。
    }



    基本上,使用真正的EF / SQL存储库,当我加入的时候一张贴到特定位置,的EntityFramework是足够聪明,添加LocationPost记录,因为在EDMX协会(LocationPosts导航属性的邮报的实体)



    但我怎样才能让我的模拟库足够聪明的模仿这个EF智慧呢?



    当我这样做对我的模拟库添加,这只是增加该词典。它没有智慧去哦,等等,你有相关的协会,让我加入到其他存储库你。



    我的模拟库是通用的,所以我不知道如何把智慧在里面。



    我也看了创造一个FakeObjectContext / FakeObjectSet(如在她的博客中建议由朱莉·勒曼),但这还是不包括这种情况。



    我有一种感觉,我嘲讽的解决方案是不够好。谁能帮助,或者提供有关如何正确地嘲笑实体框架4 / SQL Server存储库涵盖了我的情况向上最新的文章?



    核心这个问题是我每一个存储库的聚合根(这是好的,但也是我下台)。



    所以, 上传位置都聚集根,但既不是自己在 LocationPosts



    因此,它们是3独立库,并在内存中的情况下,它们是3独立词典。我觉得我失去了我的内存回购他们之间的粘合剂。



    修改



    问题的部分原因是,我使用的纯POCO的(无EF代码生成)。我也没有任何变化跟踪(不基于快照的跟踪,无代理类)。



    我的印象,这是这里的智能发生在



    目前,我正在探索委托选项。我揭露我的通用模拟存储库中的事件(无效,接受通用的T,是实体),我后添加调用。然后我订阅了这个事件在我的后库,在这里我打算将相关的实体添加到其他存储库。



    这应该工作。将把仿佛它这样的答案。



    不过,林不知道它这是最好的解决办法,但话又说回来,这仅仅是为了满足嘲讽(代码夺得 T为用于真正的功能)。


    解决方案

    我在我的编辑说,我探索委托选项,它已成功地工作



    下面是我如何做的:

     命名空间xxxx.Common .Repositories.InMemory //注意这是怎样的一个'内存'回购
    {
    公共类GenericRepository< T> :IDisposable接口,IRepository< T>其中T:类
    {
    公众委托无效UpdateComplexAssociationsHandler< T>(T实体);
    公共事件UpdateComplexAssociationsHandler< T> UpdateComplexAssociations;

    // ...喀嚓代码

    公共无效的堆加入(T实体)//在IRepository中所定义的方法; T>接口
    {
    InMemoryPersistence< T>()添加(实体); //基本上列表< T>
    使用onAdd(实体); //火灾事件
    }

    公共无效使用onAdd(T实体)
    {
    如果(UpdateComplexAssociations!= NULL)//如果有任何用户...
    UpdateComplexAssociations(实体); //调用事件,通过T
    }
    }
    }

    然后,在我的记忆后库(从上面的类继承)

     公共类PostRepository: GenericRepository<邮政和GT; 
    {
    公共PostRepository(IUnitOfWork UOW):基地(UOW)
    {
    UpdateComplexAssociations + =
    新UpdateComplexAssociationsHandler<邮政>(UpdateLocationPostRepository);
    }

    公共UpdateLocationPostRepository(后后)
    {
    //做一些东西来询问后,再加入到LocationPost。
    }
    }

    您可能还觉得坚持住,PostRepository的提炼出来的从GenericRepository,所以为什么要使用委托,你为什么不重写添加?答案是添加方法是IRepository接口实现 - 因此不能是虚拟



    正如我所说的,并不是最好的解决方案 - 但是这是一个嘲讽方案(和良好的情况下,对于委托)。我的印象不是很多人都在嘲笑的工作模式,纯POCO的和库/单位(没有变化对POCO的跟踪)方面正在进行的这么远。



    希望这可以帮助别人了。


    I'm attempting to test my repository using an in-memory mock context.

    I implement this with an in-memory Dictionary, as most people do. This implements members on my repository interface to Add, Remove, Find, etc, working with the in-memory collection.

    This works fine in most scenarios:

    [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
    }
    

    However, i have the following test which i cannot get to pass with the mock repository (works fine for the SQL repository).

    Consider i have three repositories:

    1. Posts (handles user content posts, like a StackOverflow question).
    2. Locations (locations in the world, like "Los Angeles").
    3. LocationPosts (junction table to handle many-many between Posts/Locations).

    Posts can be added to nowhere, or they can also be added with a particular Location.

    Now, here's my test:

    [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.
    }
    

    Basically, when using the "real" EF/SQL Repository, when i add a Post to a particular location, EntityFramework is smart enough to add the "LocationPost" record, because of the association in the EDMX ("LocationPosts" navigational property on "Post" entity)

    But how can i make my Mock repository smart enough to "mimic" this EF intelligence?

    When i do "Add" on my mock repository, this just adds to the Dictionary. It has no smarts to go "Oh, wait, you have a dependant association, let me add that to the OTHER repository for you".

    My Mock Repository is generic, so i dont know how to put the smarts in there.

    I have also looked at creating a FakeObjectContext / FakeObjectSet (as advised by Julie Lerman on her blog), but this still does not cover this scenario.

    I have a feeling my mocking solution isn't good enough. Can anyone help, or provide an up-to-date article on how to properly mock an Entity Framework 4/SQL Server repository covering my scenario?

    The core of the issue is i have one repository per aggregate root (which is fine, but is also my downfall).

    So Post and Location are both aggregate roots, but neither "own" the LocationPosts.

    Therefore they are 3 seperate repositories, and in an in-memory scenario, they are 3 seperate Dictionaries. I think i'm missing the "glue" between them in my in-memory repo.

    EDIT

    Part of the problem is that i am using Pure POCO's (no EF code generation). I also do not have any change tracking (no snapshot-based tracking, no proxy classes).

    I am under the impression this is where the "smarts" happen.

    At the moment, i am exploring a delegate option. I am exposing a event in my Generic Mock Repository (void, accepts generic T, being the Entity) which i invoke after "Add". I then subscribe to this event in my "Post Repository", where i plan to add the related entities to the other repositories.

    This should work. Will put as answer if it does so.

    However, im not sure it this is the best solution, but then again, this is only to satisfy mocking (code won't be used for real functionality).

    解决方案

    As i said in my EDIT, i explored the delegate option, which has worked succesfully.

    Here's how i did it:

    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
          }
       }
    }
    

    Then, in my In Memory "Post Repository" (which inherits from the above class).

    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.
       }
    }
    

    You may also think "hold on, PostRepository derives from GenericRepository, so why are you using delegates, why don't you override the Add?" And the answer is the "Add" method is an interface implementation of IRepository - and therefore cannot be virtual.

    As i said, not the best solution - but this is a mocking scenario (and a good case for delegates). I am under the impression not a lot of people are going "this far" in terms of mocking, pure POCO's and repository/unit of work patterns (with no change tracking on POCO's).

    Hope this helps someone else out.

    这篇关于我如何模拟实体框架的导航属性智力?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆