Lazy resolve

30 views
Skip to first unread message

smolesen

unread,
Oct 6, 2011, 7:54:43 AM10/6/11
to Castle Project Users
Hi Given the following code:

public interface IMyContext
{
string subtype { get; set; }
}

public class MyContext : IMyContext
{
public string subtype { get; set; }
}

public interface IMyExporter
{
string Export();
}

public class MyExporterXML : IMyExporter
{
public string Export()
{
return "";
}
}

public class MyExporterJson : IMyExporter
{
public string Export()
{
return "";
}
}

public class MyExporterFactory
{
private IMyContext context;
public MyExporterFactory(IMyContext context)
{
this.context = context;
}

public IMyExporter Create()
{
switch (context.subtype)
{
case "JSON" :
return new MyExporterJson();
default:
return new MyExporterXML();
}
}
}

public class MyService
{
private IMyContext context;
private IMyExporter exporter;
public MyService(IMyContext context, IMyExporter exporter)
{
this.context = context;
this.exporter = exporter;
}

public string Extractdata()
{
return exporter.Export();
}
}

[TestClass]
public class UnitTest2
{
[TestMethod]
public void TestMethod1()
{
var container = new WindsorContainer();

container.Register(Component.For<IMyContext>().ImplementedBy<MyContext>());
container.Register(Component.For<MyExporterFactory>());
container.Register(Component.For<MyService>());

container.Register(Component.For<IMyExporter>().UsingFactoryMethod(kernel
=> kernel.Resolve<MyExporterFactory>().Create()));
var context = container.Resolve<IMyContext>();
var service = container.Resolve<MyService>();

context.subtype = "JSON";

service.Extractdata();

}
}
Is there a way to have the injected exporter in the MyService resolved
at the time where it's actually used ?? Ie. when running the above
code, the exporter resolved is the MyExporterXML, but I really want's
it to be the MyExporterJson because of the context.subtype = "JSON"
setting. However the exporter is resolved before the subtype is set...

I know Castle::Windsor has something called delegate-based factories,
but I simply can't figure out how to use it....

Any help would be greatly appreciated, TIA

Matthew Brubaker

unread,
Oct 6, 2011, 11:18:07 AM10/6/11
to castle-pro...@googlegroups.com
I had a similar need to generate objects on the fly. In my case, I wanted to automatically create file watchers at run time for each file we wanted to watch for. Try this and see if it works for you

Register the following two components in the container
container.Register(Component.For<IExporterFactory>().AsFactory(x => x.SelectedWith(new ExporterSelector())));
container.Register(Component.For<IMyExporter>().ImplementedBy<MyExporterJson>().Named("exporter.json").LifeStyle.Transient);
container.Register(Component.For<IMyExporter>().ImplementedBy<MyExporterXML>().Named("exporter.xml").LifeStyle.Transient);

Create an interface called IExporterFactory
public interface IExporterFactory
{
    IMyExporter Create(string exporterType);
}

Then create a class that inherits from DefaultTypedFactoryComponentSelector
    public class ExporterSelector: DefaultTypedFactoryComponentSelector
    {
        protected override string GetComponentName(MethodInfo method, object[] arguments)
        {
            if (method.Name == "Create" && arguments.Length == 1)
            {
                var exporterType = Convert.ToString(arguments[0]).ToLower();

                return "exporter.{0}".FormatWith(exporterType);
            }

            return base.GetComponentName(method, arguments);
        }
    }

You can then get the exporters by calling 
container.resolve<IExporterFactory>().Create("xml")
or
container.resolve<IExporterFactory>().Create("json")

I believe that will do what you need it to do, but i don't have a compiler available to me at the moment so I can't verify. Let me know if you need any more help.


smolesen

unread,
Oct 7, 2011, 2:42:18 AM10/7/11
to Castle Project Users
Hi

Just saw a session fro NDC2011 with Krzysztof Kozmic (http://
ndc2011.macsimum.no/mp4/Day3%20Friday/Track5%201340-1440.mp4) where
he's using Func<> to Lazy resolve the dependency, so what I'd really
would like, is to do something like:

public class MyService
{
private IMyContext context;
private readonly Func<IMyExporter> exporterfactory;
public MyService(IMyContext context, Func<IMyExporter>
exporterfactory)
{
this.context = context;
this.exporterfactory = exporterfactory;
}

public string Extractdata()
{
var exporter=exporterfactory();
return exporter.Export();
}
}

However I would like to control what kind of exporter the
exporterfactory returns.....

Søren

Patrick Steele

unread,
Oct 7, 2011, 8:29:16 AM10/7/11
to castle-pro...@googlegroups.com
I just posted an answer to your SO question that utilizes the
TypedFactoryFacility and a custom TypedComponentSelector:

http://stackoverflow.com/questions/7651390/delayed-lazy-resolve

---
Patrick Steele
http://weblogs.asp.net/psteele

> --
> You received this message because you are subscribed to the Google Groups "Castle Project Users" group.
> To post to this group, send email to castle-pro...@googlegroups.com.
> To unsubscribe from this group, send email to castle-project-u...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/castle-project-users?hl=en.
>
>

smolesen

unread,
Oct 12, 2011, 3:05:16 AM10/12/11
to Castle Project Users
I ended up using the following:

using System;
using Castle.MicroKernel.Registration;
using Castle.Windsor;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace GPI.REST.UnitTest
{
public interface IMyContext
{
string subtype { get; set; }
}

public class MyContext : IMyContext
{
public string subtype { get; set; }
}

public interface IMyExporter
{
string Export();
}

public class MyExporterXML : IMyExporter
{
public string Export()
{
return "XML Data";
}
}

public class MyExporterJson : IMyExporter
{
public string Export()
{
return "JSON Data";
}
}

public class MyExporterFactory
{
private IMyContext context;
public MyExporterFactory(IMyContext context)
{
this.context = context;
}

public Func<IMyExporter> Create()
{
return () =>
{
if (context.subtype == "XML")
return new MyExporterXML();
return new MyExporterJson();
};

}
}

public class MyService
{
private readonly Func<IMyExporter> exporter;
public MyService(Func<IMyExporter> exporter)
{
this.exporter = exporter;
}
public string Extractdata()
{
return exporter().Export();
}
}


[TestClass]
public class UnitTest2
{
public Func<IMyExporter> CreateExporter()
{
Func<IMyExporter> smo = null;
return smo;
}

[TestMethod]
public void TestMethod1()
{
// Arrange
var container = new WindsorContainer();

container.Register(Component.For<IMyContext>().ImplementedBy<MyContext>());
container.Register(Component.For<MyService>());
container.Register(Component.For<MyExporterFactory>());

container.Register(Component.For<Func<IMyExporter>>().UsingFactoryMethod(kernel
=> kernel.Resolve<MyExporterFactory>().Create()));
var context = container.Resolve<IMyContext>();
var service = container.Resolve<MyService>();

// Act
context.subtype = "JSON";
var resultJson = service.Extractdata();

context.subtype = "XML";
var resultXml = service.Extractdata();

// Assert
Assert.AreEqual(resultJson, "JSON Data");
Assert.AreEqual(resultXml, "XML Data");
}
}
}

Patrick Steele

unread,
Oct 12, 2011, 8:10:20 AM10/12/11
to castle-pro...@googlegroups.com
That would work too. However, while I may be biased, I think the
solution I posted on SO
(http://stackoverflow.com/questions/7651390/delayed-lazy-resolve) has
the benefit of less maintenance. If you add more exporters, just
register them with a new name and you're all set. No need to maintain
your own factory -- let Windsor do it. :)

Reply all
Reply to author
Forward
0 new messages