Calling Get in the following code works fine:
public class ContractService : IContractService
{
private readonly IRepository<Contract> repository;
public ContractService(IRepository<Contract> repository)
{
this.repository = repository;
}
public Contract Get(int contractId)
{
return repository.Query().Where(x => x.Id == contractId).FirstOrDefault();
}
but when i do this:
public class ContractService : CRUDService<Contract>, IContractService
{
public ContractService(IRepository<Contract> repository) : base(repository)
{
}
}
public class CRUDService<TEntity> : ICRUDService<TEntity> where TEntity : IEntity
{
protected readonly IRepository<TEntity> repository;
public CRUDService(IRepository<TEntity> repository)
{
this.repository = repository;
}
public TEntity Get(int id)
{
var entities = this.repository.Query().Where(s => s.Id == id);
return entities.FirstOrDefault();
}
"entities" inside the get method throws an exception when you iterate over it:
Invalid cast from 'System.Int32' to 'TEntity' (where TEntity is the type name)
Anyone got any idea why?
Edit: here's what the different expressions look like:
In the generic version (top one), it seems to be trying to convert x for some reason, which must be because of the generics :s
{value(NHibernate.Linq.Query`1[Contract]).Where(x => (Convert(x).Id = value(CRUDService`1+<>c__DisplayClass0[Contract]).Id)).FirstOrDefault()}
{value(NHibernate.Linq.Query`1[Contract]).Where(x => (x.Id = value(ContractService+<>c__DisplayClass2).Id)).FirstOrDefault()}
(namespaces omitted for clarity)
2nd Edit: It seems to be when it tries to convert between IEntity and the instance type (TEntity)
here is IEntity:
public interface IEntity
{
int Id { get; }
}
3rd Edit: it seems to be the Convert(x) that causes the AssociationVisitor to not properly visit the expression tree and convert "Convert(x).Id"
4th Edit: And there we go, someones already found the bug https://nhibernate.jira.com/browse/NHLQ-11!
Thanks
Andrew
I believe the problem is that Linq/NHibernate is trying to map IEntity.Id to a table column instead of TEntity.Id. I had this problem with a LinqToSql repository implementation. The way around it was to use an expression like this:
private static Expression<Func<TEntity, bool>> GetFindExpression(string propertyName, object value)
{
ParameterExpression parameterExpression = Expression.Parameter(typeof (TEntity), "id");
MemberExpression propertyExpression = Expression.Property(parameterExpression, propertyName);
Expression bodyExpression = Expression.Equal(propertyExpression, Expression.Constant(value));
return Expression.Lambda<Func<TEntity, bool>>(bodyExpression, parameterExpression);
}
So this would change Get(id) to:
public TEntity Get(int id)
{
var entities = Query.Where(GetFindExpression("Id", id));
return entities.FirstOrDefault();
}
Update:
If you don't want to deal with expressions (they can be tricky!), you could use the Dynamic LINQ library as described by Scott Guthrie here: http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
That would change Get(id) to:
public TEntity Get(int id)
{
var entities = Query.Where("Id = @0", id);
return entities.FirstOrDefault();
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With