对于返回不同类型的存储库和EF数据上下文,什么是好模式?

时间:2012-06-28 01:11:21

标签: c# design-patterns ef-code-first repository-pattern automapper

我的应用程序中有一个很好的干净域图层,以DDD方式开发。在开发域时,根本没有考虑数据库。属性名称有意义,不在所有大写中,并且与我的应用程序相关。

今天,我正在实现一个存储库来从现有的EF DbContext中提取。 DbContext的开发(基本上)与设计不佳的Oracle数据库相匹配。

理想情况下,我想实现这样的存储库:

public interface IRepository {
    IQueryable<T> Find<T>(Expression<Func<T, bool>> query) where T : IMyDomainEntity;
}

T是我的域名实体。但是,在我的存储库中的Find方法中,我必须...

  1. 以某种方式将表达式转换为使用DbContext

      

    我不知道该怎么做。

  2. 查询DbContext

      

    表达式“映射”后,这很简单

  3. 以某种方式映射到我的域对象

      

    我确信我可以使用AutoMapper或实现我自己的映射器。

  4. 返回尚未前往数据库的IQueryable。

      

    在#1-3

    完成所有干预后,不确定这是否可行
  5. 那么,过去这个问题是如何解决的?这里有可重复使用的模式吗?

1 个答案:

答案 0 :(得分:0)

嗯,你已经走上了正确的轨道,只要实现你想说的话:)

1.您将表达式传递给find方法,只需在Where子句

中使用该表达式即可

2.你只需要从你的DbContext中获取正确的DbSet来查询,DbContext有一个获取给定类型的DbContext的方法,使用它然后你可以查询

public IQueryable<T> Find<T>(Expression<Func<T, bool>> query) where T : IMyDomainEntity
{
   var dbSet = context.Set<T>();
   return dbSet.Where(query);
}

3.如果您的域对象不是EF映射到数据库的域对象,则需要根据DbContext类中的数据库中的内容自定义映射(不需要自动化),因此您可能需要你的DbContext类

中有类似的东西
public class MyContext : DbContext 
{
   ...
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<User>()
                .Map(a => a.ToTable("DB_USERS"))
                .Property(a => a.Email).HasColumnName("MAIL");

            base.OnModelCreating(modelBuilder);
        }
}

要从DB中的表DB_USERS映射到类User,具有不同的字段名称等,这里有一篇文章

http://www.codeproject.com/Articles/165720/Using-the-Code-First-Model-Configuration-Classes

如果您不想/不能更改DbContext类,也可以使用属性将属性映射到正确的表列

http://msdn.microsoft.com/en-us/data/gg193958

或者您可以拥有一组映射到您的数据库的不同实体,并使用automapper将它们转换为您的域对象,但您不会丢失。因为您需要实现查询以将其自动化到您的域模型,所以需要4个铃声。

4.不需要做任何特别的事,EF负责那个

更新:无法访问DbContext的解决方案(不是完全通用的版本,但有效)

这个想法是为每个域类创建存储库的映射部分,因此所有域都正确绑定。继续使用User域模型和DBUser表模型:

public class User : IDomainModelEntity
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
}

public class DBUser
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int USER_ID { get; set; }

    [Required]
    [MaxLength(150)]
    public string USER_NAME { get; set; }

    [Required]
    [MaxLength(260)]
    public string USER_MAIL { get; set; }
}

然后你会有一个抽象的存储库和每个域类的具体存储库,它实现了映射的基本GetAll查询:

public abstract class Repository<T>  where T : IDomainModelEntity
{
    protected readonly DbContext _context;

    public Repository(DbContext context)
    {
        _context = context;
    }

    public abstract IQueryable<T> GetAll();

    public IQueryable<T> Find(Expression<Func<T, bool>> predicate)
    {
        return GetAll().Where(predicate);
    }

}

public class UserRepository : Repository<User>
{
    public UserRepository(DbContext context)
        : base(context)
    {
    }

    public override IQueryable<User> GetAll()
    {
        return _context.Set<DBUser>()
            .Select(u => new User
                {
                    Id = u.USER_ID,
                    Name = u.USER_NAME,
                    Email = u.USER_MAIL
                });
    }
}

现在要使用它,你只需调用find或获取存储库中的所有内容......

        using (var context = new CompanyDbContext())
        {
            var repo = new UserRepository(context);
            var list = repo.Find(a=>a.Id >= 2).ToList();

            list.ForEach(a => Console.WriteLine("Id: {0}, Name {1}, email {2}", a.Id, a.Name, a.Email));
        }

它不是完全通用的,因为您需要为每个需要使用的域类传递存储库,但这可能是可接受的折衷方案

希望这有帮助