具有EF4 CTP5的存储库模式 [英] Repository pattern with EF4 CTP5

查看:107
本文介绍了具有EF4 CTP5的存储库模式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图用ef4 ctp5实现存储库模式,我想出了一些东西,但是我不是ef的专家,所以我想知道我做了什么是好的。



这是我的数据库上下文

  public class Db:DbContext 
{
public DbSet< User>用户{get;组; }
public DbSet< Role>角色{get;组;
}

和存储库:(简化)

  public class Repo< T> :IRepo< T>其中T:Entity,new()
{
private readonly DbContext context;

public Repo()
{
context = new Db();
}

public IEnumerable< T> GetAll()
{
return context.Set&T;()。AsEnumerable();
}

public long Insert(T o)
{
context.Set< T>()。Add(o);
context.SaveChanges();
return o.Id;
}
}


解决方案

你需要回顾一下存储库应该做什么。存储库用于检索记录,添加记录和更新记录。您创建的存储库几乎不能处理第一种情况,处理第二种情况但不能有效地执行,并且完全不处理第三种情况。



大多数通用存储库都有一个接口沿着

  public interface IRepository< T>其中T:class 
{
IQueryable< T>得到();
void Add(T item);
void Delete(T item);
void CommitChanges();
}

对于检索记录,您不能只用 AsEnumerable()因为这会将该表的每个数据库记录加载到内存中。如果您只希望用户名为 username1 的用户,则不需要下载数据库的每个用户,因为这将是一个非常大的数据库性能命中,而且大客户端的性能根本没有好处。



相反,正如您将从上面发布的界面中看到的,您想要返回一个 IQueryable< T> 目的。 IQuerable 允许任何调用存储库的类使用Linq并向数据库查询添加过滤器,一旦IQueryable运行,它就完全在数据库上运行,只检索你想要的记录数据库更好地排序和过滤数据,然后您的系统,所以最好尽可能多地在数据库中。尽可能的。



现在关于插入数据,你有正确的想法,但你不想立即调用 SaveChanges()。原因是在您的所有数据库操作排队之后,最好调用 Savechanges()。例如,如果要在一个操作中创建用户和他的个人资料,则无法通过您的方法,因为每个插入调用将导致将数据插入到数据库然后。



相反,你想要将 Savechanges()调用到 CommitChanges 方法我有以上。



还需要处理数据库中的更新数据。为了更改实体的数据,实体框架会跟踪所收到的所有记录,并观察它们以查看是否进行了任何更改。但是,您仍然必须告诉实体框架将所有更改的数据发送到数据库。这个开始与 context.SaveChanges()调用。因此,您需要将其单独调用,以便您能够实际更新当前实现不能处理的已编辑数据。





编辑:
您的评论让我意识到我看到的另一个问题。一个垮台就是在库中创建一个数据环境,这不是很好。您真的应该拥有所有(或大多数)您创建的存储库共享数据上下文的相同实例。



实体框架跟踪实体被跟踪的内容,如果您尝试在一个上下文中更新实体,则将异常。当您开始编辑相关的实体时,可能会发生这种情况。这也意味着您的 SaveChanges()调用不是事务性的,每个实体在自己的事务中被更新/添加/删除,这可能会变得混乱。 b
$ b

我在我的存储库中的解决方案是将 DbContext 传递到构造函数中的存储库中。


I'm trying to implement the repository pattern with ef4 ctp5, I came up with something but I'm no expert in ef so I want to know if what I did is good.

this is my db context

public class Db : DbContext
{
    public DbSet<User> Users { get; set; }
    public DbSet<Role> Roles { get; set; }
}

and the repository: (simplified)

public class Repo<T> : IRepo<T> where T : Entity, new()
{
        private readonly DbContext context;

        public Repo()
        {
            context = new Db();
        }

        public IEnumerable<T> GetAll()
        {
            return context.Set<T>().AsEnumerable();
        }

        public long Insert(T o)
        {
            context.Set<T>().Add(o);
            context.SaveChanges();
            return o.Id;
        }
}

解决方案

You need to step back and think about what the repository should be doing. A repository is used for retrieving records, adding records, and updating records. The repository you created barely handles the first case, handles the second case but not efficiently, and doesn't at all handle the 3rd case.

Most generic repositories have an interface along the lines of

public interface IRepository<T> where T : class
{
    IQueryable<T> Get();
    void Add(T item);
    void Delete(T item);
    void CommitChanges();
}

For retrieving records, you can't just call the whole set with AsEnumerable() because that will load every database record for that table into memory. If you only want Users with the username of username1, you don't need to download every user for the database as that will be a very large database performance hit, and a large client performance hit for no benefit at all.

Instead, as you will see from the interface I posted above, you want to return an IQueryable<T> object. IQuerables allow whatever class that calls the repository to use Linq and add filters to the database query, and once the IQueryable is run, it's completely run on the database, only retrieving the records you want. The database is much better at sorting and filtering data then your systems, so it's best to do as much on the DB as you can.

Now in regards to inserting data, you have the right idea but you don't want to call SaveChanges() immediately. The reason is that it's best to call Savechanges() after all your db operations have been queued. For example, If you want to create a user and his profile in one action, you can't via your method, because each Insert call will cause the data to be inserted into the database then.

Instead what you want is to separate out the Savechanges() call into the CommitChanges method I have above.

This is also needed to handle updating data in your database. In order to change an Entity's data, Entity Framework keeps track of all records it has received and watches them to see if any changes have been made. However, you still have to tell the Entity Framework to send all changed data up to the database. This happenes with the context.SaveChanges() call. Therefore, you need this to be a separate call so you are able to actually update edited data, which your current implementation does not handle.


Edit: Your comment made me realize another issue that I see. One downfall is that you are creating a data context inside of the repository, and this isn't good. You really should have all (or most) of your created repositories sharing the same instance of your data context.

Entity Framework keeps track of what context an entity is tracked in, and will exception if you attempt to update an entity in one context with another. This can occur in your situation when you start editing entities related to one another. It also means that your SaveChanges() call is not transactional, and each entity is updated/added/deleted in it's own transaction, which can get messy.

My solution to this in my Repositories, is that the DbContext is passed into the repository in the constructor.

这篇关于具有EF4 CTP5的存储库模式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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