3

So, here's a problem that I'm thinking about and could use some help with. Firstly, I'm using Unity for IOC, and want to use it to resolve and produce instances of a wrapper for my SQL calls. To do this, I have a SqlWrapper that implements ISqlWrapper. It has two contructors. Here's the relevent code snippet.

public interface ISqlWrapper : IDisposable
{
    string CommandText { get; set; }
    void Execute();
}

public class SqlWrapper : ISqlWrapper, IDisposable
{
    public SqlWrapper(string connectionString);
    public SqlWrapper(IDbConnection sqlConnection);

    string CommandText { get; set; }
    void Execute();
}

Obviously the about isn't the complete code, it's to illustrate the relevent part of the implementation.

For my application, I'm only using the contructor with the connectionString. So, in my IoC container, I've registered the following...

_unityContainerontainer.RegisterType<ISqlWrapper, SqlWrapper>( new InjectionConstructor(typeof(string)));

Now, this is where things get interesting. What I would like to do is implement a method that will allow me to easily resolve an instance of the ISqlWrapper. I've boilder the code down to this method.

ISqlWrapper CreateSqlWrapper(string connectionString)
{
    ParameterOverrides parameterOverride = new ParameterOverrides();
    parameterOverride.Add("connectionString", connectionString);
    return _iocContainer.Resolve<ISqlWrapper>(parameterOverride);
}

However, currently I am putting a copy of this method into each class that I'm using to connect to my database.

public class ExampleClass1 : IExampleClass1
{
    private readonly IIocContainer _iocContainer;

    ISqlWrapper CreateSqlWrapper(string connectionString)
    {
        ParameterOverrides parameterOverride = new ParameterOverrides();
        parameterOverride.Add("connectionString", connectionString);
        return _iocContainer.Resolve<ISqlWrapper>(parameterOverride);
    }

    public ExampleClass1(IIocContainer iocContainer)
    {
        _iocContainer = iocContainer;
    }

    public void DoStuff(string connectionString)
    {
        using( ISqlWrapper sqlWrapper = CreateSqlWrapper(connectionString))
        {
            CommandText = "Select * from Table*";
            Execute;
        }
    }
}

So, the problem is that I need to have an instance of CreateSqlWrapper in every class to make implementing an instance of ISqlWrapper easier. I know that I could use inheritance to implement this method into a parent class. However, I'm trying to see if there is a better solution to this problem.

Does anyone have any ideas of how I can define the CreateSqlWrapper method without needing to copy and paste it into every class?

Colin Dawson
  • 435
  • 2
  • 12
  • to fulfill your dry tag, I'd start having your CreateSqlWrapper as a method of a specific class, without repeating the implementation of the method in every class with the same need. You would need to repeat the same method call (`SomeWrapperCreator.CreateSqlWrapper(connString)`). Not the complete solution to what you are asking, but it would be an improvement anyway. – Gian Paolo Jan 05 '16 at 21:11
  • 1
    Actually, by spoiling your class constructor with a container, you totally ruin the idea of IoC. Definitely read Mark Seeman's book on IoC or any other good reference. In short - IoC should only help you to compose dependencies, it should not **introduce** dependencies. – Wiktor Zychla Jan 05 '16 at 21:13
  • As for the question, code repetition can be easily avoided by moving the common code to another class, in your case call it SqlWrapperFactory and you are fine. – Wiktor Zychla Jan 05 '16 at 21:26

2 Answers2

0

See this answer: How to create objects using a static factory method?

You can use an InjectionFactory and pass a delegate during configuration.

Community
  • 1
  • 1
Matthias
  • 12,053
  • 4
  • 49
  • 91
  • If that is going to be the entirety of your answer you really should have not answered and marked the question as duplicate. – Scott Chamberlain Jan 05 '16 at 21:13
  • Well, I can't be entirely sure that I understood the question correctly. So I think it would be unfair to mark a question as a duplicate when in the end it's a slightly different one (personally, I think too many questions on stackoverflow are closed, although keeping them open would benefit more readers). But feel free to vote to close, of course :-) – Matthias Jan 05 '16 at 21:15
  • I have a gold badge in C#, my vote for duplicate counts as 5 votes if the question is tagged C#. I don't feel confidant enough to do a close vote on it. – Scott Chamberlain Jan 05 '16 at 21:18
  • I've had a look at the link Matthias posted. It's not the same although it is a similar problem. The difference is that in my example, I want to pass in the connectionString at the time of creating the instance of the object. This is because the connectionString can be different depending on what I'm doing at the time. Also there is a problem that the method CreateAuthoringRepository from the other link would have to live in a class - but every class other than the IoC container is resolved. – Colin Dawson Jan 05 '16 at 23:26
0

I've read what everyone has said and decided that Gian Paolo has posted the best suggestion. Bascially I needed to implement a factory. So here's what I've done.

Gian Paolo if you post an answer, I'll accept it based on your comment.

Created an interface and class for the factory method.

public interface ISqlWrapperFactory
{
    ISqlWrapper CreateInstance(string connectionString);
}

public class SqlWrapperFactory : ISqlWrapperFactory { private readonly IIocContainer _iocContainer;

    public SqlWrapperFactory(IIocContainer iocContainer)
    {
        _iocContainer = iocContainer;
    }

    public ISqlWrapper CreateInstance(string connectionString)
    {
        ParameterOverrides parameterOverride = new ParameterOverrides();
        parameterOverride.Add("connectionString", connectionString);
        return _iocContainer.Resolve<ISqlWrapper>(parameterOverride);
    }
}

Registered it with the IoC container.

_unityContainerontainer.RegisterType<ISqlWrapperFactory, SqlWrapperFactory>();

Then modified the class as follows.

public class ExampleClass1 : IExampleClass1
{
    private readonly IIocContainer _iocContainer;
    private readonly ISqlWrapperFactory _sqlWrapperFactory;


    public ExampleClass1(IIocContainer iocContainer, ISqlWrapperFactory sqlWrapperFactory)
    {
        _iocContainer = iocContainer;
        _sqlWrapperFactory = sqlWrapperFactory;
    }

    public void DoStuff(string connectionString)
    {
        using( ISqlWrapper sqlWrapper = _sqlWrapperFactory.CreateInstance(connectionString))
        {
            CommandText = "Select * from Table*";
            Execute();
        }
    }
}

This means that the factory method only has to be declared one - yay for dry :) Also the code that which implements the ISqlWrapper is still a one liner. Which means less chance of someone messing around with it.

Thought I'd post this in full so that others can see the solution to this problem. Bascially, the passing of the parameter to the contructor is the thing that makes it different from other solutions.

Colin Dawson
  • 435
  • 2
  • 12
  • A class that directly depends on a container is still bad. – Wiktor Zychla Jan 06 '16 at 00:41
  • You would think, however I disagree. Using the container to resolve the concrete classes means that you can easily mock every concrete class. In this case, I can unit test the Factory class by mocking the ioc container, and have it fake a resolve of the ISqlWrapper to a mocked object instead of the real one. – Colin Dawson Jan 06 '16 at 09:28
  • All you wrote is true but this is not what I say. I say you don't depend on a container in a class. Read more on Local factory/Dependency resolver patterns and how you should correctly involve ioc in your architecture. – Wiktor Zychla Jan 06 '16 at 13:24
  • I'm wondering if you are talking about something that I have already implemented. My IocContainer is created once and used throughout the application. I tend to pass it around rather than creating a new one each time - but there's nothing from stopping me from doing that. I've also got it implemented so that if it tried to resolve and IIocContainer instance, it'll return itself rather than a new one. Although. To be clear, it's not implemented as a Singeton. – Colin Dawson Jan 12 '16 at 10:56
  • Not exactly. Check out this fiddle I created for you: https://dotnetfiddle.net/KRUYPL Pay attention to where the container is configured and how it is used and how the both service and the client are unaware of the container. – Wiktor Zychla Jan 12 '16 at 11:18