Search code examples
c#interfaceentity-framework-5factory-pattern

Returning a derived type from factory class


I have a factory class with this method signature

 public static PortfolioUnitOfWork<IPortfolio> GetPortfolioUnitOfWork(string supplier)
 {
 }

The PortfolioUnitOfWork is a generic object, which can take different Portfolio types.

public class PortfolioUnitOfWork<T> :Abstracts.UnitOfWork<T> where T:class, Data.Interfaces.IPortfolio
{
}

The idea here, is a string is passed into the factory, from that string it could return PortfolioUnitOfWork<Type1> or PortfolioUnitOfWork<Type2> etc. Type1 and Type2 would inherit from IPortfolio.

When trying to set return items for this factory class method, I have the error

cannot convert expression type PortfolioUnitOfWork<Type1> to return type PortfolioUnitOfWork<IPortfolio>

The IPortfolio interface has no methods, just a number of properties

public interface IPortfolio
{
   int Id { get; set; }
   string Name { get; set; }
   ....
} 

The Portfolio types are EntityFramework Entities, but I have a partial class for each, in which the entity is inherited from the interface. They also have a few non-mapped properties as a result of that.

I would have thought using PortfolioUnitOfWork<IPortfolio> as the return type would allow the factory class to return the correct PortfolioUnitOfWork as required. Any ideas why the error might occur?

//Edit

Strangely, in the Factory Class if I set the return type to

return new PortfolioUnitOfWork<IPortfolio>()

There is no immediate error showing in the IDE (didn't do a build though). I would have thought that would be invalid as T inherits from class in the PortfolioUnitOfWork class

This does not work either, same error.

PortfolioUnitOfWork<IPortfolio> porfolio = new PortfolioUnitOfWork<Type1>();

Something like this DOES work, showing the Type1 implements the correct interface

IPortfolio test = new Type1();

Solution

  • That's because generic types are invariant by default in c#. It means you cannot do following:

    List<object> list = new List<string>();
    

    or in your case

    PortfolioUnitOfWork<IPortfolio> porfolio = new PortfolioUnitOfWork<Type1>();
    

    And you can't do them variant, because in .NET only interface and delegate type parameters can be variant. Following error appears when you try:

    Invalid variance modifier. Only interface and delegate type parameters can be specified as variant.

    Possible way to go? Create IPortfolioUnitOfWork:

    public interface IPortfolioUnitOfWork
    { }
    

    And change your PortfolioUnitOfWork<T> to implement that:

    public class PortfolioUnitOfWork<T> : IPortfolioUnitOfWork where T: class, IPortfolio
    

    now you can do following:

    IPortfolioUnitOfWork porfolio = new PortfolioUnitOfWork<Type1>();
    

    ofc, you'll have to change your return statements and variable types from PortfolioUnitOfWork<IPortfolio> to IPortfolioUnitOfWork.