How to lookup entities in EntityFramework 4 by a finder interface? (works in LinqToSql)
I have a repository layer which my applications access which can be initialised with an IDataSource
; e.g. LinqToSqlDataSource, EntityFrameworkDataSource, etc...
An IDataSource provides methods for inserting, updating, deleting and querying a data source respectively. Relevant to this question, is that the FindAll<T>
returns an IQueryable<T>
.
All my base entities implement a simple interface to make looking up entities by id generic and convenient;
public interface IAmIdentifiable<T>
{
T Id { get; set; }
}
Below is the relevant code for the FindById<T, TKey>
method I am having problems with in EntityFramework.
public class Repository
{
public Repository(IDataSource dataSource)
{...}
public T FindById<T, TKey>(TKey identifier) where T : class, IAmIdentifiable<TKey>
{
return _DataSource.FindAll<T>().SingleOrDefault(i => i.Id.Equals(identifier));
}
...
}
This FindById<T, Tkey>(...)
works fine with LinqToSql but does not work in EntityFramework 4.
Example usage
User user = Repository.FindById<User, int>(someUserId);
Message msg = Repository.FindById<Message, Guid>(someMessageId);
When the above code is run with an EntityFramework 4 IDataSource implementation it produces the following error;
Unable to create a constant value of type 'System.Object'. Only primitive types ('such as Int32, String, and Guid') are supported in this context.
I have tried changing this to perform an == comparison on value types. I read that a roundabout way to constrain a generic to a value type is to constraint to struct
. I have updated the base interface for all entities and the repository finder accordingly...
public interface IAmIdentifiable<T> where T : struct
{
T Id { get; set; }
}开发者_运维知识库
public T FindById<T, TKey>(TKey identifier)
where T : class, IAmIdentifiable<TKey>
where TKey : struct
{
return _DataSource.FindAll<T>().SingleOrDefault(i => i.Id == identifier);
}
However this still results in a compilation error;
Error 59 Operator '==' cannot be applied to operands of type 'TKey' and 'TKey'
Can anybody shed some light on how I might go about casting these entities to the IAmIdentifiable<T>
interface in order to have a generic way to retrieve entities by Id?
I think the error there lies in the way you are trying to compare the TKey generic. Since TKey is a complex type, the == operator has to be explicitly implemented in order to make this comparison. Now with the TKey generic, there is no guarantee that it is. Perhaps there is another generic constraint you can place on TKey to ensure that there is a comparison method available?
Don't ask me why this works, but we managed to make this happen by doing:
i => (object)i.Id == (object)identifier
Somehow this behaves nicely and writes the correct SQL WHERE
clause. All of the other alternatives we tried didn't work.
精彩评论