質問

I have the following DBML modification (i'm using Linq to SQL as a DAL).

public interface ILinqSQLObject { }

// these are objects from SQL Server mapped into Linq to SQL
public partial class NEWDEBT : ILinqSQLObject { }
public partial class OLDDEBT : ILinqSQLObject { }
public partial class VIPDEBT : ILinqSQLObject { }

With that i can manipulate my Linq objects more properly on other areas.

I've just done an IRepository pattern implementation.

public interface IDebtManager<T>
    {
        IQueryable<T> GetAllDebts();
        IQueryable T GetSpecificDebt(System.Linq.Expressions.Expression<Func<T, bool>> predicate);
        void Insert(T debt);
        // other methods
    }

public class DebtManager<T> : IDebtManager<T> where T : class, ILinqSQLObject 
    {
        DebtContext conn = new DebtContext();
        protected System.Data.Linq.Table<T> table;

        public DebtManager()
        {
            table = conn.GetTable<T>();
        }

        public void Insert(T debt)
        {
            throw new NotImplementedException();
        }

        public IQueryable<T> GetSpecificDebt(System.Linq.Expressions.Expression<Func<T, bool>> predicate)
        {
            return table.Where(predicate);
        }

        public IQueryable<T> GetAllDebts()
        {
            return table;
        }
    }

And that works flawlessly. But, there are sometimes that i don't know, on compilation time, which specific table i'll be using. For that i tried to create a simple generic factory for my DebtManager.

public static class DebtFactoryManager
{

    public static DebtManager<ILinqSQLObject> GetDebtManager(string debtType) 
    {
        switch (debtType)
        {
            case "New Client":
                return new DebtManager<NEWDEBT>();
            case "Old Client":
                return new DebtManager<OLDDEBT>();
            case "VIP Client":
                return new DebtManager<VIPDEBT>();
            default:
                return new DebtManager<NEWDEBT>();
        }

        return null;
    }

}

However it doesn't work. It says that i cannot 'implicity convert DebtManager<NEWDEBT> to DebtManager<ILinqSQLObject>', but if NEWDEBT implements ILinqSQLObject, why isn't the compiler recognizing it? Obviously i'm doing some mistake but i can't see it.

役に立ちましたか?

解決

This error is caused by the fact that generics do not implicitly support covariance; that is, treating a specific generic parameter type as if it were one of its base types.

Couple ways around this. First, you can define a non-generic DebtManager base class that the generic DebtManager inherits from, and return that. Second, you can define a generic interface that DebtManager implements; generic interfaces CAN be defined to be covariant, by using the keyword out before the generic type parameter.

EDIT: Let's go back to the primary need. You may not know, at compile-time, what type of object you will be required to work with and therefore you don't know which Repository you need. Might I suggest, then, that instead of a Repository-per-table architecture, you use a Repository-per-database. DebtManager is already generic to any Linq to SQL type; why not then make the methods generic as well, allowing them to be generic from call to call?

public interface IRepository<T> where T:class, ILinqSqlObject
{
    IQueryable<TSpec> GetAllDebts<TSpec>() where TSpec : T;
    IQueryable<TSpec> GetSpecificDebt<TSpec>(System.Linq.Expressions.Expression<Func<TSpec, bool>> predicate) where TSpec : T;
    void Insert<TSpec>(TSpec debt) where TSpec:T;
    // other methods
}

interface IDebtObject : ILinqSqlObject

public interface IDebtManager:IRepository<IDebtObject> { }

public class DebtManager:IDebtManager
{
    DebtContext conn = new DebtContext();

    public DebtManager()
    {            
    }

    public void Insert<T>(T debt) where T:IDebtObject
    {
        throw new NotImplementedException();
    }

    public IQueryable<T> GetSpecificDebt(System.Linq.Expressions.Expression<Func<T, bool>> predicate) where T:IDebtObject
    {
        return conn.GetTable<T>().Where(predicate);
    }

    public IQueryable<T> GetAllDebts<T>() where T:IDebtObject
    {
        return conn.GetTable<T>();
    }

}
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top