2009-03-10 8 views
11

Calling Get dans le code suivant fonctionne très bien:LINQ étrange question NHibernate, non valide cast de 'System.Int32'

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(); 
    } 

mais quand je fais ceci:

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(); 
    } 

"entités" à l'intérieur la méthode get lève une exception lorsque vous la parcourez:

Invalid cast from 'System.Int32' to 'TEntity' (where TEntity is the type name) 

Vous avez des idées?

Edit: voici ce que les différentes expressions ressemblent:

Dans la version générique (en haut un), il semble essayer de convertir x pour une raison quelconque, qui doit être à cause des médicaments génériques: 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 omis pour plus de clarté)

2ème édition: il semble être quand il essaie de convertir entre IEntity et le type d'instance (TEntity)

ici est IEntity:

public interface IEntity 
{ 
    int Id { get; } 
} 

3ème édition: il semble être Convert (x) qui provoque la AssociationVisitor de ne pas correctement visiter l'arbre d'expression et de convertir "Convert (x) .id"

4 Edit: Et voilà, quelqu'un a déjà trouvé le bug https://nhibernate.jira.com/browse/NHLQ-11!

Merci

Andrew

+0

Cela semble très étrange. Avez-vous des journaux de ce que SQL est en cours d'exécution? À quoi ressemble le reste de la pile? –

+0

Aucun SQL ne semble être exécuté, il semble que la Linq échoue avant d'atteindre la base de données. Je viens de télécharger la source pour un débogage http://sourceforge.net/project/showfiles.php?group_id=216446&package_id=306405&release_id=654054 –

+0

J'ai aussi rencontré ce problème! – bleevo

Répondre

1

Je l'ai rencontré dans le passé et il revient habituellement au champ dans la table de base de données que vous lisez ne sont pas dans un format compatible. Comme les booléens ne convertissent pas en entiers.

Vérifiez vos types de champs dans le tableau et assurez-vous qu'ils sont compatibles. Peut-être que votre colonne Id est un BIGINT?

+0

je suis d'accord que cela pourrait provoquer une exception "casting invalide", mais ce n'est pas ce qui se passe ici. Lisez le code et le rapport de bug sur jira. C'est un bug causé par les génériques –

3

Je crois que le problème est que Linq/NHibernate essaie de mapper IEntity.Id à une colonne de table au lieu de TEntity.Id. J'ai rencontré ce problème avec une implémentation de référentiel LinqToSql. La façon autour d'elle était d'utiliser une expression comme ceci:

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); 
} 

Donc, cela changerait Get (id) à:

public TEntity Get(int id) 
{ 
    var entities = Query.Where(GetFindExpression("Id", id)); 
    return entities.FirstOrDefault(); 
} 

Mise à jour:

Si vous ne voulez pas pour faire face aux expressions (elles peuvent être difficiles!), Vous pouvez utiliser la bibliothèque dynamique LINQ comme décrit par Scott Guthrie ici: http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx

qui changerait Get (id) à:

public TEntity Get(int id) 
{ 
    var entities = Query.Where("Id = @0", id); 
    return entities.FirstOrDefault(); 
} 
+1

"Je crois que le problème est que Linq/NHibernate essaie de mapper IEntity.Id à une colonne de table au lieu de TEntity.Id." Je suis entièrement d'accord. J'aime l'idée de transformer le lambda, je vais essayer de faire une version générique qui va gérer toutes les transformations ... restez à l'écoute –

1

Je suis le même problème :(

Ce qui suit ne fonctionne pas.

public override T Load(int id) 
{ 
    return (from t in _sessionFactory.Session.Linq<T>() 
      where t.ID == id 
      select t).SingleOrDefault(); 
} 

Ce qui suit fait!

public override Product Load(int id) 
{ 
    return (from t in _sessionFactory.Session.Linq<Product>() 
      where t.ID == id 
      select t).SingleOrDefault(); 
} 
1

Le bug a été rapporté, mais pas encore fixé:

https://nhibernate.jira.com/browse/NHLQ-11

J'ai posté un test simple à reproduire.

Espérons qu'il sera bientôt traité!

+0

J'ai remarqué que vous avez ajouté un cas de test, un beau travail. J'espère qu'ils vont régler ça bientôt! –