LINQ للكيانات - إنشاء عبارات حيث لاختبار المجموعات ضمن علاقة كثير إلى كثير

StackOverflow https://stackoverflow.com/questions/110314

سؤال

لذلك، أنا أستخدم إطار عمل Linq.لدي كيانين: Content و Tag.إنهم في علاقة متعدد إلى متعدد مع بعضهم البعض. Content يمكن أن يكون لها الكثير Tags و Tag يمكن أن يكون لها الكثير Contents.لذلك أحاول كتابة استعلام لتحديد جميع المحتويات التي تتساوى فيها أسماء العلامات blah

يحتوي كلا الكيانين على مجموعة من الكيان الآخر كخاصية (ولكن لا توجد معرفات).هذا هو المكان الذي أعاني فيه.لدي تعبير مخصص ل Contains (لذا، من يستطيع مساعدتي، يمكنك أن تفترض أنني أستطيع عمل "يحتوي على" لمجموعة).حصلت على هذا التعبير من: http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2670710&SiteID=1

تحرير 1

انتهى بي الأمر بالعثور على إجابتي الخاصة.

هل كانت مفيدة؟

المحلول

بعد القراءة عن PredicateBuilder, ، وقراءة جميع المشاركات الرائعة التي أرسلها لي الأشخاص، ونشرها على مواقع أخرى، ثم قراءة المزيد الجمع بين المسندات و رسم خرائط الوظائف الأساسية..أوه وأنا التقطت قليلا من وظائف الاتصال في استعلامات LINQ (بعض هذه الفصول مأخوذة من هذه الصفحات).

وأخيرا لدي الحل !!!رغم أن هناك قطعة تم اختراقها قليلاً ...

دعونا ننتهي من القطعة المخترقة :(

اضطررت إلى استخدام العاكس ونسخ فئة ExpressionVisitor التي تم وضع علامة عليها على أنها داخلية.ثم اضطررت إلى إجراء بعض التغييرات الطفيفة عليه حتى يعمل.اضطررت إلى إنشاء استثناءين (لأنه كان بمثابة استثناءات داخلية جديدة.اضطررت أيضًا إلى تغيير طريقة إرجاع طريقة ReadOnlyCollection() من:

return sequence.ToReadOnlyCollection<Expression>();

ل:

return sequence.AsReadOnly();

أود أن أنشر الفصل، لكنه كبير جدًا ولا أريد أن أتسبب في فوضى هذا المنشور أكثر مما سيكون عليه بالفعل.آمل أن تتم إزالة هذا الفصل من مكتبتي في المستقبل وأن تقوم Microsoft بنشره للعامة.المضي قدما...

أضفت فئة ParameterRebinder:

public class ParameterRebinder : ExpressionVisitor {
        private readonly Dictionary<ParameterExpression, ParameterExpression> map;

        public ParameterRebinder(Dictionary<ParameterExpression, ParameterExpression> map) {
            this.map = map ?? new Dictionary<ParameterExpression, ParameterExpression>();
        }

        public static Expression ReplaceParameters(Dictionary<ParameterExpression, ParameterExpression> map, Expression exp) {
            return new ParameterRebinder(map).Visit(exp);
        }

        internal override Expression VisitParameter(ParameterExpression p) {
            ParameterExpression replacement;
            if (map.TryGetValue(p, out replacement)) {
                p = replacement;
            }
            return base.VisitParameter(p);
        }
    }

ثم أضفت فئة ExpressionExtensions:

public static class ExpressionExtensions {
        public static Expression<T> Compose<T>(this Expression<T> first, Expression<T> second, Func<Expression, Expression, Expression> merge) {
            // build parameter map (from parameters of second to parameters of first)
            var map = first.Parameters.Select((f, i) => new { f, s = second.Parameters[i] }).ToDictionary(p => p.s, p => p.f);

            // replace parameters in the second lambda expression with parameters from the first
            var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body);

            // apply composition of lambda expression bodies to parameters from the first expression 
            return Expression.Lambda<T>(merge(first.Body, secondBody), first.Parameters);
        }

        public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second) {
            return first.Compose(second, Expression.And);
        }

        public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second) {
            return first.Compose(second, Expression.Or);
        }
    }

والفئة الأخيرة التي أضفتها كانت PredicateBuilder:

public static class PredicateBuilder {
    public static Expression<Func<T, bool>> True<T>() { return f => true; }
    public static Expression<Func<T, bool>> False<T>() { return f => false; }

}

هذه هي نتيجتي...لقد تمكنت من تنفيذ هذا الرمز واستعادة كيانات "المحتوى" الناتجة التي تحتوي على كيانات "علامة" مطابقة من العلامات التي كنت أبحث عنها!

    public static IList<Content> GetAllContentByTags(IList<Tag> tags) {
        IQueryable<Content> contentQuery = ...

        Expression<Func<Content, bool>> predicate = PredicateBuilder.False<Content>();

        foreach (Tag individualTag in tags) {
            Tag tagParameter = individualTag;
            predicate = predicate.Or(p => p.Tags.Any(tag => tag.Name.Equals(tagParameter.Name)));
        }

        IQueryable<Content> resultExpressions = contentQuery.Where(predicate);

        return resultExpressions.ToList();
    }

يرجى إعلامي إذا كان أي شخص يحتاج إلى مساعدة في هذا الأمر نفسه، أو إذا كنت تريد مني أن أرسل لك ملفات لهذا الغرض، أو تحتاج فقط إلى مزيد من المعلومات.

نصائح أخرى

تلخيص ذلك...

contentQuery.Where(
    content => content.Tags.Any(tag => tags.Any(t => t.Name == tag.Name))
);

فهل هذا ما تتوقعه؟

أنا مرتبك قليلا.

وهذا ما يطرحه السؤال نفسه:

contentQuery.Where(
    content => content.Tags.Any(tag => tag.Name == "blah")
);

لست متأكدًا من عملية التفكير للوصول إلى رمز السائل، حقًا، ولست متأكدًا تمامًا مما تفعله بالفعل.الشيء الوحيد الذي أنا متأكد منه حقًا هو أن استدعاء .AsQueryable() غير ضروري تمامًا - إما أن .Tags هو IQueryable بالفعل، أو أن .AsQueryable() سيقوم بتزييفه نيابةً عنك - مضيفًا مكالمات إضافية حيث ليس من الضروري أن يكون هناك أي شيء.

الخطأ مرتبط بمتغير "العلامات".لا يدعم LINQ to Entities المعلمة التي تمثل مجموعة من القيم.ببساطة استدعاء العلامات.AsQueryable() - كما هو مقترح في إجابة سابقة - لن يعمل أيضًا لأن موفر استعلام LINQ الافتراضي في الذاكرة غير متوافق مع LINQ للكيانات (أو موفري العلاقات الآخرين).

كحل بديل، يمكنك إنشاء عامل التصفية يدويًا باستخدام واجهة برمجة تطبيقات التعبير (انظر هذا المنتدى) وتطبيقه على النحو التالي:

var filter = BuildContainsExpression<Element, string>(e => e.Name, tags.Select(t => t.Name));
var query = source.Where(e => e.NestedValues.Any(filter));
tags.Select(testTag => testTag.Name)

من أين تتم تهيئة متغير العلامات؟ما هذا؟

ملحوظة:الرجاء تعديل السؤال نفسه، بدلاً من الرد بإجابة -- هذا ليس موضوع مناقشة، ويمكنهم إعادة ترتيب أنفسهم في أي وقت

إذا كنت تبحث عن جميع المحتويات التي تم وضع علامة عليها بأي واحدة من مجموعة العلامات:

IEnumerable<Tag> otherTags;
...
var query = from content in contentQuery
            where content.Tags.Intersection(otherTags).Any()
            select content;

يبدو أنك قد تستخدم LINQ To SQL، وفي هذه الحالة قد يكون من الأفضل أن تكتب إجراءً مخزنًا للقيام بذلك:ربما لن يتم تشغيل استخدام LINQ للقيام بذلك على SQL Server - فمن المحتمل جدًا أنه سيحاول سحب كل شيء من contentQuery وجلب كل .Tags مجموعات.يجب أن أقوم بالفعل بإعداد خادم للتحقق من ذلك.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top