DDiVita
DDiVita

Reputation: 4265

Extension method that returns an the correct implementation of an interface?

I am not sure if this is possible and I may need to write extension methods for each implementation. Here is some example code:

public interface IBaseService<T>
{
    IUnitOfwork UnitOfWork {get;}
}

public interface IService<T>: IBaseService<T>
{
    IEnumerable<T> GetAll();
    T GetById(Guid id);
}

public interface IUserService: IService<User>
{
     User FindByUsernameAndPassword(string username, string password)
}

public class BaseService<T>: IService<T>
{

     public BaseService(IRepository<T> repository)
     {
        _repository = repository
     }
     public virtual IEnumerable<T> GetAll(){....};
     public virtual T GetById(Guid id){....};
     IUnitOfWork UnitOfWork {get {return _repository.UnitOfWork;}}
}

public class UserService: BaseService<User>, IUserService
{
   ...
}

public static class ServiceExtensions
{
     public static IBaseService<T> EnableLazyLoading<T>(this IBaseService<T> service, bool lazyLoad = True)
     {
          service.UnitOfWork.EnableLazyLoad(lazyLoad);
          return service;
     }
}

So, let's say I am using the UserService. When I call the extension method on the UserService, is it possible to have it return the proper implementation of IBaseService or do I need to create and Extension Method for each implementation?:

Example:

userService.EnableLazyLoading(false).FindByUsernameAndPassword("ddivita","123456")

Upvotes: 4

Views: 239

Answers (2)

James Michael Hare
James Michael Hare

Reputation: 38397

Okay, this may or may not work with your design, but it builds on Felix's answer (which he should get the credit for) and makes it infer-able.

Because your UnitOfWork class does not depend on type T, you can create an IBaseService that is non generic that contains the UnitOfWork member, then make IBaseService<T> extend IBaseService like so:

public interface IBaseService
{
    // all non-type-T related stuff
    IUnitOfwork UnitOfWork {get;}
}

public interface IBaseService<T> : IBaseService
{
    // .. all type T releated stuff
}

Then, keep rest of your class design as normal and write the extension method as:

public static S EnableLazyLoading<S>(this S service, bool lazyLoad = true)
     where S : IBaseService
{
     service.UnitOfWork.EnableLazyLoad(lazyLoad);
     return service;
}

Since we don't need the IBaseService<T> to get UnitOfWork now, we don't need to specify the second type parameter T which was making inference problematic. So now we can write the code exactly as you wanted because it can now infer S is UserService without needing to know about the type T (User):

userService.EnableLazyLoading(false).FindByUsernameAndPassword("ddivita","123456");

Of course, this is assuming, as I said, that UnitOfWork doesn't need anything from type T.

As I said, @Felix deserves the answer, but just wanted to expand on how could make it infer-able to avoid having to pass in the generic type parameter. Up-votes are appreciated though :-)

Upvotes: 2

Felix K.
Felix K.

Reputation: 6281

You can do it this way:

public static S EnableLazyLoading<T, S>(this S service, bool lazyLoad = true)
     where S : IBaseService<T>
{
     service.UnitOfWork.EnableLazyLoad(lazyLoad);
     return service;
}

Upvotes: 3

Related Questions