Reputation: 14166
I have a series of interface definitions, all of which compile (so my objects are composed correctly). The objects instantiate, as expected. However, when I try to return the object from its' underlying factory I get the following error:
ERROR:
Unable to cast object of type 'SampleLibrary.Domain.DataAcessors.Person.SQLDataAccessor' to type 'Common.Contracts.DataAccessors.IDataAccessorModel`2[SampleLibrary.Contracts.Models.IPerson,SampleLibrary.Domain.DataAccessors.Types.SqlServer]'.
Please keep in mind I am trying to return each instance as the IDataAccessor interface.
CODE:
public interface IDataAccessor<I, T>
{
T AccessType { get; }
}
public interface IDataAccessorModel<I, T> : IDataAccessor<I, T>
{
I Instance { get; }
IResult<string> Get(I instance);
IResult<string> Add(I instance);
IResult<string> Update(I instance);
IResult<string> Delete(I instance);
}
public class SQLDataAccessor : IDataAccessorModel<IPerson, IAccessType>
{
internal SQLDataAccessor(IResult<string> result)
{
_connectionString = "";
_result = result;
}
private readonly string _connectionString;
private IResult<string> _result;
public IAccessType AccessType { get { return new SqlServer(); } }
public IPerson Instance { get; private set; }
public IResult<string> Add(IPerson instance)
{
Instance = instance;
return _result;
}
public IResult<string> Get(IPerson instance)
{
Instance = instance;
return _result;
}
public IResult<string> Delete(IPerson instance)
{
Instance = instance;
return _result;
}
public IResult<string> Update(IPerson instance)
{
Instance = instance;
return _result;
}
}
public class FactoryDataAccess : IFactoryDataAccess
{
internal FactoryDataAccess() { }
public IDataAccessor<I, T> Create<I, T>()
{
var model = typeof(I);
var target = typeof(T);
if (model.IsAssignableFrom(typeof(IPerson)))
{
if (target == typeof(SqlServer)) {
var accessor = new Person.SQLDataAccessor(new Result());
// This next line FAILS!
return (IDataAccessorModel<I, T>)accessor;
}
}
throw new NotSupportedException("Type " + target.FullName + " and Source " + model.FullName + " is not supported.");
}
}
UPDATE:
Please keep in mind that IDataAccessorModel
can be used by any desired DataAccess type you wish to define.
Upvotes: 0
Views: 154
Reputation: 112299
It work for me if I declare SQLDataAccessor
like this:
public class SQLDataAccessor : IDataAccessorModel<IPerson, SqlServer>
{
...
}
You are probably calling it like this
var factory = new FactoryDataAccess();
var da = factory.Create<IPerson, SqlServer>();
i.e. you call it with T
being SqlServer
. If you declare T
as IAccessType
in SQLDataAccessor
it is not guaranteed that IAccessType
would be SqlServer
. Therefore the casting error. (However, SqlServer
is guaranteed to be IAccessType
as it probably implements it.)
Upvotes: 0
Reputation: 1252
The way you have it, I
could be any type derived from IPerson
and T
is exactly of type SqlServer
, which would cause the cast to fail since SQLDataAccessor
implements the IDataAccessorModel
with different parameters. You would need to have a more exact cast, such as:
return (IDataAccessorModel<IPerson, IAccessType>)accessor;
Upvotes: 0
Reputation: 13286
SQLDataAccessor
is not a generic class, but implements IDataAccessorModel<IPerson, IAccessType>
exactly, so your method Create
should return IDataAccessor<IPerson, IAccessType>
, but you probably called it with other generic types.
Change SqlDataAccessor
to:
public class SQLDataAccessor<I, T> : IDataAccessorModel<I, T>
{
internal SQLDataAccessor(IResult<string> result)
{
_connectionString = "";
_result = result;
}
private readonly string _connectionString;
private IResult<string> _result;
public T AccessType { get { return new SqlServer(); } }
public I Instance { get; private set; }
public IResult<string> Add(I instance)
{
Instance = instance;
return _result;
}
public IResult<string> Get(I instance)
{
Instance = instance;
return _result;
}
public IResult<string> Delete(I instance)
{
Instance = instance;
return _result;
}
public IResult<string> Update(I instance)
{
Instance = instance;
return _result;
}
}
You might want to limit I
and T
to the interfaces, so add a where
constraint:
public class SQLDataAccessor<I, T> : IDataAccessorModel<I, T>
where I : IPerson
where T : IAccessType
Upvotes: 0
Reputation: 292405
SQLDataAccessor
implements IDataAccessorModel<IPerson, IAccessType>
, so it would work only if <I, T>
were <IPerson, IAccessType>
. There is no guarantee about that, since the method is generic and I and T could be any type, so the cast fails.
Of course, since you're checking the types of I and T, you know the cast would be valid, but the compiler doesn't. You can trick it like this:
return (IDataAccessorModel<I, T>)(object)accessor;
However, since T
has to be SqlServer
, it doesn't make sense to make it a generic type parameter. And since I
has to implement IPerson
, there should be a constraint on it. So the method signature should be:
public IDataAccessor<I, T> Create<T>() where T : IPerson
Upvotes: 1