Soru Varlıklara LINQ - Çoktan fazla ilişki içindeki koleksiyonları test eden cümlelerin bulunduğu bina


Yani, Linq varlık çerçevesini kullanıyorum. 2 varlığım var: Content ve Tag. Onlar birbirleriyle çoktan çoğa ilişki içinde. Content çok olabilir Tags ve Tag çok olabilir Contents. Bu yüzden, herhangi bir etiket adının eşit olduğu tüm içeriği seçmek için bir sorgu yazmaya çalışıyorum. blah

Varlıkların her ikisi de bir varlık olarak diğer varlığın bir koleksiyonuna sahiptir (ancak kimliksiz). Bu benim mücadele ettiğim yer. İçin özel bir ifadem var Contains (yani, bana kim yardım edebilirse, bir koleksiyon için "içerir" yapabileceğimi varsayabilirsin). Bu ifadeyi aldım: http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2670710&SiteID=1

1'i düzenle

Kendi cevabımı bulmayı bitirdim.


18
2017-09-21 05:28


Menşei




Cevaplar:


Hakkında okuduktan sonra PredicateBuilderİnsanların bana gönderdiği, diğer sitelere gönderdiği harika mesajların hepsini okumak ve daha fazlasını okumak Öngörüler birleştiriliyor ve Kanonik Fonksiyon Haritalama.. oh ve ben birazdan aldım LINQ sorgularında arama işlevleri (Bu sınıflardan bazıları bu sayfalardan alınmıştır).

NEDEN bir çözüm var! Her ne kadar parçalanmış bir parça olsa da ...

Hacked parçası ile olsun :(

Reflektör kullanmak ve dahili olarak işaretlenmiş ExpressionVisitor sınıfını kopyalamak zorunda kaldım. Daha sonra işe geçmek için bazı küçük değişiklikler yapmak zorunda kaldım. İki istisna oluşturmam gerekiyordu (çünkü iç istisnaları yeniliyordu. Ayrıca ReadOnlyCollection () yönteminin dönüşünü değiştirmek zorunda kaldım:

return sequence.ToReadOnlyCollection<Expression>();

Kime:

return sequence.AsReadOnly();

Dersi gönderirdim, ama oldukça büyük ve bu yazıyı zaten olduğundan daha fazla karıştırmak istemiyorum. Gelecekte bu sınıfın kütüphanemden kaldırılabileceğini ve Microsoft'un bunu halka açacağını umuyorum. Hareketli...

ParameterRebinder sınıfı ekledim:

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

Sonra bir ExpressionExtensions sınıfı ekledim:

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

Ve eklediğim son ders PredicateBuilder oldu:

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

}

Bu benim sonucum ... Bu kodu yürütebildim ve aradığım etiketlerden eşleşen "etiket" varlıkları olan sonuçta ortaya çıkan "içerik" varlıkları geri aldım!

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

Bunun için size bir dosya göndermemi isterseniz veya sadece daha fazla bilgiye ihtiyacım varsa, lütfen aynı şeyle ilgili yardıma ihtiyaç duyarsa lütfen bana bildirin.


18
2017-09-25 05:04



Saldırıya uğramış ExpressionVisitor sürümünüzü yapıştırmak için pastebin.com veya gist.github.com'u kullanabilirsiniz, sınıf bazılarına yararlı olabilir :) - chakrit
Kodun bir kopyasını alabilir miyim. Bana e-posta gönderebilirsiniz mikemurf22@hotmail.com. E-postanı bulamadım. - mikemurf22
Evet, evet, evet. - John Gietzen


Özetlemek ...

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

Yani beklediğiniz şey bu mu?

Biraz kafam karıştı.


3
2017-09-21 17:52



Evet ... bu ihtiyaç duyduğum hatlar boyunca ... Şimdi bir hata alıyorum:> 'Kapanış tipi' tipi sabit bir değer oluşturulamıyor. Bu bağlamda yalnızca ilkel türler ('Int32, String ve Guid' gibi) desteklenir. Gelen: .Any (t => t.Name == tag.Name) - Phobis
Ben de aynı hatayı alıyorum Phobis. - Victor Rodrigues


Sorunun kendisi şöyle soruyor:

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

Düşünce sürecinin, sorucunun koduna ulaşmak için ne olduğundan emin değilim ve gerçekten tam olarak ne yaptığından emin değilim. Gerçekten emin olduğum bir şey .AsQueryable () çağrısı tamamen gereksiz - ya .Tags zaten bir IQueryable, ya da .AsQueryable () sadece sizin için sahte olacak - nerede fazladan aramalar ekleyerek Orada olması gerekmiyor.


2
2017-09-21 06:01





Hata, 'etiketler' değişkeniyle ilgilidir. LINQ to Entities, değerlerin bir toplamı olan bir parametreyi desteklemez. Basitçe tag.AsQueryable () - bir ealier yanıtında önerildiği gibi - varsayılan bellek içi LINQ sorgu sağlayıcısı varlıkları (veya diğer ilişkisel sağlayıcıları) LINQ ile uyumlu olmadığı için çalışmaz.

Çözüm olarak, API'yi ifade API'sini kullanarak manuel olarak oluşturabilirsiniz (bkz. bu forum gönderisi) ve aşağıdaki gibi uygulayın:

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

2
2017-09-24 12:27



Bu tür bir yardım ... ama yine de bu işe yarayan bir çözüm bulamadım :( - Phobis


tags.Select(testTag => testTag.Name)

Etiketler değişkeni nereden başlıyor? Bu ne?


1
2017-09-21 05:49



Oh, pardon .. Bu sade bir IList olarak geçer <Tag>. - Phobis


NOT: Lütfen bir cevabı yanıtlamak yerine, soruyu kendiniz düzenleyin - bu bir tartışma dizisi değildir ve herhangi bir zamanda kendilerini yeniden düzenleyebilirler.

Bir etiket kümesinden herhangi biri ile işaretlenmiş tüm İçeriği arıyorsanız:

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

LINQ To SQL kullanıyor olabilirsiniz, bu durumda bunu yapmak için saklı bir yordam yazmanız daha iyi olabilir: Bunu yapmak için LINQ kullanarak muhtemelen SQL Server'da çalışmaz - büyük olasılıkla denemesi her şeyi aşağı çekmek contentQuery ve hepsini getir .Tags koleksiyonları. Yine de kontrol etmek için bir sunucu kurmak zorundayım.


0
2017-09-22 04:42



SQL için LINQ kullanmıyorum, varlıklar LINQ. - Phobis
Bunu yaparsam hata alıyorum: "Kapanış tipi" türünde sabit bir değer oluşturulamıyor. Bu bağlamda yalnızca ilkel türler ('Int32, String ve Guid' gibi) desteklenir. - Phobis