有没有比这更好的方法将函数注入Unity中的对象?

时间:2016-08-31 03:23:14

标签: c# dependency-injection unity-container

我是一个Unity noob,我有一个问题。我想在每个IPerson实例中注入一个IAgeCalculator,以便IAgeCalculator实例可供以后创建的任何IPerson使用。

这是我到目前为止所尝试的内容。它有效,但感觉不对。

static void Main(string[] args) 
{
    IUnityContainer container = new UnityContainer();
    container.RegisterType<IAgeCalculator, AgeInYearsCalculator>("Years");
    container.RegisterType<IAgeCalculator, AgeInDaysCalculator>("Days");

    // Using the initializer like this does not feel right, 
    // but I cant think of another way...

    var ageCalculator = container.Resolve<IAgeCalculator>("Days");
    var personInitializer = new InjectionMethod("Initializer", ageCalculator);
    container.RegisterType<IPerson, Person>(personInitializer);

    var person1 = Factory<IPerson>.Create(container);
    person1.Name = "Jacob";
    person1.Gender = "Male";
    person1.Birthday = new DateTime(1995, 4, 1);

    var person2 = Factory<IPerson>.Create(container);
    person2.Name = "Emily";
    person2.Gender = "Female";
    person2.Birthday = new DateTime(1998, 10, 31);
}

以下是我的课程定义:

public interface IAgeCalculator 
{
    string GetAge(IPerson person);
}

internal class AgeInYearsCalculator : IAgeCalculator
{
    public string GetAge(IPerson person) {
        var years = DateTime.Now.Subtract(person.Birthday).TotalDays / 365;
        return years.ToString("0.00") + " years";
    }
}

internal class AgeInDaysCalculator : IAgeCalculator 
{
    public string GetAge(IPerson person) {
        var days = (DateTime.Now.Date - person.Birthday).TotalDays;
        return days.ToString("#,#") + " days";
    }
}

public interface IPerson
{
    string Name { get; set; }
    string Gender { get; set; }
    DateTime Birthday { get; set; }
    string Age { get; }
}

public class Person : IPerson
{
    private IAgeCalculator _ageCalculator;
    public void Initializer(IAgeCalculator ageCalculator) {
        _ageCalculator = ageCalculator;
    }

    public string Name { get; set; }
    public string Gender { get; set; }
    public DateTime Birthday { get; set; }
    public string Age => _ageCalculator.GetAge(this);
}

public class Factory<T> 
{
    public static T Create(IUnityContainer container) {
        return container.Resolve<T>();
    }
}

有更好的方法吗?

2 个答案:

答案 0 :(得分:6)

DI容器的目的是构建应用程序组件的对象图。应用程序组件通常是包含应用程序行为的长寿命对象。组件通常是不可变的,它们实现的服务抽象应始终是不可变的。

DI容器并不意味着构建短期和可变对象,如DTO和实体(如Person对象)。解析或注入可变的,短期的数据对象会导致歧义。例如,如果我们注入一个Person,我们期望得到哪个人?

这通常意味着您不应该在数据对象上使用 Constructor Injection 。在您的情况下,IAgeCalculatorPerson对象的依赖性甚至是不必要的。 Person的消费者很容易依赖IAgeCalculator并为正确的人调用IAgeCalculator.GetAge

如果您想在您的实体中放置特定于域的逻辑, Method Injection 非常适合。这意味着负责调用该域方法的组件负责转发所需的服务。例如:

public void Handle(AddItemToBasketCommand command)
{
    var product = this.productRepository.Get(command.ProductId);
    var person = this.personRepository.Get(this.userContext.UserId);

    if (product.IsAdultProduct && person.GetAge(this.ageCalculator) < 18) {
        throw new ValidationException("You must be 18 to order this item.");
    }

    var basket = this.basketService.GetUserBasket();

    basket.AddItemToBasket(command.ProductId, command.Quantity);
}

请注意,我的Person.GetAge方法会返回年龄。在您的情况下,AgeCalculator会返回此人年龄的字符串表示形式。这不属于实体本身的责任。这是一个介绍责任;它会因为不同的原因而改变,而不是为什么其他Person类会改变。这是在实体内部没有这种逻辑的另一个原因。

另请注意,对您的实体进行IPerson之类的抽象通常没有多大意义。实体是数据容器,而接口则用于抽象行为。 Person中没有您要抽象的行为,因为您不太可能希望将来有多个Person实现。

答案 1 :(得分:1)

使用RewriteRule ^member/(.*)$ /members/$1 [R=301,NC,L] (不是@foreach (var cocktail in Model) { <li> <div class="drink"> <div class="drink-sprite @cocktail.Type"></div> </div> <div class="details"> <div class="name">@cocktail.CocktailName</div> <div class="price">£@cocktail.Price.ToString("0.00")</div> <div class="description">@cocktail.Description</div> </div> </li> } )注册IAgeCalculator并传入从该类型派生的实例。

RegisterInstance<T>

然后在RegisterType<T>课程中,使用static void Main(string[] args) { IUnityContainer container = new UnityContainer(); container.RegisterInstance<IAgeCalculator>("Years", new AgeInYearsCalculator()); container.RegisterInstance<IAgeCalculator>("Days", new AgeInDaysCalculator()); container.RegisterType<IPerson, Person>(); var person1 = Factory<IPerson>.Create(container); person1.Name = "Jacob"; person1.Gender = "Male"; person1.Birthday = new DateTime(1995, 4, 1); var person2 = Factory<IPerson>.Create(container); person2.Name = "Emily"; person2.Gender = "Female"; person2.Birthday = new DateTime(1998, 10, 31); } 属性标记Person

AgeCalculator