Shane van Wyk
Shane van Wyk

Reputation: 1900

Inject different implementations into same interface then pick up right implementation in right project / assembly

We have 2 OpenLdap servers. One is the latest out of the box version. The other one is an older heavily customized version.

Both implementations really follows a similar logic. Take the connection for example.

Here is the interface

namespace Infrastructure.Interfaces
{
    public interface ISessionService
    {
        LdapConnection GetConnection();       
    }
}

Each of the implementations will make use of this interface to get a connection.

New Server

namespace Infrastructure.NewLdap.Service
{
    public class SessionService : ISessionService
    {
        LdapConnection GetConnection()
        {
        .....
        }
    }
}

Old Server

namespace Infrastructure.OldLdap.Service
{
    public class SessionService : ISessionService
    {
        LdapConnection GetConnection()
        {
        .....
        }
    }
}

Each of the implementations are in different Projects. Each project will have a different app.config with the right credentials etc. Along with this there is a IConfigService which will have all the credentials etc loaded into it from the app.config.

namespace Infrastructure.Interfaces
{
    public interface IConfigService
    {
        string ServerAddress { get; }   
        ...   
        ...   
    }
}

Again each project will have its own implementation of this.

The repositories in each project will be different, because of the way we access data in each of the servers. The old server will be used to query only so we are using it to import users into the new server.

How would I use Simple Injector to inject these services into the same interface but depending on which repository is used, the right implementation will be pulled in? If that makes sense?

Is this even possible, Also does this break the Liskov Substitution Principle.

Am I better to write each implementation as it's own thing? If so, won't this break the DRY principle?

Hope this is not to broad, thank you in advance.

Upvotes: 1

Views: 370

Answers (1)

Steven
Steven

Reputation: 172855

Also does this break the Liskov Substitution Principle.

Whether or not you break the LSP depends on how the SessionService classes behave. You should always ask yourself: "What will happen if I swap the implementations?" If injecting the Oldldap into the NewRepo causes the NewRepo to fail at runtime, you are violating the LSP, which is basically telling you your design is wrong. If on the other hand the NewRepo keeps functioning correctly, because both the Oldldap and Newldap behave contractually the same, you're fine. So does the repository care about its implementation, or do only you care. Note that if the Oldldap performs too slow for NewRepo, that is probably not a violation of LSP. In that case only you care (or probably your customer, because of non-functional requirements).

The solution to LSP violations is always quite easy: give each implementation its own interface. Developers tend to have problems with this, because both abstractions seem exact duplicates of each other and making 'a copy' seems to violate DRY. But appearances are deceptive; although they have the same members, they have a different, incompatible, contract.

But in case you are not violating LSP, keeping that single interface is fine (according to the LSP). You can make multiple contextual registrations with Simple Injector 3 using RegisterConditional as follows:

container.RegisterConditional<ISessionService,
    Infrastructure.OldLdap.Service.SessionService>(
    c => c.Consumer.ImplementationType.Namespace.Contains("Oldldap"));

container.RegisterConditional<ISessionService,
    Infrastructure.NewLdap.Service.SessionService>(
    c => c.Consumer.ImplementationType.Namespace.Contains("Newldap"));

Upvotes: 4

Related Questions