Dienstag, Februar 18, 2020

Vergleichen Sie in von auf null festlegbaren Typen in Linq to Sql

Habe ich eine Category-Entität, die hat eine null-ParentId-Feld. Wenn die Methode ausgeführt wird, und die Kategorie-id ist null, das Ergebnis scheint null, jedoch gibt es Kategorien, die null-ParentId mit dem Wert.

Was ist das problem hier, was bin ich?

public IEnumerable<ICategory> GetSubCategories(long? categoryId)
{
    var subCategories = this.Repository.Categories.Where(c => c.ParentId == categoryId)
        .ToList().Cast<ICategory>();

    return subCategories;
}

Übrigens, wenn ich die Bedingung auf (c.ParentId == null), das Ergebnis scheint normal.

Ich habe einen Weg gefunden,… wird aktualisiert…

InformationsquelleAutor Ali Ersöz | 2009-02-25

7 Kommentare

  1. 28

    Das erste, was zu tun ist, setzen Sie die Protokollierung, um zu sehen, was TSQL erzeugt wurde; zum Beispiel:

    ctx.Log = Console.Out;

    LINQ-to-SQL scheint zur Behandlung von null-Werten ein wenig uneinheitlich (je nach literal vs-Wert):

    using(var ctx = new DataClasses2DataContext())
    {
        ctx.Log = Console.Out;
        int? mgr = (int?)null; //redundant int? for comparison...
        //23 rows:
        var bosses1 = ctx.Employees.Where(x => x.ReportsTo == (int?)null).ToList();
        //0 rows:
        var bosses2 = ctx.Employees.Where(x => x.ReportsTo == mgr).ToList();
    }

    Also alles, was ich vorschlagen kann, ist die Verwendung der top-form mit null-Werten!

    d.h.

    Expression<Func<Category,bool>> predicate;
    if(categoryId == null) {
        predicate = c=>c.ParentId == null;
    } else {
        predicate = c=>c.ParentId == categoryId;
    }
    var subCategories = this.Repository.Categories
               .Where(predicate).ToList().Cast<ICategory>();

    Update – ich habe es funktioniert „richtig“ mithilfe einer benutzerdefinierten Expression:

        static void Main()
        {
            ShowEmps(29); //4 rows
            ShowEmps(null); //23 rows
        }
        static void ShowEmps(int? manager)
        {
            using (var ctx = new DataClasses2DataContext())
            {
                ctx.Log = Console.Out;
                var emps = ctx.Employees.Where(x => x.ReportsTo, manager).ToList();
                Console.WriteLine(emps.Count);
            }
        }
        static IQueryable<T> Where<T, TValue>(
            this IQueryable<T> source,
            Expression<Func<T, TValue?>> selector,
            TValue? value) where TValue : struct
        {
            var param = Expression.Parameter(typeof (T), "x");
            var member = Expression.Invoke(selector, param);
            var body = Expression.Equal(
                    member, Expression.Constant(value, typeof (TValue?)));
            var lambda = Expression.Lambda<Func<T,bool>>(body, param);
            return source.Where(lambda);
        }
    Es scheint, es gibt keinen besseren Weg, dies zu behandeln. Danke!
    Ich lief in genau dem gleichen problem, die gleiche Problemumgehung und wollte Fragen, ob es einen besseren Weg, es zu tun. Sieht aus wie es nicht ist 🙁 Dieses Verhalten ist wirklich counter-intuitive.
    Ich würde sagen, die Inkonsistenz zwischen literalen und Variablen schlimmer ist, dass counter-intuitive. Vielen Dank für die Bestätigung meiner Vermutung. +1
    Es sollten eigentlich behoben sein, ich habe gerade über diese erfahren und nun vermute ich, dass einige der code, den ich schrieb in früheren Projekten betroffen sein können naja, ein bisschen nervt…
    Ich vermute, dass es ist, weil die beiden Typen sind inkonsistent-ich denke, dass ist eine Monade (ericlippert.com/2013/02/25/monads-part-two) und der andere ist ein ’null-literal‘ ericlippert.com/2013/07/25/what-is-the-type-of-the-null-literal … aber ich gestehe das Beispiel macht mein Kopf drehen.

    InformationsquelleAutor Marc Gravell

  2. 53

    Anderen Weg:

    Where object.Equals(c.ParentId, categoryId)

    oder

    Where (categoryId == null ? c.ParentId == null : c.ParentId == categoryId)
    Dies funktionierte gut
    Dies funktioniert für mich perfekt. Deshalb sollten wir uns standardmäßig mit Equals(x, y) anstatt „==“ in unserem Prädikat oder gibt es noch andere Fallstricke mit Equals?

    InformationsquelleAutor ariel

  3. 6

    Müssen Sie die Verwendung von operator Gleich:

     var subCategories = this.Repository.Categories.Where(c => c.ParentId.Equals(categoryId))
            .ToList().Cast<ICategory>();

    Gleich fot nullable-Typen gibt wahr wenn:

    • Die HasValue-Eigenschaft false ist, und der andere parameter ist null. Das heißt, werden zwei null-Werte sind per definition gleich.
    • Die HasValue-Eigenschaft true ist, und der Wert, der zurückgegeben wird, indem Sie die Value-Eigenschaft ist gleich der anderen parameter.

    und gibt false wenn:

    • Die HasValue-Eigenschaft für die aktuelle Nullable-Struktur wahr ist, und der andere parameter ist null.
    • Die HasValue-Eigenschaft für die aktuelle Nullable-Struktur false ist, und der andere parameter ist nicht null.
    • Die HasValue-Eigenschaft für die aktuelle Nullable-Struktur wahr ist, und der Wert, der zurückgegeben wird, indem Sie die Value-Eigenschaft ist nicht gleich der anderen parameter.

    Mehr info hier Nullable<.T>.Equals-Methode

    Dies ist eine korrekte Antwort
    Getestet habe ich diese in LinqPad und dies scheint nicht zu funktionieren. Wenn Sie in die ’null‘ – literal, das generierte sql-tests Kategorien.ParentID IST NULL, wie man erwarten würde. Aber wenn Sie übergeben in eine variable, prüft es, Kategorien.ParentID = p0, das wird nicht funktionieren, wenn p0 ist null. Das Objekt.Equals(Kategorien.ParentID, value) Ansatz von @ariel arbeitete großartig.

    InformationsquelleAutor algreat

  4. 5

    Meine Vermutung ist, dass es sich dabei um ein eher häufiges Attribut des DBMS ist – Nur weil zwei Dinge sind, beide null bedeutet nicht, dass Sie gleich sind.

    Erarbeiten ein wenig, versuchen Sie die Ausführung dieser zwei Abfragen:

    SELECT * FROM TABLE WHERE field = NULL
    
    SELECT * FROM TABLE WHERE field IS NULL

    Den Grund für die „NULL“ – Konstrukt ist, dass die in der DBMS-Welt, NULL != NULL, da die Bedeutung der NULL ist, der Wert ist undefiniert. Da NULL ist nicht definiert, Sie kann nicht sagen, dass zwei null-Werte sind gleich, da per definition wissen Sie nicht, was Sie sind.

    Wenn Sie explizit das Häkchen für „Feld == NULL“, LINQ wahrscheinlich konvertiert, die auf „Feld“ IST NULL“. Aber wenn Sie eine variable verwenden, ich vermute, dass LINQ nicht automatisch, dass die Konvertierung.

    Hier ein MSDN-forum posten mit mehr info über dieses Problem.

    Sieht aus wie ein guter „cheat“ ist die änderung der lambda-Ausdruck wie folgt Aussehen:

    c => c.ParentId.Equals(categoryId)
    Sie können ändern das Verhalten der NULL = NULL MSSQL von set ansi-Nullen-Schalter. Siehe: msdn.microsoft.com/en-us/library/aa259229(SQL.80).aspx
    NÖ! immer noch nichts! :/
    Sie müssen mit dem Objekt.Equals(a, b) die für mich gearbeitet hat, wo ein.Equals(b) nicht

    InformationsquelleAutor Eric Petroelje

  5. 1

    Was etwas einfacheres wie dieses?

    public IEnumerable<ICategory> GetSubCategories(long? categoryId)
    {
        var subCategories = this.Repository.Categories.Where(c => (!categoryId.HasValue && c.ParentId == null) || c.ParentId == categoryId)
            .ToList().Cast<ICategory>();
    
        return subCategories;
    }

    InformationsquelleAutor Ryan Versaw

  6. 1

    Oder können Sie einfach diesen verwenden. Es wird auch zu übersetzen, um ein schöneres sql-Abfrage

    Where((!categoryId.hasValue && !c.ParentId.HasValue) || c.ParentId == categoryId)

    InformationsquelleAutor Jiří Herník

  7. 0

    Linq to Entities unterstützt Null-Coelescing (??) so einfach konvertieren Sie die null-auf-the-fly zu einem default-Wert.

    Where(c => c.ParentId == categoryId ?? 0)

    InformationsquelleAutor Kevbo

Kostenlose Online-Tests