4

次の問題があります。IntまたはGuidエンティティキー用に設計されたDbContextを使用する汎用EFリポジトリがあるため、基本エンティティクラスがあります。

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

コードを実行すると

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

ここで、EntityはTKey型であり、派生クラスでintとして設定されています。たとえば、次のエラーが発生します。

タイプ「System.Int32」をタイプ「System.Object」にキャストできません。LINQ to Entitiesは、エンティティデータモデルのプリミティブ型のキャストのみをサポートします

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

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

4

1 に答える 1

12

それを回避する簡単な方法があると思いますが、それはハックです。もう一度強調しておきますが、これは本当にハックです。あなたはこれを試すことができます:

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

上記のコードは一般的には機能しないはずです。CLRの世界では、値はボックス化され、参照によって比較されます。ボックス化された値が同じであっても、参照は異なるため、結果はfalseになります。ただし、EFクエリはCLRによって実行されませんが、SQLに変換されます。その結果、クエリは次のように変換されますWHERE Id = {EntityId}。これが必要なものです。繰り返しますが、これを使用するには、このようなものがどのように、そしてなぜ機能するのかを理解する必要があり、おそらく少し危険です。ただし、ハックがあるため、よりクリーンなソリューションが必要です。実際、クリーンな(そしてここでは簡単な解決策ではありません)のは、上記の式を手動で作成することです。次に例を示します(申し訳ありませんが、お客様のエンティティを正確に使用していません)。

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

Filterメソッドでは、作成できるフィルター式を作成することに注意してください。この例では、効果的なクエリは次のようになりますDbSet()。Where(e => e.Id == idValue).First()(上記のハックに似ています)が、このクエリの上に他のlinq演算子を使用できます(複数の基準でフィルタリングするためのFilterメソッドの結果に対するFilterメソッドの呼び出しを含む)

エンティティとコンテキストを次のように定義しました。

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メソッドを呼び出します。var e2 = GetEntity(e => e.Id, guidKey);

于 2012-05-03T00:10:29.810 に答える