[.net] Comment puis-je demander des valeurs nulles dans le cadre d'entité?


Answers

Depuis Entity Framework 5.0, vous pouvez utiliser le code suivant pour résoudre votre problème:

public abstract class YourContext : DbContext
{
  public YourContext()
  {
    (this as IObjectContextAdapter).ObjectContext.ContextOptions.UseCSharpNullComparisonBehavior = true;
  }
}

Ceci devrait résoudre vos problèmes car Entity Framerwork utilisera la comparaison nulle de 'C # like'.

Question

Je veux exécuter une requête comme celle-ci

   var result = from entry in table
                     where entry.something == null
                     select entry;

et obtenir un IS NULL généré.

Edité: Après les deux premières réponses, je ressens le besoin de clarifier que j'utilise Entity Framework et non Linq to SQL. La méthode object.Equals () ne semble pas fonctionner dans EF.

Modifier n ° 2: La requête ci-dessus fonctionne comme prévu. Il génère correctement IS NULL . Mon code de production était cependant

value = null;
var result = from entry in table
                         where entry.something == value
                         select entry;

et le SQL généré était something = @p; @p = NULL something = @p; @p = NULL . Il semble que EF traduise correctement l'expression constante mais si une variable est impliquée, elle la traite comme une comparaison normale. Cela a du sens en réalité. Je vais fermer cette question




Soulignant que toutes les suggestions d'Entity Framework <6.0 génèrent un SQL maladroit. Voir le deuxième exemple de correction "propre".

Solution ridicule

// comparing against this...
Foo item = ...

return DataModel.Foos.FirstOrDefault(o =>
    o.ProductID == item.ProductID
    // ridiculous < EF 4.5 nullable comparison workaround http://.com/a/2541042/1037948
    && item.ProductStyleID.HasValue ? o.ProductStyleID == item.ProductStyleID : o.ProductStyleID == null
    && item.MountingID.HasValue ? o.MountingID == item.MountingID : o.MountingID == null
    && item.FrameID.HasValue ? o.FrameID == item.FrameID : o.FrameID == null
    && o.Width == w
    && o.Height == h
    );

résultats dans SQL comme:

SELECT TOP (1) [Extent1].[ID]                 AS [ID],
       [Extent1].[Name]               AS [Name],
       [Extent1].[DisplayName]        AS [DisplayName],
       [Extent1].[ProductID]          AS [ProductID],
       [Extent1].[ProductStyleID]     AS [ProductStyleID],
       [Extent1].[MountingID]         AS [MountingID],
       [Extent1].[Width]              AS [Width],
       [Extent1].[Height]             AS [Height],
       [Extent1].[FrameID]            AS [FrameID],
FROM   [dbo].[Foos] AS [Extent1]
WHERE  (CASE
  WHEN (([Extent1].[ProductID] = 1 /* @p__linq__0 */)
        AND (NULL /* @p__linq__1 */ IS NOT NULL)) THEN
    CASE
      WHEN ([Extent1].[ProductStyleID] = NULL /* @p__linq__2 */) THEN cast(1 as bit)
      WHEN ([Extent1].[ProductStyleID] <> NULL /* @p__linq__2 */) THEN cast(0 as bit)
    END
  WHEN (([Extent1].[ProductStyleID] IS NULL)
        AND (2 /* @p__linq__3 */ IS NOT NULL)) THEN
    CASE
      WHEN ([Extent1].[MountingID] = 2 /* @p__linq__4 */) THEN cast(1 as bit)
      WHEN ([Extent1].[MountingID] <> 2 /* @p__linq__4 */) THEN cast(0 as bit)
    END
  WHEN (([Extent1].[MountingID] IS NULL)
        AND (NULL /* @p__linq__5 */ IS NOT NULL)) THEN
    CASE
      WHEN ([Extent1].[FrameID] = NULL /* @p__linq__6 */) THEN cast(1 as bit)
      WHEN ([Extent1].[FrameID] <> NULL /* @p__linq__6 */) THEN cast(0 as bit)
    END
  WHEN (([Extent1].[FrameID] IS NULL)
        AND ([Extent1].[Width] = 20 /* @p__linq__7 */)
        AND ([Extent1].[Height] = 16 /* @p__linq__8 */)) THEN cast(1 as bit)
  WHEN (NOT (([Extent1].[FrameID] IS NULL)
             AND ([Extent1].[Width] = 20 /* @p__linq__7 */)
             AND ([Extent1].[Height] = 16 /* @p__linq__8 */))) THEN cast(0 as bit)
END) = 1

Solution de contournement outrancière

Si vous voulez générer du SQL plus propre, quelque chose comme:

// outrageous < EF 4.5 nullable comparison workaround http://.com/a/2541042/1037948
Expression<Func<Foo, bool>> filterProductStyle, filterMounting, filterFrame;
if(item.ProductStyleID.HasValue) filterProductStyle = o => o.ProductStyleID == item.ProductStyleID;
else filterProductStyle = o => o.ProductStyleID == null;

if (item.MountingID.HasValue) filterMounting = o => o.MountingID == item.MountingID;
else filterMounting = o => o.MountingID == null;

if (item.FrameID.HasValue) filterFrame = o => o.FrameID == item.FrameID;
else filterFrame = o => o.FrameID == null;

return DataModel.Foos.Where(o =>
    o.ProductID == item.ProductID
    && o.Width == w
    && o.Height == h
    )
    // continue the outrageous workaround for proper sql
    .Where(filterProductStyle)
    .Where(filterMounting)
    .Where(filterFrame)
    .FirstOrDefault()
    ;

aboutit à ce que vous vouliez en premier lieu:

SELECT TOP (1) [Extent1].[ID]                 AS [ID],
           [Extent1].[Name]               AS [Name],
           [Extent1].[DisplayName]        AS [DisplayName],
           [Extent1].[ProductID]          AS [ProductID],
           [Extent1].[ProductStyleID]     AS [ProductStyleID],
           [Extent1].[MountingID]         AS [MountingID],
           [Extent1].[Width]              AS [Width],
           [Extent1].[Height]             AS [Height],
           [Extent1].[FrameID]            AS [FrameID],
FROM   [dbo].[Foos] AS [Extent1]
WHERE  ([Extent1].[ProductID] = 1 /* @p__linq__0 */)
   AND ([Extent1].[Width] = 16 /* @p__linq__1 */)
   AND ([Extent1].[Height] = 20 /* @p__linq__2 */)
   AND ([Extent1].[ProductStyleID] IS NULL)
   AND ([Extent1].[MountingID] = 2 /* @p__linq__3 */)
   AND ([Extent1].[FrameID] IS NULL)



Personnellement, je préfère:

var result = from entry in table    
             where (entry.something??0)==(value??0)                    
              select entry;

plus de

var result = from entry in table
             where (value == null ? entry.something == null : entry.something == value)
             select entry;

parce que cela empêche la répétition - bien que ce ne soit pas mathématiquement exact, mais cela correspond bien à la plupart des cas.




pour faire face aux comparaisons Null, utilisez Object.Equals() au lieu de ==

vérifier cette reference




Malheureusement, dans Entity Framework 5 DbContext, le problème n'est toujours pas résolu.

J'ai utilisé cette solution de contournement (fonctionne avec MSSQL 2012 mais le paramètre ANSI NULLS peut être déconseillé dans toute future version de MSSQL).

public class Context : DbContext
{

    public Context()
        : base("name=Context")
    {
        this.Database.Connection.StateChange += Connection_StateChange;
    }

    void Connection_StateChange(object sender, System.Data.StateChangeEventArgs e)
    {
        // Set ANSI_NULLS OFF when any connection is opened. This is needed because of a bug in Entity Framework
        // that is not fixed in EF 5 when using DbContext.
        if (e.CurrentState == System.Data.ConnectionState.Open)
        {
            var connection = (System.Data.Common.DbConnection)sender;
            using (var cmd = connection.CreateCommand())
            {
                cmd.CommandText = "SET ANSI_NULLS OFF";
                cmd.ExecuteNonQuery();
            }
        }
    }
}

Il convient de noter que c'est une solution de contournement sale mais il est celui qui peut être mis en œuvre très rapidement et fonctionne pour toutes les requêtes.




var result = from entry in table    
             where entry.something == value||entry.something == null                   
              select entry;

Utiliser ça




Si c'est un type nullable, peut-être essayer d'utiliser la propriété HasValue?

var result = from entry in table
                 where !entry.something.HasValue
                 select entry;

N'ayez pas d'EF à tester ici ... juste une suggestion =)




Related