具有开放式通用接口的Windsor型工厂

时间:2012-10-14 11:28:07

标签: dependency-injection castle-windsor factory-pattern

在我的应用程序中,我想依赖于类中的多个存储库,而不是每次都需要所有这些存储库。我不是在不必要的地方构建每个实例,而是使用Windsor中的Typed Factory facility

但是,为每个存储库注册工厂有点令人厌烦,我想用开放的通用注册替换它。我想做的事情如下:

container.Register(
    Component.For<IFactory<IRepository<>>>().AsFactory()
);

但是,由于缺少IRepository的类型参数,这是一个语法错误。我可以使用哪种语法来实现这个功能吗?

注意:我知道我可以注册一个无类型的Factory接口,并使用它来创建多个组件。我对此不感兴趣,因为这实际上是依赖于服务定位器 - 如果我没有注册依赖项,那么在代码尝试使用它之前我不会知道它 - 我知道的方法这在构造函数中,即使我还没有创建实例。

下面的完整(简化)示例:

public class TestA { }
public class TestB { }
public interface IRepository<T> { T Create();    }
public class Repository<T> : IRepository<T>
{
    public T Create() { return Activator.CreateInstance<T>(); }
}

public interface IFactory<T>
{
    T Create();
    void Release(T instance);
}

class Program
{
    static void Main(string[] args)
    {
        IWindsorContainer container = new WindsorContainer();
        container.AddFacility<TypedFactoryFacility>();

        container.Register(
            // Individual registrations of repositories here are fine
            Component.For<IRepository<TestA>>().ImplementedBy<Repository<TestA>>(),
            Component.For<IRepository<TestB>>().ImplementedBy<Repository<TestB>>()
        );

        container.Register(
            // Individual registrations of factories - works, but trying to avoid!
            Component.For<IFactory<IRepository<TestA>>>().AsFactory(),
            Component.For<IFactory<IRepository<TestB>>>().AsFactory()
        );

        container.Register(
            // Generic Registration of Factories - syntax errors
            // Component.For<IFactory<IRepository<>>>().AsFactory()
            // Component.For(typeof(IFactory<IRepository<>>)).AsFactory()
        );

        var factoryA = container.Resolve<IFactory<IRepository<TestA>>>();
        var factoryB = container.Resolve<IFactory<IRepository<TestB>>>();

        var repoA = factoryA.Create();
        var repoB = factoryB.Create();

        Console.WriteLine("Everything worked");
    }
}

2 个答案:

答案 0 :(得分:4)

您的工厂界面定义有点过于“开放”。按如下方式更改工厂界面:

public interface IRepositoryFactory<T>
{
    IRepository<T> Create();
    void Release(IRepository<T> instance);
}

然后你可以注册:

container.Register(Component.For(typeof(IRepositoryFactory<>)).AsFactory());

并解决:

var factoryA = container.Resolve<IRepositoryFactory<TestA>>();
var factoryB = container.Resolve<IRepositoryFactory<TestB>>();

答案 1 :(得分:1)

有一种模式可以将存储库组合在一起。它被称为unit of work。因此,不是创建用于创建存储库的工厂,而是创建引用这些存储库的工作单元。例如:

public abstract class UnitOfWork : IDisposable
{
    // here is your factory
    protected abstract IRepository<T> GetRepository<T>();

    public IRepository<User> Users
    {
        get { return this.GetRepository<User>();
    }

    public IRepository<Customer> Customers
    {
        get { return this.GetRepository<Customer>();
    }

    // etc..
}

在您的Composition Root中,您可以定义一个UnitOfWork实现,其中包含对Windsor的引用,并使您能够获得IRepository<T>实现:

internal sealed class WindsorUnitOfWork : UnitOfWork
{
    private WindsorContainer container;

    public WindsorUnitOfWork(WindsorContainer container)
    {
        this.container = container;
    }

    protected override IRepository<T> GetRepository<T>()
    {
        return this.container.Resolve<IRepository<T>>();
    }
}

并注册如下:

container.Register(Component.For<UnitOfWork>()
    .ImplementedBy<WindsorUnitOfWork>()
        .LifeStyle.Transient);

消费者现在可以非常方便地使用存储库:

private readonly UnitOfWork db;

public KarmaService(UnitOfWork db)
{
    this.db = db;
}

public int CalculateKarmaForActiveUsersByName(string name)
{
    var users =
        from user in this.db.Users
        where user.Name == name
        where user.Active
        select user;

    return users.Sum(user => user.Karma);
}