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.