【问题标题】:Mapping database entities to domain entities via repositories - static property or separate service?通过存储库将数据库实体映射到域实体 - 静态属性还是单独的服务?
【发布时间】:2013-11-14 16:56:37
【问题描述】:

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

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

【问题讨论】:

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


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

    【讨论】:

    • 因此,如果验证和业务规则不是域实体的一部分,并且我们将存储库直接包含在控制器中,那么在这种情况下如何应用业务逻辑?我正在使用 ViewModels 进行基本的表单字段验证,但显然我们不想向 ViewModels 添加更复杂的业务逻辑。
    • 通过将存储库直接包含在控制器中,您将自己限制在拥有业务逻辑的位置。请记住,这里的整个想法是关注点分离。您的 ViewModel 不应该有任何业务逻辑,您只是想使用它们在视图和控制器之间传递数据。您是否考虑过为您的业务层实现服务模式?并将您的存储库注入您的服务?反过来,将您的服务注入您的控制器?
    • 看看这个项目:ddmvc4.codeplex.com 或这个codeproject.com/Articles/543810/… 他们都有很好的设计模式,可能会有所帮助。
    • 另外,您可能需要研究 FluentValidation,因为它允许您完全分离验证并进行多层验证。
    • 这些链接正是我想要的。服务模式似乎是最大的缺失环节,也是我难以掌握的地方。我已经在使用 FluentValidation,但我认为在审查这两个项目后我需要重新审视我的实施。谢谢!
    猜你喜欢
    • 2023-04-01
    • 1970-01-01
    • 2012-06-30
    • 1970-01-01
    • 2010-10-14
    • 1970-01-01
    • 2018-08-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多