在这个域数据建模场景中访问参考数据的正确方法是什么?

发布于 2024-09-08 12:06:41 字数 2339 浏览 3 评论 0原文

初始警告:很长的帖子,我可能已经得到了完全错误的模式:

给定以下类,它是客户聚合的开始:

public class Customer : KeyedObject
{

   public Customer(int customerId)
   {
      _customerRepository.Load(this);
   }

   private ICustomerRepository _customerRepository = IoC.Resolve(..);  
   private ICustomerTypeRepository = _customerTypeRepository = IoC.Resolve(..);

   public virtual string CustomerName {get;set;}
   public virtual int CustomerTypeId [get;set;}

   public virtual string CustomerType
   {
      get
      {
         return _customerTypeRepository.Get(CustomerTypeId);
      }
   }

}

并且 CustomerType 由值对象表示:

public class CustomerType : ValueObject
{
   public virtual int CustomerTypeId {get;set;}
   public virtual string Description {get;set;}
}

这一切都很好并且当我有一个带有 CustomerTypeId 的客户对象时很有用。但是,当我想在 MVC 视图中填充 DropDownList 时,我正在努力解决如何从 ICustomerTypeRepostory 正确获取 CustomerType 值列表的概念。

ICustomerTypeRepository 非常简单:

public interface ICustomerTypeRepository
{
   public CustomerType Get(int customerTypeId);
   public IEnumerable<CustomerType> GetList();
}

基本上,我想要的是能够从我的控制器正确调用 ICustomerTypeRepository,但是我认为最好将 DAL(存储库)分开)来自控制器的层。现在,我是否把事情过于复杂化了?

这就是我的控制器目前的情况:

public class CustomerController : ControllerBase
{ 

    private ICustomerTypeRepository _customerTypeRepository = IoC.Resolve(..);

    public ActionResult Index()
    {
       Customer customer = new Customer(customerId); 
       IEnumerable<CustomerType> customerTypeList = 
          _customerTypeRepository.GetList();

       CustomerFormModel model = new CustomerFormModel(customer);
       model.AddCustomerTypes(customerTypeList );
    }
}

这对我来说似乎是错误的,因为我在控制器和客户中都有存储库。对我来说,CustomerType 应该有一个独立的访问层,这似乎很合乎逻辑。即 CustomerType.GetList()

public class CustomerType : ValueObject
{
   // ... Previous Code

   private static ICustomerTypeRepository _customerTypeRepository = IoC.Resolve(..);

   public static IEnumerable<CustomerType> GetList()
   {
      _customerTypeRepository.GetList();
   }
}

所以,这就是我应该通过 ICustomerTypeRepository 公开 CustomerType 对象的方式> 到 CustomerController

Initial Warning: Long post and I might have got the pattern totally wrong anyway:

Given the following class which is the start of the Customer Aggregate:

public class Customer : KeyedObject
{

   public Customer(int customerId)
   {
      _customerRepository.Load(this);
   }

   private ICustomerRepository _customerRepository = IoC.Resolve(..);  
   private ICustomerTypeRepository = _customerTypeRepository = IoC.Resolve(..);

   public virtual string CustomerName {get;set;}
   public virtual int CustomerTypeId [get;set;}

   public virtual string CustomerType
   {
      get
      {
         return _customerTypeRepository.Get(CustomerTypeId);
      }
   }

}

And CustomerType is represented by a value object:

public class CustomerType : ValueObject
{
   public virtual int CustomerTypeId {get;set;}
   public virtual string Description {get;set;}
}

This is all well and good for when I have a customer object with a CustomerTypeId. However, when I want to populate a DropDownList within my MVC View, I'm struggling with the concept of how to correctly get the CustomerType value list from the ICustomerTypeRepostory.

The ICustomerTypeRepository is very simple:

public interface ICustomerTypeRepository
{
   public CustomerType Get(int customerTypeId);
   public IEnumerable<CustomerType> GetList();
}

Basically, what I want is to be able to call ICustomerTypeRepository correctly from my Controller, however I thought it would be best to separate the DAL (repository) layer from the controller. Now, am I just overcomplicating things?

This is how my controller currently stands:

public class CustomerController : ControllerBase
{ 

    private ICustomerTypeRepository _customerTypeRepository = IoC.Resolve(..);

    public ActionResult Index()
    {
       Customer customer = new Customer(customerId); 
       IEnumerable<CustomerType> customerTypeList = 
          _customerTypeRepository.GetList();

       CustomerFormModel model = new CustomerFormModel(customer);
       model.AddCustomerTypes(customerTypeList );
    }
}

This seems wrong to me as I've got repositories in the Controller and in Customer. It just appears logical to me that there should be a segragated access layer for CustomerType. I.e. CustomerType.GetList():

public class CustomerType : ValueObject
{
   // ... Previous Code

   private static ICustomerTypeRepository _customerTypeRepository = IoC.Resolve(..);

   public static IEnumerable<CustomerType> GetList()
   {
      _customerTypeRepository.GetList();
   }
}

So, which is the way I should be exposing the CustomerType objects through the ICustomerTypeRepository to the CustomerController?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

许久 2024-09-15 12:06:41

我认为这里有一些事情需要考虑。

首先,如果您确实对域建模感兴趣,那么您将需要不惜一切代价尝试使域实体本身免于横切问题,例如验证、IoC 容器和持久性,尽管有 Active Record 模式。

这意味着 Customer 可能不应该有任何对存储库的引用,即使您正在使用接口和服务定位器。它的设计应该从目标客户/用户的角度反映“客户”的属性或构成因素。

除了域建模之外,我有点担心在变量初始值设定项中使用 IoC 服务定位器。您将失去任何捕获异常的机会,并且构造函数抛出的异常非常难以调试(这些初始化程序在第一个非静态构造函数中的任何代码之前运行)。

使用静态网关/服务定位器代替注入依赖项也会使该类几乎无法测试(使用自动化单元测试方法,也就是说,您可以进行集成和手动测试,但测试失败不太可能将您指向损坏的地方)位很容易——与单元测试相反,在单元测试中你确切地知道你正在测试什么,因此知道什么被破坏了)。

应用程序通常会使用存储库来获取实体,而不是通过在构造函数中调用 _customerRepository.Load(this) 来让 Customer 对象向自身填充数据,因此它从完全填充的存储库中返回,包括 CustomerType 属性。在这种情况下,这似乎可能发生在 CustomerController 中。

您表明您希望拥有一个与 CustomerController 所在的层分开的 DAL,并且您实际上已经拥有了它——这就是使用存储库接口的用武之地。您注入一些 该接口的实现(或者在本例中,从 IoC 实现获取实现),但该存储库的实际实现可以存在于单独的层中(甚至可以是另一个程序集)。这是一种称为分离接口的模式。

就我个人而言,我会将 CustomerController 重构为如下所示:

public class CustomerController : ControllerBase
{ 
     private ICustomerTypeRepository _customerTypeRepository;
     private ICustomerRepository _customerRepository;

     public CustomerController(ICustomerRepository customerRepository,
        ICustomerTypeRepository customerTypeRepository)
     {
        _customerRepository = customerRepository;
        _customerTypeRepository = customerTypeRepository;
     }

     public ActionResult Index()
     {
         Customer customer 
             = _customerRepository.GetCustomerWithId(customerId); 
             // from where does customerId come?

         IEnumerable<CustomerType> customerTypeList 
             = _customerTypeRepository.GetTypes();

        . . .

     }
}

…并且我将从 Customer 和任何其他域实体类中获取对存储库的所有引用。

I think there are a few things to consider here.

First of all, if you are really interested in modeling your domain, you'll want to try at all costs to keep domain entities themselves free of cross-cutting concerns, like validation, IoC containers, and persistence, the Active Record pattern notwithstanding.

What this means is that Customer should probably not have any reference to a repository, even if you're using interfaces and a service locator. It should be designed to reflect the attributes of -- or what constitutes -- a "customer" from the perspective of your target client/user.

Domain-modeling aside, I'm a little concerned by the use of your IoC service locator in a variable initializer. You forfeit any opportunity to catch exceptions and exceptions thrown by constructors are notoriously hard to debug (these initializers run before any code in your first non-static constructor).

The use of the static gateway/service locator in lieu of injecting dependencies also renders the class virtually untestable (using automated unit testing methodologies, that is -- you can do integration and manual testing, but test failures are unlikely to point you to the broken bits readily -- as opposed to a unit test, where you know exactly what one thing you are testing and therefore what is broken).

Instead of having the Customer object populate itself with data by calling _customerRepository.Load(this) in the constructor, an application will more typically use the repository to get the entity, so it comes back from the repository wholly populated, including the CustomerType property. In this case, it appears that this might occur in the CustomerController.

You indicate that you would want to have a separate DAL from the layer in which the CustomerController resides, and you effectively have that -- this is where using a repository interface comes in. You inject some implementation of that interface (or in this case, get an implementation from the IoC implementation), but the actual implementation of that repository can exist in a separate layer (it can be another assembly even). This is a pattern known as Separated Interface.

Personally, I would refactor the CustomerController to look like this:

public class CustomerController : ControllerBase
{ 
     private ICustomerTypeRepository _customerTypeRepository;
     private ICustomerRepository _customerRepository;

     public CustomerController(ICustomerRepository customerRepository,
        ICustomerTypeRepository customerTypeRepository)
     {
        _customerRepository = customerRepository;
        _customerTypeRepository = customerTypeRepository;
     }

     public ActionResult Index()
     {
         Customer customer 
             = _customerRepository.GetCustomerWithId(customerId); 
             // from where does customerId come?

         IEnumerable<CustomerType> customerTypeList 
             = _customerTypeRepository.GetTypes();

        . . .

     }
}

…and I'd take any and all references to repositories out of Customer and any other domain entity class.

仅一夜美梦 2024-09-15 12:06:41

如何更改您的客户域模型以包含 CustomerTypes 的属性?这也将不再需要每次调用 CustomerType 时都访问存储库。

public class Customer : KeyedObject
{

   public Customer(int customerId)
   {
      _customerRepository.Load(this);

      ICustomerTypeRepository _customerTypeRepository = IoC.Resolve(..);
      _customerTypes = _customerTypeRepository.GetList();
   }

   private ICustomerRepository _customerRepository = IoC.Resolve(..);  

   public virtual string CustomerName {get;set;}
   public virtual int CustomerTypeId {get;set;}

   public virtual string CustomerType
   {
      get
      {
         return _customerTypes.Find(CustomerTypeId);
      }
   }

   private IEnumerable<CustomerType> _customerTypes;
   public virtual IEnumerable<CustomerType> CustomerTypes
   {
      get
      {
          return _customerTypes
      }
   }
}

How about changing your customer domain model to include a property for CustomerTypes? This would also stop the need to hit the repository each time CustomerType is called.

public class Customer : KeyedObject
{

   public Customer(int customerId)
   {
      _customerRepository.Load(this);

      ICustomerTypeRepository _customerTypeRepository = IoC.Resolve(..);
      _customerTypes = _customerTypeRepository.GetList();
   }

   private ICustomerRepository _customerRepository = IoC.Resolve(..);  

   public virtual string CustomerName {get;set;}
   public virtual int CustomerTypeId {get;set;}

   public virtual string CustomerType
   {
      get
      {
         return _customerTypes.Find(CustomerTypeId);
      }
   }

   private IEnumerable<CustomerType> _customerTypes;
   public virtual IEnumerable<CustomerType> CustomerTypes
   {
      get
      {
          return _customerTypes
      }
   }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文