Could somebody explain why it doesn't work.
I have two db context. And one common method with different return types and different queries.
public interface IDataFetcher<T> where T : IMarker
{
public List<T> GetData();
}
public interface IFetchServiceOne<T> : IDataFetcher<T> where T : IMarker
{
//maybe some methods here
}
public interface IFetchServiceTwo<T> : IDataFetcher<T> where T : IMarker
{
//maybe some different methods here
}
Implementation:
public class FetchServiceOne<T> : IFetchServiceOne<T> where T : IMarker
{
private readonly DBContext _dbContext;
public FetchServiceOne(DBContext dbContext) => _dbContext = dbContext;
public List<CrucialData> GetData()
{
var example = _dbContext.Test.ToList();
return example;
}
}
public class FetchServiceTwo<T> : IFetchServiceOne<T> where T : IMarker
{
private readonly DBContext _dbContext;
public FetchServiceTwo(DBContext dbContext) => _dbContext = dbContext;
public List<CrucialDataTwo> GetData()
{
var example = _dbContext.Test2.ToList();
return example;
}
}
public class CrucialData: IMarker
{
//some properries
}
public class CrucialDataTwo: IMarker
{
//another properries
}
In the output I'm getting compile error:
Error (active) CS0738 'FetchService' does not implement interface member 'IDataFetcher.GetData()'. 'FetchService.GetData()' cannot implement 'IDataFetcher.GetData()' because it does not have the matching return type of 'List'.
CodePudding user response:
It looks like IFetchServiceOne
and IFetchServiceTwo
(and their implementations) should be "concrete" interfaces which implement closed versione of IDataFetcher
. I.e.:
public interface IFetchServiceOne : IDataFetcher<CrucialData>
{
//maybe some methods here
}
public class FetchServiceOne : IFetchServiceOne
{
// ...
public List<CrucialData> GetData()
{
// implementation ...
return default;
}
}
Otherwise you can't guarantee that T
requested by user will be CrucialData
for FetchServiceOne
(i.e. FetchServiceOne<CrucialDataTwo>.GetData()
...)
Or just:
public class FetchServiceOne : IDataFetcher<CrucialData>
{
// ...
}
Without need for introduction of the intermediate interface.
Note that for EF you can create a generic implementation via Set
method if needed:
public class FetchServiceOne<T> : IDataFetcher<T> where T : IMarker
{
private readonly DBContext _dbContext;
public FetchServiceOne(DBContext dbContext) => _dbContext = dbContext;
public List<T> GetData()
{
var example = _dbContext.Set<T>.ToList();
return example;
}
}
But note that this will fail at runtime if there is no matching DbSet
property setup for provided T
type.
CodePudding user response:
List<T>
is neither co- nor contravariant. I.e., a List<A>
is not assignment comapatible to a List<B>
, even if A
and B
are.
Let GetData
return a List<T>
and also cast the IQueryable
result to T
before creating the list:
public class FetchServiceOne<T> : IFetchServiceOne<T> where T : IMarker
{
...
public List<T> GetData()
{
return _dbContext.Test.Cast<T>().ToList();
}
}
This compiles, but be aware that this is not type safe. If the user specifies a T
that is not compatible to the DataSet, you will get a runtime error.