开发者

How to reuse a transient dependency in same context with Castle Windsor DI container

If I have the following setup, how can I configure my container to use the same database, when objects are created in the same context

public class Database { }
public interface IRepository { Database Database { get; } }
public interface IFooRepository : IRepository { }
public interface IBarRepository : IRepository { }

public class FooRepository : IFooRepository
{
    public Database Database { get; private set; }
    public FooRepository(Database database) { this.Database = database; }
}

public class BarRepository : IBarRepository
{
    public Database Database { get; private set; }
    public BarRepository(Database database) { this.Database = database; }
}

public class Consumer
{
    public IFooRepository fooRepository { get; private set; }
    public IBarRepository barRepository { get; private set; }
    public Consumer(IFooRepository fooRepository, IBarRepository barRepository)
    {
        this.fooRepository = fooRepository;
        this.barRepository = barRepository;
    }
}

[TestClass]
public class ConfigurationTest
{
    private IWindsorContainer container;

    [TestMethod]
    public void SameDatabaseIsUsed()
    {
        Consumer consumer = container.Resolve<Consumer>();
        IFooRepository fooRepository = consumer.fooRepository;
        IBarRepository barRepository = consumer.barRepository;
        Assert.AreEqual(fooRepository.Database, barRepository.Database); //FAILS
    }

    [TestMethod]
    public void DifferentDatabaseIsUsed()
    {
        Consumer consumer = container.Resolve<Consumer>();
        IFooRepository fooRepository = consumer.fooRepository;
        Consumer consumer2 = container.Resolve<Consumer>();
        IBarRepository barRepository = consumer2.barRepository;
        Assert.AreNotEqual(fooRepository.Database, barRepository.Database); //PASSES
    }

    [TestInitialize]
    public void SetUp()
    {
        container = new WindsorContainer();
        container.Register(
            Component.For<Database>().ImplementedBy<Database>().LifeStyle.Transient,
            AllTypes.FromThisAssembly()
                    .BasedOn<IRepository>().WithService.FromInterface()
                    .Configure(c => c.LifeStyle.Transient),
            Component.For<Consumer>().ImplementedBy<Consumer>().LifeStyle.Transient
                    );

    }
}

EDIT: I have tried to use a custom lifest开发者_开发知识库yle, but i cannot figure out what I can use to detect that i have switched context

public class DatabaseLifestyleManager : AbstractLifestyleManager
{
    private CreationContext context;
    private Database database;

    private Database Database
    {
        get
        {
            if (database == null) database = new Database();
            return database;
        }
        set 
        {
            database = null;
        }
    }

    public override object Resolve(CreationContext context)
    {
        if (this.context!=null && this.context.??? == context.???)
            return Database;
        else
        {
            this.context = context;
            Database = null;
            return Database;
        }

    }

    public override void Dispose()
    {
        database = null;
        context = null;
    }
}
......
Component.For<Database>().ImplementedBy<Database>().LifeStyle.Custom(typeof(DatabaseLifestyleManager)


You always get a new instance when requesting a transient component, if it's not what you want don't use the transient lifestyle :-)

Why would you register a transient component, but attempt to resolve the same object depending on some kind of "context"? Most likely the lifestyle is wrong for the situation, and you will be in trouble trying to coerce it into something it's not.

What you want is something like a contextual lifestyle, mentioned in this article. The below two gists have an implementation for this:

  • http://gist.github.com/400979
  • http://gist.github.com/400980

This will allow you do this:

Register(Component.For<Database>().LifeStyle.Scoped())

[TestMethod]
public void SameDatabaseIsUsed()
{
    using (container.BeginScope())
    {
        Consumer consumer = container.Resolve<Consumer>();
        IFooRepository fooRepository = consumer.fooRepository;
        IBarRepository barRepository = consumer.barRepository;
        Assert.AreEqual(fooRepository.Database, barRepository.Database); // YAY!
    }
}

Hope this helps!


A contextual lifestyle is included in the Castle.Windsor.Lifestyles contrib project.


I came up with this solution myself by implementing IDisposable, so that I can use a kind of sessionscope for the Database

Would this be a valid way to handle this situation?

All test passes, but there is some added functionality, that must be implemented in all my future consumers of the repositories:

public class Database { }
public interface IRepository : IDisposable { Database Database { get; } }
public interface IFooRepository : IRepository { }
public interface IBarRepository : IRepository { }

public abstract class BaseRepository : IDisposable
{
    public BaseRepository(Database database) { this.Database = database; }
    public Database Database { get; private set; }
    public void Dispose() { Database = null; }
}

public class FooRepository : BaseRepository, IFooRepository
{
    public FooRepository(Database database) : base(database) { }
}

public class BarRepository : BaseRepository, IBarRepository
{
    public BarRepository(Database database) : base(database) { }
}

public abstract class BaseConsumer : IDisposable
{
    public abstract void Dispose();
}

public class Consumer : BaseConsumer
{
    public IFooRepository fooRepository { get; private set; }
    public IBarRepository barRepository { get; private set; }

    public Consumer(IFooRepository fooRepository, IBarRepository barRepository)
    {
        this.fooRepository = fooRepository;
        this.barRepository = barRepository;
    }

    public override void Dispose()
    {
        this.fooRepository.Dispose();
        this.barRepository.Dispose();
    }
}

[TestClass]
public class ConfigurationTest
{
    private IWindsorContainer container;

    [TestMethod]
    public void SameDatabaseIsUsed()
    {
        IFooRepository fooRepository;
        IBarRepository barRepository;
        using (Consumer consumer = container.Resolve<Consumer>())
        {
            fooRepository = consumer.fooRepository;
            barRepository = consumer.barRepository;
            Assert.AreEqual(fooRepository.Database, barRepository.Database); //FAILS
        }
        Assert.IsNull(fooRepository.Database);
        Assert.IsNull(barRepository.Database);

    }

    [TestMethod]
    public void DifferentDatabaseIsUsed()
    {
        IFooRepository fooRepository;
        IBarRepository barRepository;

        using (Consumer consumer = container.Resolve<Consumer>())
            fooRepository = consumer.fooRepository;

        Assert.IsNull(fooRepository.Database);

        using (Consumer consumer2 = container.Resolve<Consumer>())
            barRepository = consumer2.barRepository;

        Assert.IsNull(barRepository.Database);
    }

    [TestInitialize]
    public void SetUp()
    {
        container = new WindsorContainer().Register(
            Component.For<Database>().ImplementedBy<Database>().LifeStyle.Singleton,
            AllTypes.FromThisAssembly()
                    .BasedOn<IRepository>().WithService.FromInterface()
                    .Configure(c => c.LifeStyle.Transient),
            Component.For<Consumer>().ImplementedBy<Consumer>().LifeStyle.Transient
                    );
    }
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜