0

我的应用程序中有一个干净的域层,它是以 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 中完成所有干预之后,不确定这是否可能

那么,过去这个问题是如何解决的呢?这里有可重用的模式吗?

4

1 回答 1

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_USERS 映射到用户类,字段具有不同的名称等,这里有一篇文章

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

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

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

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

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 或 get all on the repository ...

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

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

希望这可以帮助

于 2012-06-28T02:19:37.527 回答