当注入的对象是有条件的时,C#IoC实例化

当注入的对象是有条件的时,C#IoC实例化,c#,dependency-injection,inversion-of-control,C#,Dependency Injection,Inversion Of Control,我有一个国际奥委会的问题,目前是抽象的。我还没有选择一个IoC框架来开始编码。我仍然在心里计划着我将要用于一个即将到来的项目的方法 我的编码风格通常遵循以下模式: 某种处理器被实例化并传递给业务对象 处理器将依次实例化验证器,以验证传递的业务对象对于给定流程是否有效 如果发现业务对象有效,则将实例化持久化对象。持久化对象负责转换,如加密、缓存,以及在对象图的单个事务中将多个请求分组在一起 然后,业务对象实例化一个数据层,该数据层的任务是将业务对象持久化到数据库中,或者根据具体情况从数据库中提取

我有一个国际奥委会的问题,目前是抽象的。我还没有选择一个IoC框架来开始编码。我仍然在心里计划着我将要用于一个即将到来的项目的方法

我的编码风格通常遵循以下模式:

  • 某种处理器被实例化并传递给业务对象
  • 处理器将依次实例化验证器,以验证传递的业务对象对于给定流程是否有效
  • 如果发现业务对象有效,则将实例化持久化对象。持久化对象负责转换,如加密、缓存,以及在对象图的单个事务中将多个请求分组在一起
  • 然后,业务对象实例化一个数据层,该数据层的任务是将业务对象持久化到数据库中,或者根据具体情况从数据库中提取它(或者数据所在的文本文件或Web服务)
  • 我的理想结构是,处理器知道验证器和Perience对象,但不知道AccessLayer。持久化对象知道访问层,但不能直接实例化或调用进程。这样就有了明确定义的层,可以根据需要进行分离
  • 最后,这个过程对输入或输出是不可知的,并且根据应用程序类型是不可变的。换句话说,我可以使用与桌面应用相同的处理器在web应用中添加业务对象。显然,模型/视图/控制器将根据应用程序类型而改变,但是添加或选择业务对象的规则仍然是通用的
我的问题是这个。例如,我不喜欢我的AccessLayer需要从配置文件中提取连接字符串。也许我希望我的用户能够为设置指定配置文件或Db表。让访问层检查配置文件,看看它是否应该使用配置文件是循环的和愚蠢的。访问层同样不能调用持久性对象来获取设置,也不能查询应用程序框架以查看它是带有web.Config的web应用程序还是带有DbSettings的桌面应用程序

所以我想对我来说最好的办法就是使用某种IoC容器。然后我可以注入任何我需要的设置。这还允许我模拟对象进行测试,这是我当前方法的另一项困难(但并非不可能)的任务。因此,根据我的阅读,我模糊的处理器实现如下所示:

public class VagueProcessor{
   public VagueProcessor(IValidator validator, 
                         IPersistence persistence, 
                         IAccessLayer accessLayer,
                         ISettings settings) { ... }
}
这是我的难题。在我正在规划的应用程序中,业务对象有各种各样的实现,每个实现都有自己的可配置规则。假设一个BO代表加利福尼亚州,另一个BO代表纽约州,两个州都有自己的特殊规则,由各自的管理机构进行验证。因此,验证器可以是CAValidator或NYValidator,这取决于业务对象的状态

好的,在所有这些前导和后记之后,我的问题是:在这个场景中,我会将一个验证器工厂传递给处理器,工厂会根据业务对象的状态实例化适当类型的验证器吗?如果是这样的话,我会用IoC容器注册每种类型,还是只注册工厂


谢谢你对这件事的想法

这是一个模糊的问题,因为你还没有问题,只有想法

根据我对你问题的理解,我想说:

  • IOC解决了创建新对象的问题,而不是确切地决定要创建哪个对象。在大多数IOC容器中,您可以在某种程度上选择所要求的实现,但在您的情况下,逻辑看起来非常以应用程序为中心,没有IOC容器可以帮助您决定使用哪一个。在这种情况下,您确实应该将一个工厂传递给处理器,在那里您可以询问类似于
    factory.CreateValidatorFrom(myBusinessObject)
    的问题

  • 在内部,该工厂仍然可以使用DI来实例化每个组件。例如,如果使用.NET Core DI,可以将
    IServiceProvider
    传递到工厂,并在工厂内部调用
    serviceProvider.GetService()
    。所有DI提供者都会有这样一个对象

  • 因此,从某种意义上说,工厂和DI可以共存,它们各自解决了部分问题。如果您使用的是DI,就不必实例化实际的类。这将使每个验证器更容易拥有自己的依赖项,而您不必关心如何获得它们

    是的,在这种情况下,您需要在DI和工厂中注册每个验证器。在这种情况下,您可以通过反射轻松地遍历所有这些文件,并按名称或接口动态注册它们,如果这让您感到困扰的话

    最后,如果您使用的是.NETCore,我强烈建议您只使用内置的DI。这对于大多数情况来说都很简单,也很好。

    验证是一个复杂的过程,因此验证服务通常不知道它正在验证的对象的详细信息。它只知道它的布尔有效状态以及如何获取通常显示在UI上的验证错误

    作为一个横切关注点,验证规则是从读取它们的服务中抽象出来的。这通常通过接口和/或.NET属性完成

    public class ValidateMe : IValidatableObject
    {
        [Required]
        public bool Enable { get; set; }
    
        [Range(1, 5)]
        public int Prop1 { get; set; }
    
        [Range(1, 5)]
        public int Prop2 { get; set; }
    
        public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
        {
            if (!this.Enable)
            {
                /* Return valid result here.
                * I don't care if Prop1 and Prop2 are out of range
                * if the whole object is not "enabled"
                */
            }
            else
            {
                 /* Check if Prop1 and Prop2 meet their range requirements here
                 * and return accordingly.
                 */ 
            }
        }
    }
    
    这也可以通过使用来完成,以确保在接口和/或属性可用于处理时始终发生

    注意:术语业务对象意味着您希望使用知道如何保存和检索自身状态的对象(内部实现CRUD)构建某种智能对象框架。这种设计不太适合DI。这并不是说你不能同时使用DI和智能对象设计,它只是更难构建,更困难
    if (validationService.IsValid(model));
    {
        // persist
    }
    
    var productDetails = this.queryProcessor.Execute(new GetProductDetailsQuery
    {
         ProductId = id
    });
    
    // This command executes a long and complicated workflow,
    // but this is all that is done inside of the action method
    var command = new AddToCartCommand
    {
        ProductId = model.Id,
        Quantity = model.Qty,
        Selections = model.Selections,
        ShoppingCartId = this.anonymousIdAccessor.AnonymousID
    };
    
    this.addToCartHandler.Handle(command);