0

オブジェクトの特定のリストに PK がリストされている特定のテーブルから要素のみを取得する方法を見つけようとしています。

私がそうしようとしている理由を聞かないでください。これは、指定された主キーのすべてのローカル値をプリロードしてから、それらを挿入、更新、または削除する複数のデータベースの同期用です。

最初の試行では、動的リフレクションを使用してテーブル全体をロードし、テーブル PK をキーとして、EntityObject を値としてディクショナリを返しましたが、これはまったく問題ありませんでした。ただし、大きなテーブルの場合、これは非常に重くなります。したがって、特定の PK 用のものだけが必要です。

private static Dictionary<object, EntityObject> GetAllEntities<TEntityType>(ObjectContext dbContext, IEnumerable<object> primaryValues) where TEntityType : EntityObject
    {
        // loop through the elements for the given entity
        return dbContext.CreateObjectSet<TEntityType>().ToDictionary(type => type.EntityKey.EntityKeyValues[0].Value, type => (EntityObject)type); // this runs fine

        // this is what i need to be executed
        return dbContext.CreateObjectSet<TEntityType>().Where(type => primaryValues.Contains(type.EntityKey.EntityKeyValues.First().Value)).ToDictionary(type => type.EntityKey.EntityKeyValues[0].Value, type => (EntityObject)type); // this crashes
    }

Linq 2 エンティティは EntityKey を直接処理できないため、2 行目は予想どおりにエラーをスローします。

指定された型メンバー 'EntityKey' は、LINQ to Entities ではサポートされていません。初期化子、エンティティ メンバー、およびエンティティ ナビゲーション プロパティのみがサポートされています。

インターネットのどこかで数か月前に方法を見つけたと確信していますが、再び見つけることができませんでした。私が覚えているのは、最初にそれをどこかに投稿した人が、WHERE句でEntityObjects PK値を返すメソッドを呼び出したので、指定されたリストに存在するかどうかを確認できることだけです。

ヘルプ、ヒント、リンクなど、何でも歓迎します。

多分それは間違った質問であるか、これらのケースを処理する正しい方法ではありません. だから何でも役に立ちます。

追加情報: 各テーブルには、Guid または string 型の PK が 1 つだけあります。したがって、単純な文字列から文字列への等価性チェックになります。

4

1 に答える 1

0

これより少し複雑です。ここでの問題は、linq クエリが SQL に変換されることです。Entity Framework は、「既知の」型、つまりモデルの型を含む SQL 式にのみ変換できます。EntityKey はそのようなタイプではないため、例外です。それでもあなたがやろうとしていることは可能ですが、Linq Expressions とリフレクション マジックが必要です。私は次のような方法を思いつきました:

    private static IQueryable<TEntity> DynamicContains<TEntity, TProperty>(IQueryable<TEntity> query, Expression<Func<TEntity, TProperty>> property, IEnumerable<TProperty> values)
    {
        var memberExpression = property.Body as MemberExpression; 
        if (memberExpression == null || !(memberExpression.Member is PropertyInfo)) 
        { 
            throw new ArgumentException("Property expression expected", "property"); 
        }

        // get the generic .Contains method
        var containsMethod =
            typeof(Enumerable)
            .GetMethods()
            .Single(m => m.Name == "Contains" && m.GetParameters().Length == 2);

        // convert the generic .Contains method so that is matches the type of the property
        containsMethod = containsMethod.MakeGenericMethod(typeof(TProperty));

        // build e => Enumerable.Contains(values, e.Property)
        var lambda = 
            Expression.Lambda<Func<TEntity, bool>>(
                Expression.Call(
                    containsMethod, Expression.Constant(values), property.Body), 
                    property.Parameters.Single());

        // return query.Where(e => Enumerable.Contains(values, e.Property))
        return query.Where(lambda);
    }

これは、エンティティが持つ可能性のあるプロパティ (キーだけでなく) の汎用の Container です。IQueryable を使用するため、(DbSet/ObjectSet だけでなく) 任意のクエリに適用でき、IQueryable を返すため、その上にさらに構築できます。すべての山かっこに怖がらないでください。このメソッドは次のように使用します。

var entities = DynamicContains(ctx.EntitySet, e => e.Id, new[] { 1, 4 });

e => e.Id は、Contains にどのプロパティを使用する必要があるかを伝える優れた方法です。

このメソッドを野生で示す完全な例を次に示します。

    using System;
    using System.Collections.Generic;
    using System.Data.Entity;
    using System.Linq;
    using System.Linq.Expressions;
    using System.Reflection;
    using System.Text;
    using System.Threading.Tasks;

    namespace ConsoleApplication3
    {
        class MyEntity
        {
            public int Id { get; set; }
            public string Description { get; set; }
        }


        class MyContext : DbContext
        {
            public DbSet<MyEntity> EntitySet { get; set; }
        }

        class Program
        {

            private static void Seed()
            {
                using (var ctx = new MyContext())
                {
                    if (!ctx.EntitySet.Any())
                    {
                        ctx.EntitySet.Add(new MyEntity() { Description = "abc" });
                        ctx.EntitySet.Add(new MyEntity() { Description = "xyz" });
                        ctx.EntitySet.Add(new MyEntity() { Description = null });
                        ctx.EntitySet.Add(new MyEntity() { Description = "123" });
                        ctx.SaveChanges();
                    }
                }
            }

            private static void PrintEntities(IEnumerable<MyEntity> entities)
            {
                foreach (var e in entities)
                {
                    Console.WriteLine("Id: {0}, Description: {1}", e.Id, e.Description);
                }
            }

            static void Main(string[] args)
            {
                List<int> list = new List<int>() { 1, 3 };

                Seed();

                using (var ctx = new MyContext())
                {
                    PrintEntities(DynamicContains(ctx.EntitySet, e => e.Id, new[] { 1, 4 }));
                    PrintEntities(DynamicContains(ctx.EntitySet, e => e.Description, new[] { null, "xyz" }));

                }
            }

            private static IQueryable<TEntity> DynamicContains<TEntity, TProperty>(IQueryable<TEntity> query, Expression<Func<TEntity, TProperty>> property, IEnumerable<TProperty> values)
            {
                var memberExpression = property.Body as MemberExpression; 
                if (memberExpression == null || !(memberExpression.Member is PropertyInfo)) 
                { 
                    throw new ArgumentException("Property expression expected", "property"); 
                }

                // get the generic .Contains method
                var containsMethod =
                    typeof(Enumerable)
                    .GetMethods()
                    .Single(m => m.Name == "Contains" && m.GetParameters().Length == 2);

                // convert the generic .Contains method so that is matches the type of the property
                containsMethod = containsMethod.MakeGenericMethod(typeof(TProperty));

                // build e => Enumerable.Contains(values, e.Property)
                var lambda = 
                    Expression.Lambda<Func<TEntity, bool>>(
                        Expression.Call(
                            containsMethod, Expression.Constant(values), property.Body), 
                            property.Parameters.Single());

                // return query.Where(e => Enumerable.Contains(values, e.Property))
                return query.Where(lambda);
            }
        }
    }
于 2012-06-20T18:27:43.770 に答える