James Gordon
James Gordon

Reputation: 145

Implicit conversion en C# generic classes

I have an application that is structured as an service layer wich uses a repository layer for persistence. I'm trying to create a generic controller class to reuse shared behavior but I'm having trouble trying to set the generic parameters. The following code:

public class BusinessEntity
{ }

public class Person : BusinessEntity
{ }

public interface IRepository<T> where T : BusinessEntity
{ }

public interface IService<T, R>
    where T : BusinessEntity
    where R : IRepository<T>
{ }

public partial interface IPersonRepository : IRepository<Person>
{ }

public interface IPersonService : IService<Person, IPersonRepository>
{ }

public abstract class BaseController<X, Y>
    where X : BusinessEntity
    where Y : IService<X, IRepository<X>>
{ }

public class PersonController : BaseController<Person, IPersonService>
{ }

fails at compilation with

The type ConsoleApplication.IPersonService cannot be used as type parameter Y in the generic type or method ConsoleApplication.BaseController<X,Y>. There is no implicit reference conversion from ConsoleApplication.IPersonService to ConsoleApplication.IService<ConsoleApplication.Person,ConsoleApplication.IRepository<ConsoleApplication.Person>>

this works

public interface IPersonService : IService<Person, IRepository<Person>>

but I lose the custom repository

There is a way to make the compiler realize IPersonRepository is an IRepository<Person>?

Upvotes: 3

Views: 759

Answers (2)

phoog
phoog

Reputation: 43076

public class BusinessEntity
{ }

public class Person : BusinessEntity
{ }

public interface IRepository<T> where T : BusinessEntity
{ }

public interface IService<T, R>
    where T : BusinessEntity
    where R : IRepository<T>
{ }

public partial interface IPersonRepository : IRepository<Person>
{ }

public interface IPersonService : IService<Person, IPersonRepository>
{ }

public abstract class BaseController<X, Y, Z>
    where X : BusinessEntity
    where Y : IService<X, Z>
    where Z : IRepository<X>
{ }

public class PersonController : BaseController<Person, IPersonService, IPersonRepository>
{ } 

To address your comment:

IPersonService can extend the base service class to add custom facilities, like FindPersonsUnderAge(). For this it requires a custom repository. Actually LINQ avoids a lot of custom repository code, but sometimes they are required.

Couldn't IPersonService do that without requiring the repository type to be a type parameter? For example:

public interface IService<T> where T : BusinessEntity { } 

public interface IPersonService : IService<Person>
{
    IEnumerable<Person> FindPersonsByAge(double minAge, double maxAge);
}

public class Service<T, R> : IService<T>
    where T : BusinessEntity 
    where R : IRepository<T> 
{ }

public class PersonService : Service<Person, IPersonRepository>, IPersonService
{ }

Upvotes: 4

James Gordon
James Gordon

Reputation: 145

Thanks to sll for pointing me in the right direction

public interface IService<T, out R>
    where T : BusinessEntity
    where R : IRepository<T>
{ }

does the trick

Upvotes: 0

Related Questions