通过存储库将数据库实体映射到域实体 - 静态属性还是单独的服务?

时间:2013-11-14 16:56:38

标签: c# asp.net-mvc entity-framework repository domain-driven-design

我正在开发一个应用程序,其中我的数据库架构与我的域模型不匹配,并且对数据库架构的修改不在我的控制之下。因此,我最终在我的存储库调用中进行了大量的映射,并且我有许多具体的repos来处理与数据库之间的映射(使用实体框架数据库优先)。我正在寻找的是一种基于域实体对象类型调用我的存储库的优雅方法。到目前为止,域模型本身仍然非常贫乏,因为我们仍然在定义业务规则。

我在别处看到了一个例子(无法回想起链接),其中存储库调用是通过静态属性通过域实体传递的,但我不知道这是否会出现线程问题或是否违反任何域模型原则,特别是如果我们决定在未来实施DI / IoC。

这是我到目前为止所拥有的一个例子。为了简洁起见,我简化了对数据库的映射调用,因为实际应用程序中的映射更复杂。

知识库示例:

public interface IRepository<T>
{
    T GetById(int id);
    void Save(T entity);
}

public abstract class RepositoryFactory<T> : IRepository<T>
{
    protected MyDbContext db;
    protected int userId;

    protected RepositoryFactory()
    {
        this.db = new MyDbContext();
        this.userId = WebSecurity.GetCurrentUser().UserId;
    }

    public abstract T GetById(int id);
    public abstract void Save(T entity);
}

public class CustomerRepository : RepositoryFactory<Customer>
{
    public override void Save(Customer customer)
    {
        var entity = db.customers.FirstOrDefault(p => p.customerid == customer.Id && p.users.userid == userId);

        if (entity == null) return;  // TODO: Add error trapping

        // Mapping (lots of reshaping happening here)
        entity.customername = customer.Name;
        entity.customeraddress = customer.Address;
        // ...

        // Save changes to DB
        db.Entry(entity).State = EntityState.Modified;
        db.SaveChanges();
    }

    public override Customer GetById(int id)
    {
        var entity = db.customers.FirstOrDefault(p => p.customerid == id && p.users.userid == userId);
        if (entity == null) return null;  // TODO: Add error trapping
        return new Customer
        {
            Name = entity.customername,
            Address = entity.customeraddress,
            // ...
        };
    }
}

域实体示例:

public class Entity { public int Id { get; set; } }

public class Customer : Entity
{
    public string Name { get; set; }
    public string Address { get; set; }

    // Does this violate any domain principles?
    public static IRepository<Customer> Repository
    {
        get { return new CustomerRepository(); }
    }
}

使用此代码,我可以从我的控制器执行以下操作:

Customer customer = Customer.Repository.GetById(id);

而不是:

IRepository<Customer> repo = new CustomerRepository();
Customer customer = repo.GetById(id);

这对我的问题来说似乎是一个非常优雅的解决方案,它也使我无需在我的控制器(MVC)中包含Repository命名空间。如果这闻起来很有趣并且有更好的方法来解决这个问题,我很乐意学习。我能想到的唯一另一件事就是创建一个单独的crud服务来处理我的存储库调用,但我想我需要一个字典或哈希表来将我的具体存储库映射到我的域模型对象,这似乎就像它是一场维护噩梦。

1 个答案:

答案 0 :(得分:1)

我建议使用控制(依赖注入)容器的反转并将您的存储库注入您的控制器或任何地方。这样你可以像这样使用它们:

public class HomeController : Controller
{
    private readonly IRepository<Customer> _customerRepository;

    public HomeController(IRepository<Customer> customerRepository)
    {
        _customerRepository = customerRepository;
    }

    public ActionResult Index(int id)
    {
        var customer = _customerRepository.GetById(id)

        return View(customer);
    }
}

这样,如果你需要替换CustomerRepository类,或者需要有多个版本(比如CustomerRepositoryEntityFramework或CustomerRepositoryNHibernate),你可以简单地替换从IRepository继承的新类,你的控制器代码仍然可以继续工作而不做任何改变。

我建议使用Castle Windsor,或Ninject,或许多其他IoC容器之一。

此外,您通常希望将域实体保留为poco(Plain Old CLR Object)。这意味着将您的实体中的所有内容分开,包括验证,业务规则等,并且仅仅具有其属性。这使您可以更轻松地通过管道传递域实体,特别是因为您处于开发的早期阶段。这将为您提供最大的灵活性。

相关问题