1

Given this simplified scenario:

public interface IRepository
{
    string ConnectionString { get; set; }
}

public abstract class Repository : IRepository
{
    public string ConnectionString { get; set; }
}

public class CustomerRepository : Repository, ICustomerRepository
{
}

public class OrderRepository : Repository, IOrderRepository
{
}

and the following Unity registrations:

IUnityContainer container = new UnityContainer();
container.RegisterType<ICustomerRepository, CustomerRepository>();
container.RegisterType<IOrderRepository, OrderRepository>();

What is the recommended way to intercept the creation of any IRepository instance in order to set the value for the ConnectionString property (and by extension any other property).

UPDATE 1: This post is not about design patters or an alternate solution involving re-factoring of the original classes. It's about Unity and the best way to set a property by intercepting the creation of instances implementing a given interface.

JCallico
  • 1,446
  • 18
  • 25
  • If you have different repositories it means that you have different connection strings for each of them it that correct? – Sofian Hnaide Feb 15 '12 at 18:27
  • @SofianHnaide: Yes I could potentially have different connection strings per repository. Now in my specific case there is only connection string but the way to resolve it changes across environments, for example, on Development comes from the web.config, on Production comes from inter-operating with a COM object. – JCallico Feb 15 '12 at 18:55

3 Answers3

0

If you make the connectionString a constructor parameter the configuration looks like this

container.RegisterType<ICustomerRepository, CustomerRepository>(new InjectionConstructor("myConnStr1");

For property injection the configuration is

container.RegisterType<IOrderRepository, OrderRepository>(new InjectionProperty("ConnectionString", "myConnStr2");

[Update] The TecX project on codeplex contains a port of the Castle Windsor Typed Factory Facilities. It allows you to register a factory interface that takes parameters.

public interface ICustomerRepositoryFactory
{
  ICustomerRepository Create(string connectionString);
}

container.RegisterType<ICustomerRepositoryFactory>(new TypedFactory());
container.RegisterType<ICustomerRepository, CustomerRepository>(new InjectionProperty("ConnectionString");

If you resolve the factory interface and call the Create method it will map the method parameter to the according constructor parameter. [Update2]It does not yet support property injection (yet. ask again tomorrow). It also supports property injection but you will have to mark the property for injection when you register your repository types.[/Update2] You will not have to implement the factory. The extension does that for you.

Sebastian Weber
  • 6,766
  • 2
  • 30
  • 49
  • This is a very good workaround but let's assume for a second that I can't modify the original classes and add the parameter to the constructor as you suggest. I'm interested in knowing how using Interception I could "capture" any instance implementing IRepository and set the ConnectionString property. – JCallico Feb 15 '12 at 21:17
  • Also note that the actual value of the connection string is only known at runtime so the solution has to factor that in. – JCallico Feb 15 '12 at 21:19
0

Since you need connection strings coming from differnt providers (as you mentioned in your comment). I would suggest a slight change to the design, you can add a new dependency such as IConnectionStringProvider

public interface IConnectionStringProvider 
{
    string GetConnectionString(); 
}

Then update IRepository

public interface IRepository  
{    
        IConnectionStringProvider ConnectionStringProvider  {get; set;}
        string ConnectionString { get; } // no set is required 
 }

public abstract class Repository : IRepository     
   {       
         IConnectionStringProvider ConnectionStringProvider  {get; set;}
         public string ConnectionString 
           { 
              get 
                 {
                    return ConnectionStringProvider.GetConnectionString(); 
                 }
            }
   }    

Then you can use unity to inject the right connection string provider per your need

container.RegisterType<IConnectionStringProvider , ConnectionStringProvider>();
Sofian Hnaide
  • 2,284
  • 16
  • 13
  • I will use a IConnectionStringProvider injected using the constructor if I was modeling this problem from scratch but let's assume that I can't modify the original classes/interfaces and try to solve the problem from an AOP perspective. How do I set a property after the instance has been created but before it's consumed? – JCallico Feb 15 '12 at 21:26
  • Well then you should keep track (a list) of all repositories created and then make a additional pass to fix the connection strings. – Sofian Hnaide Feb 15 '12 at 22:24
0

Your repositories should not be responsible of creating the connection.

  1. You are breaking Single Responsibility Principle
  2. You got duplicated code = harder to maintain the code in the future.
  3. What do you do if you want to share a connection between your repositories?

Solution:

Create an IConnectionFactory (which implementation you can register as an instance/singleton) and use that as an dependency in your repositories.

Update 1

You can use InjectionProperty. Lookup the connection string on the line before you configure it.

Update 2:

Use factories to configure the repositories: http://www.pnpguidance.net/post/RegisteringFactoryMethodCreateObjectsUnityStaticFactoryExtension.aspx

Something like:

container.Configure<IStaticFactoryConfiguration>()
    .RegisterFactory<ICustomerRepository>
    (new FactoryDelegate(c => 
       { 
          var repos = new CustomerRepository();
          repos.ConnectionString = yourComPlus.ConnectionString;
          return repos;
       }
    ));
jgauffin
  • 99,844
  • 45
  • 235
  • 372
  • I agree with you that this is not the best design but let's assume that this is what you have and that can't be changed. The question is more about Unity itself that about design patterns. – JCallico Feb 15 '12 at 21:28
  • Then you need to use `InjectionProperty` – jgauffin Feb 15 '12 at 21:31
  • Do you mind adding some code explaining how the problem can be solved by using InjectionProperty? Remember the original classes/interfaces can't be modified. – JCallico Feb 15 '12 at 21:34
  • InjectionProperty may work if all I needed was a static string but as explained before I need to execute some code at runtime in order to find the actual value, for example the connection string sometimes is resolved by using a COM object. – JCallico Feb 15 '12 at 21:56
  • @jgauffin As far as I know the `StaticFactoryExtension` is deprecated in favor of the `InjectionFactory`. `[Obsolete("Use RegisterType(new InjectionFactory(...)) instead of the extension's methods.")]` – Sebastian Weber Feb 16 '12 at 10:37
  • @SebastianWeber: The docs (http://msdn.microsoft.com/en-us/library/microsoft.practices.unity.staticfactory.staticfactoryextension.aspx) doesn't mention that. – jgauffin Feb 16 '12 at 10:39
  • @jgauffin See the section [Changes in Unity](http://msdn.microsoft.com/en-us/library/ff660878%28PandP.20%29.aspx) – Sebastian Weber Feb 16 '12 at 11:17
  • @jgauffin regarding Update 2: This will work but the downside is that I'm going to have to register a factory for every single repository. Also these repositories have other dependencies already been injected so I rather have them created by having the container resolve them instead of doing a new CustomerRepository(). When I originally posted the question I was looking for a solution involving Interception. – JCallico Feb 16 '12 at 15:40
  • also @SebastianWeber is right, the recommended way of creating factories since version 2 is using InjectionFactory: container.RegisterType(new InjectionFactory(c => { var repos = new CustomerRepository(); repos.ConnectionString = yourComPlus.ConnectionString; return repos; })); – JCallico Feb 16 '12 at 15:42