Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/322.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何避免在单向api设计中传递DI容器?_C#_Dependency Injection_Dependencies_Unity Container - Fatal编程技术网

C# 如何避免在单向api设计中传递DI容器?

C# 如何避免在单向api设计中传递DI容器?,c#,dependency-injection,dependencies,unity-container,C#,Dependency Injection,Dependencies,Unity Container,我有一个使用活动记录设计的业务实体的业务层和一个单向api表面。我有两个截然不同的问题: 在不使代码复杂化的情况下,应该如何处理运行时值,例如将id值从DAL传递给构造的对象?这是通过参数覆盖来实现的吗? 如果我没有向下传递容器并使其更像一个反模式/服务定位器,那么如何创建其他业务实体并传递依赖关系 产品是包装容器的根,充当我们的应用程序外观,以及BAL其余部分的入口点。我试图解决的问题在Product.FindCustomer和Customer.FindDocument中 我的目标之一是对域进

我有一个使用活动记录设计的业务实体的业务层和一个单向api表面。我有两个截然不同的问题:

在不使代码复杂化的情况下,应该如何处理运行时值,例如将id值从DAL传递给构造的对象?这是通过参数覆盖来实现的吗? 如果我没有向下传递容器并使其更像一个反模式/服务定位器,那么如何创建其他业务实体并传递依赖关系 产品是包装容器的根,充当我们的应用程序外观,以及BAL其余部分的入口点。我试图解决的问题在Product.FindCustomer和Customer.FindDocument中

我的目标之一是对域进行建模,并提供一个非常容易理解的API。目前,我们的BAL使用服务定位器,但我正在尝试用合适的IoC/DI和容器替换它


API越深,需要的依赖性就越多,所有更高级别的构造函数都可能相当长,并且可能不再具有内聚性。

虽然使用半度量可以将DI压缩到大多数应用程序设计中,但不幸的是,并非所有的应用程序设计都对DI特别友好。当涉及到API设计时,创建智能实体似乎很神奇,但事实是,在它们的核心,它们违反了SRP加载和保存是独立的责任,不管您如何划分它

您基本上有4种选择:

找到一种更有利于DI的设计,并将其对象模型用于API 找到一种更有利于DI的设计,并为您的API创建一个facade对象模型 使用属性注入加载依赖项,并让最终用户控制构造函数 使用服务定位器 在尝试与DI结合使用时,我遇到了类似的问题,经过多次尝试,我最终决定,需要去寻找更好的设计方法的是CSLA

有一段时间,我尝试使用选项3。在这种情况下,您可以围绕DI容器创建一个facade包装器,并且只通过静态访问器公开其构建方法。这将防止将容器用作服务定位器

[Dependency]
public ISomeDependency SomeDepenency { get; set; }

public Customer()
{
    Ioc.BuildUp(this);
}
一些DI容器可以使用fluent配置而不是属性注入属性,因此您的业务模型不需要引用容器,但这会使DI配置非常复杂。另一个选择是让构建自己的属性

备选方案1和2类似。基本上,您将每个职责都划分到自己的类中,并将实体分离到哑数据容器中。一种有效的方法是使用

不幸的是,这方面的主要问题是对象生命周期的控制不再取决于DI容器,因此像DbContext这样的依赖项需要特殊处理

另一种变体是将每个实体变成一个简单的对象,该对象使用其他松散耦合的API对象在内部构建自己的DI容器。对于难以与DI一起使用的遗留框架(如web表单),这是推荐的方法

最后,创建一个静态服务定位器,所有API对象都使用它来解析它们的依赖关系。虽然这最能实现目标,但它应该被视为最后的手段。最大的问题是,您无法快速、轻松地理解类所需的依赖关系。因此,您要么被迫创建和更新文档,指明对最终用户的依赖关系,要么最终用户必须通过挖掘源代码才能找到。使用服务定位器是否可以接受取决于您的目标受众,以及您希望他们能够自定义默认之外的依赖关系的频率。如果自定义依赖项是千载难逢的事情,那么它可能会起作用,但是如果25%的用户需要添加自定义依赖项,那么服务定位器可能不是正确的方法

底线是,如果可维护性是您的主要目标,那么选项1显然是赢家。但是,如果您与这个特定的API设计结为夫妻,那么您将需要选择其他选项之一,并接受支持这样一个API所涉及的额外维护

参考资料:

.
public abstract class ActiveRecord<TEntity, TKey>
{
    protected ActiveRecord(IDataContext context)
    {
    }

    public virtual void Load() ...
    public virtual void Save() ...
    public virtual void Delete() ...
}

public abstract class BusinessEntity<TEntity, TKey> : ActiveRecord<TEntity, TKey>
{
    protected BusinessEntity(IDataContext context) : base(context)
    {
    }

    protected BusinessEntity(IDataContext context, TKey id) : this(context)
    {
    }

    ...
}
var customer = product.FindCustomer("123");
var account  = customer.FindAccount("321");
var document = account.FindDocument("some_code");
var file     = document.GetFile();
[Dependency]
public ISomeDependency SomeDepenency { get; set; }

public Customer()
{
    Ioc.BuildUp(this);
}
public class FindCustomer : IDataQuery<Customer>
{
    public string CustomerNumber { get; set; }
}

public class FindCustomerHandler : IQueryHandler<FindCustomer, Customer>
{
    private readonly DbContext context;

    public FindCustomerHandler(DbContext context)
    {
        if (context == null)
            throw new ArgumentNullException("context");
        this.context = context;
    }

    public Customer Handle(GetCustomer query)
    {
        return (from customer in context.Customers
                where customer.CustomerNumber == query.CustomerNumber
                select new Customer
                {
                    Id = customer.Id,
                    Name = customer.Name,
                    Addresses = customer.Addresses.Select(a =>
                        new Address
                        {
                            Id = a.Id,
                            Line1 = a.Line1,
                            Line2 = a.Line2,
                            Line3 = a.Line3
                        })
                        .OrderBy(x => x.Id)
                }).FirstOrDefault(); 
    }
}
var customer = new CustomerBuilder().Build(); // defaults

var customer = new CustomerBuilder(c => 
    c.WithSomeDependency(new SomeDependency()).Build(); // overridden dependency