2

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

我在其他地方看到了一个示例(不记得链接),其中存储库调用是通过静态属性通过域实体传递的,但我不知道这是否会出现线程问题或是否违反任何域模型原则,尤其是如果我们决定在未来实施 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 服务来处理我的存储库调用,但我想为此我需要一个字典或哈希表来将我的具体存储库映射到我的域模型对象,这似乎会成为维护的噩梦。

4

1 回答 1

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(普通旧 CLR 对象)。这意味着将所有内容从您的实体中分离出来,包括验证、业务规则等,并且只拥有它的属性。这使您可以更轻松地通过管道传递域实体,特别是因为您处于开发的早期阶段。这将在未来为您提供最大的灵活性。

于 2013-11-14T19:08:00.100 回答