views:

150

answers:

3

Before I begin I will say this: I have to extend DataContext in my repository because I'm calling stored procedures and ExecuteMethodCall is only available internally. Many people don't seem to know this, so please don't say "just don't extend DataContext".

I've just started using Windsor as my IoC container. My controller happily does the following:

public ContractsControlController(IContractsControlRepository contractsControlService)
{
    _contractsControlRepository = contractsControlService;
}

But my repository must have this constructor:

public ContractsControlRepository()
  : base(ConfigurationManager.ConnectionStrings["AccountsConnectionString"].ToString()) { }

But the IoC container is there to let you specify connection strings for your repository in the web.config. What must my constructor in the repository look like in order to do this? If I don't specify the one I've shown then it complains that there are no constructors that take zero arguments.

Cheers

EDIT

In global.asax.cs

ControllerBuilder.Current.SetControllerFactory(new WindsorControllerFactory());

WindsorControllerFactory.cs (in the root)

public class WindsorControllerFactory : DefaultControllerFactory
{
  WindsorContainer container;

  public WindsorControllerFactory()
  {
    container = new WindsorContainer(new XmlInterpreter(new ConfigResource("castle")));

    var controllerTypes = from t in Assembly.GetExecutingAssembly().GetTypes() where typeof(IController).IsAssignableFrom(t) select t;

    foreach (Type t in controllerTypes)
    {
      container.AddComponentLifeStyle(t.FullName, t, LifestyleType.Transient);
    }
  }

  protected IController GetControllerInstance(Type controllerType)
  {
    return (IController)container.Resolve(controllerType);
  }
}

But the container isn't needed if nothing is going in web.config?

+1  A: 

Look at this question to see how to provide parameter constructors.

David Kemp
+2  A: 

David Kemp's suggestion is terse, but not type-safe. A more complex, but type-safe approach, would be using a factory method.

Imagine that the ContractsControlRepository class has been changed to take a connection string in its constructor. When that is the case, you can write something like this:

var connectionString = 
    ConfigurationManager.ConnectionStrings
        ["AccountsConnectionString"].ConnectionString;

var container = new WindsorContainer();
container.AddFacility<FactorySupportFacility>();
container.Register(Component
    .For<IContractsControlRepository>()
    .UsingFactoryMethod(k => new ContractsControlRepository(connectionString)));

Note that it is necessary to add the FactorySupportFacility to the container before the UsingFactoryMethod method will work.

Mark Seemann
But then connection strings would need to be specified in code as opposed to through the web.config? One benefit of the IoC containers is being able to specify connection strings on the fly.
Kezzer
The connection string is pulled from web.config - just a different place in web.config. Does it matter that it is being pulled from the connectionStrings element instead of the castle element? Personally, I don't even use Castle's XML config features unless I have to...
Mark Seemann
Okay, but extending from `DataContext` doesn't seem to allow you to take a connection string as a parameter in the constructor, which I find very strange as there are four constructor definitions, on takes `IDbConnection` and the other takes `string fileOrServerOrConnection`
Kezzer
That is right, but you can always build a SqlConnection (implements IDbConnection) from a connection string.
Mark Seemann
But do I even need to do this? Can I not just have `k => new ContractsControlRepository()` then use the default constructor I've currently got specified that already picks up the accounts connection string?
Kezzer
Oh and also, where do you put the code you've got in your post?
Kezzer
Yes, you can also use the default constructor, but I read your original question as though you were asking about how you could register a component with a parameter. In any case, my answer waas only meant to provide you with some alternatives - I'm not saying that have to do it in this way... In ASP.NET MVC the container should be configured in global.asax - see here for more information: http://stackoverflow.com/questions/1410719/design-where-should-objects-be-registered-when-using-windsor/1410738#1410738
Mark Seemann
Last question I promise. Does this mean I _don't_ require the WindsorControllerFactory, because that's used for web.config settings?
Kezzer
By WindsorControllerFactor I assume you mean the class from MVC contrib? You would still need that (or something similar) to wire up your Controller instances.
Mark Seemann
I've just made a quick edit to my original post to show you what I mean.
Kezzer
You don't need to configure your DI Container from web.config. I mostly do it via code. Even in your WindsorControllerFactory, you register all your Controllers so that it can later resolve the correct Controller, but if none of your Controllers use DI I can understand why it may seem redundant.
Mark Seemann
How's it possible to tell that it's working though?
Kezzer
Uhm, test it? Sorry if this answer sounds a little glib, but I don't know what else to say...
Mark Seemann
A: 

It works like this: you configure your repository as any other component, but you also provide the required connection string as a parameter in your configuration.

<component id="MyDataContext" service="yourservice" type="yourtype" lifestyle="singleton">
  <parameters>
    <connectionString>Data Source=localhost;Initial Catalog=YourCatalog;Integrated Security=SSPI</connectionString>
  </parameters>
</component>

Then, you add a parameter to the constructor of your own DataContext, the name of which is connectionString.

Venemo