LINQ İfadesinde String.IsNullOrWhiteSpace


151

Takip koduna sahibim:

return this.ObjectContext.BranchCostDetails.Where(
    b => b.TarrifId == tariffId && b.Diameter == diameter
        || (b.TarrifId==tariffId && !string.IsNullOrWhiteSpace(b.Diameter))
        || (!b.TarrifId.HasValue) && b.Diameter==diameter);

Ve kodu çalıştırmayı denediğimde bu hatayı alıyorum:

LINQ to Entities, 'Boolean IsNullOrWhiteSpace (System.String)' yöntemini tanımıyor ve bu yöntem bir mağaza ifadesine dönüştürülemiyor. "

Bu sorunu nasıl çözebilir ve kodu bundan daha iyi yazabilirim?

Yanıtlar:


263

Değiştirmeniz gerekiyor

!string.IsNullOrWhiteSpace(b.Diameter)

ile

!(b.Diameter == null || b.Diameter.Trim() == string.Empty)

Linq to Entities için bu çeviri:

DECLARE @p0 VarChar(1000) = ''
...
WHERE NOT (([t0].[Diameter] IS NULL) OR (LTRIM(RTRIM([t0].[Diameter])) = @p0))

ve Linq to SQL için neredeyse aynı değil

DECLARE @p0 NVarChar(1000) = ''
...
WHERE NOT (LTRIM(RTRIM([t0].[TypeName])) = @p0)

3
Neden? Bu kod derler:List<string> my = new List<string>(); var i = from m in my where !string.IsNullOrWhiteSpace(m) select m;
Eric J.

37
Derlenebilir, ancak Linq tarafından varlıklara SQL'ye çevrilmez. 'Boolean IsNullOrWhiteSpace (System.String)' yönteminin SQL için desteklenen bir çevirisi yok. Aynısı IsNullOrEmpty için de geçerlidir.
Phil

1
Aynı şey Linq to SQL için de geçerlidir
Phil

3
Dikkatli bir kelime: 'string.mpty' over '"(boş string olarak da bilinir) kullanmak çok önemlidir. Birincisi ikincisi işe yaramaz (en azından Oracle'ın EF sürücüsü söz konusu olduğunda). Aka kullanırsanız: b.Diameter.Trim () == "" <- bu amaçlandığı gibi çalışmaz (deli biliyorum ...)
XDS

Trim () de en azından MongoDB kullanan sorgularda desteklenmiyor gibi görünüyor.Driver
Leandro hereñu

20

Bu durumda IQueryable<T>ve arasında ayrım yapmak önemlidir IEnumerable<T>. Kısacası IQueryable<T>, optimize edilmiş bir sorgu sunmak için bir LINQ sağlayıcısı tarafından işlenir. Bu dönüştürme sırasında tüm C # ifadeleri desteklenmez, çünkü bunları arka uç özel bir sorguya (örn. SQL) çevirmek mümkün değildir veya uygulayıcı ifadeye olan ihtiyacı öngörmediğinden.

Buna karşılık IEnumerable<T>, somut nesnelere karşı yürütülür ve bu nedenle dönüştürülmez. Yani, kullanım alanlarıdır yapılar, oldukça yaygındır IEnumerable<T>, birlikte kullanılamaz IQueryable<T>ve ayrıca bu IQueryables<T>işlevlerin aynı kümesini desteklemeyen farklı LINQ sağlayıcıları tarafından desteklenmektedir.

Ancak, sorguyu değiştiren bazı geçici çözümler ( Phil'in yanıtı gibi ) vardır. Ayrıca, daha genel bir yaklaşım IEnumerable<T>olarak, sorgunun spesifikasyonuna devam etmeden önce bir önceye geri dönmek mümkündür . Bununla birlikte, bu, özellikle kısıtlamalarda (örn. Maddeler nerede) kullanıldığında bir performans isabetine sahip olabilir. Buna karşılık, dönüşümlerle uğraşırken, isabet performansı çok daha küçük, hatta bazen yok - sorgunuza bağlı olarak.

Yukarıdaki kod da şu şekilde yeniden yazılabilir:

return this.ObjectContext.BranchCostDetails
    .AsEnumerable()
    .Where(
        b => b.TarrifId == tariffId && b.Diameter == diameter
        || (b.TarrifId==tariffId && !string.IsNullOrWhiteSpace(b.Diameter))
        ||(!b.TarrifId.HasValue) && b.Diameter==diameter
    );

NOT: Bu kodun Phil'in cevabından daha yüksek bir performans etkisi olacaktır . Ancak prensibi gösterir.


10

String.IsNullOrWhiteSpace referanslarını algılamak ve daha basit bir ifadeye ayırmak için bir ifade ziyaretçisi kullanın (x == null || x.Trim() == string.Empty).

Aşağıda, genişletilmiş bir ziyaretçi ve bundan yararlanmak için bir genişletme yöntemi verilmiştir. Kullanmak için özel bir yapılandırma gerektirmez, Where yerine WhereEx'i arayın.

public class QueryVisitor: ExpressionVisitor
{
    protected override Expression VisitMethodCall(MethodCallExpression node)
    {
        if (node.Method.IsStatic && node.Method.Name == "IsNullOrWhiteSpace" && node.Method.DeclaringType.IsAssignableFrom(typeof(string)))
        {
            //!(b.Diameter == null || b.Diameter.Trim() == string.Empty)
            var arg = node.Arguments[0];
            var argTrim = Expression.Call(arg, typeof (string).GetMethod("Trim", Type.EmptyTypes));

            var exp = Expression.MakeBinary(ExpressionType.Or,
                    Expression.MakeBinary(ExpressionType.Equal, arg, Expression.Constant(null, arg.Type)),
                    Expression.MakeBinary(ExpressionType.Equal, argTrim, Expression.Constant(string.Empty, arg.Type))
                );

            return exp;
        }

        return base.VisitMethodCall(node);
    }
}

public static class EfQueryableExtensions
{
    public static IQueryable<T> WhereEx<T>(this IQueryable<T> queryable, Expression<Func<T, bool>> where)
    {
        var visitor = new QueryVisitor();
        return queryable.Where(visitor.VisitAndConvert(where, "WhereEx"));
    }
}

Yani çalıştırırsanız myqueryable.WhereEx(c=> !c.Name.IsNullOrWhiteSpace())o dönüştürülecektir !(c.Name == null || x.Trim() == "")(sql / kuruluşlara linq) ne olursa olsun olmanın geçiş öncesi ve sql dönüştürülür.


Bir tür basit gereksinimi için Phil'in cevap daha karmaşık, ancak ExpressionVisitor ilgili eğitim amaçlı çok ilginç bir sürü, teşekkür
AFract

2

Boşluğu kontrol etmek için bunu da kullanabilirsiniz:

b.Diameter!=null && !String.IsNullOrEmpty(b.Diameter.Trim())

6
çap boşsa bu bir istisna oluşturur.
Okan Kocyigit

@OkanKocyigit Haklısın. Cevabı düzenledim. :)
Majid

0
!String.IsNullOrEmpty(b.Diameter.Trim()) 

eğer özel durum oluşturur b.Diameterolduğunu null.
İfadenizi hala kullanmak istiyorsanız, bu kontrolü daha iyi kullanın

!String.IsNullOrWhiteSpace(b.Diameter), IsNullOrWhiteSpace = IsNullOrEmpty + WhiteSpace

2
StackOverflow'a hoş geldiniz! Her şeyden önce, SO'ya bir cevaplayıcı olarak katıldığınız için teşekkür ederiz. Net ve okunması kolay bir cevap oluşturmak için lütfen biçimlendirmeye bakın .
Hille
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.