2012-05-01 12 views
1

私は次のような問題を持っている:私はのIntまたはGUIDエンティティのキ​​ーのために設計されDbContextを使用して、一般的なEFリポジトリを持っているので、私は基本エンティティクラスを持っている:EFのオブジェクト比較

public class EntityBase<TKey> where TKey : struct, IComparable 
{ 
    public virtual TKey Id { get; set; } 
} 
  • 処理鍵ます派生クラスでIntまたはGuidとして提供されます。

私はエンティティタイプの処理鍵であり、intとして派生クラスで設定されているコード

public virtual void LoadEntity() 
{ 
    TEntity entity = Repository.Get<TEntity, TKey>(e => object.Equals(e.Id, EntityId)); 
} 

または

public virtual void LoadEntity() 
{ 
    TEntity entity = Repository.Get<TEntity, TKey>(e => e.Id.CompareTo(EntityId) == 0); 
} 

を実行すると、例えば、私は次のエラーを取得します:

'のSystem.Object' をタイプするタイプ '可能System.Int32' をキャストすることができません。 LINQ to Entitiesは、エンティティデータモデルのプリミティブタイプをキャストするだけをサポートします。

Repository.Get DbSetリポジトリのWhere呼び出しのフィルタとして述語パラメータを渡します。

私はエラーを理解しています - EFはSQL文に変換しようとしますが、オブジェクト比較の処理方法はわかりません。しかし、EFがプリミティブ型で動作できるように、基本クラスやLoadEntity()関数を書き直す方法はわかりません。 アイデア

答えて

8

私はその周りに簡単な方法があると思いますが、それはハックです。私は再びそれを強調してみましょう - それは実際にハックです。これを試すことができます:

Repository.Get<TEntity, TKey>(e => (object)e.Id == (object)EntityId); 

上記のコードは一般的に動作しません。 CLRの世界では、値はボックス化され、参照によって比較されます。ボックス化された値が同じであっても、参照は異なり、その結果はfalseになります。ただし、EFクエリはCLRでは実行されませんが、SQLに変換されます。その結果、クエリは次のようなものに変換されます:WHERE Id = {EntityId}これは必要なものです。繰り返しになりますが、これを使うには、どのように、なぜこのような仕組みが必要かを理解する必要があります。しかし、ハックがあるので、よりクリーンなソリューションが必要です。実際、クリーンな(ここでは簡単な解決策ではありません)というのは、上記の式を手動で構築することです。ここで(申し訳ありませんが、私は正確にあなたのエンティティを使用していない)の例である:Filterメソッドで、私は私が作曲できるフィルタ式を構築することを

private static TEntity GetEntity<TEntity, TKey>(Expression<Func<TEntity, TKey>> property, TKey keyValue) 
     where TKey : struct 
     where TEntity : BaseEntity<TKey> 
    { 
     using (var ctx = new Context2()) 
     { 
      var query = Filter(ctx.Set<TEntity>(), property, keyValue); 
      return query.First(); 
     } 
    } 


    private static IQueryable<TEntity> Filter<TEntity, TProperty>(IQueryable<TEntity> dbSet, 
                    Expression<Func<TEntity, TProperty>> property, 
                    TProperty value) 
     where TProperty : struct 
    { 

     var memberExpression = property.Body as MemberExpression; 
     if (memberExpression == null || !(memberExpression.Member is PropertyInfo)) 
     { 
      throw new ArgumentException("Property expected", "property"); 
     } 

     Expression left = property.Body; 
     Expression right = Expression.Constant(value, typeof (TProperty)); 

     Expression searchExpression = Expression.Equal(left, right); 
     var lambda = Expression.Lambda<Func<TEntity, bool>>(Expression.Equal(left, right), 
                  new ParameterExpression[] {property.Parameters.Single()}); 

     return dbSet.Where(lambda); 
    } 

注意。この例では、有効なクエリはこのDbSet()のように見えますが、(e => e.Id == idValue).First()(上記のハックに似ています)次のように

私は、エンティティとコンテキストを定義した(複数の基準によってフィルタリングするFilterメソッドの結果にフィルタメソッドを呼び出す含む): var e2 = GetEntity(e => e.Id, guidKey);

public class BaseEntity<TKey> where TKey : struct 
{ 
    public TKey Id { get; set; } 
} 

public class EntityWithIntKey : BaseEntity<int> 
{ 
    public string Name { get; set; } 
} 

public class EntityWithGuidKey : BaseEntity<Guid> 
{ 
    public string Name { get; set; } 
} 

public class Context2 : DbContext 
{ 
    public DbSet<EntityWithIntKey> EntitiesWithIntKey { get; set; } 

    public DbSet<EntityWithGuidKey> EntitiesWithGuidKey { get; set; } 
} 

あなたはこのようGetEntityメソッドを呼び出します

+0

こんにちはPawel、私はRepository.Get (e =>(オブジェクト)e.Id ==(オブジェクト)EntityId)を使用しました。それはオブジェクトにキャストされているので、好奇心です、しかし、SQLは正しく生成されます(SELECT .... WHERE Id = {EntityId})。ありがとうございました! – Lixi

+0

問題を解決したと聞いてうれしいです。あなたの質問への回答として回答を記入してください。ありがとう! – Pawel

+0

Repository.Get (e =>(オブジェクト)e.Id ==(オブジェクト)EntityId)はデータベース上で動作しますが、メモリ内のコレクションでは機能しません。だから私はPawel(Filter (IQueryable dbSet、式>プロパティ、TProperty値)で提案された2番目のソリューションを試してみましたが、これは正しい解決策です – Lixi

関連する問題