7

Ich benutze Entity Framework 4.3.1 mit dem DbContext POCO-Ansatz gegen eine SQL Server 2012-Datenbank.Warum verhindert eine Datetime, dass eine Navigationseigenschaft geladen wird?

Ich habe nur zwei Tabellen in der Datenbank, und sie sehen wie folgt aus:

tables

Hinweis: Es sind keine Fremd in der Datenbank angegebenen Schlüssel überhaupt - ich bin nur die Durchsetzung der Beziehung in der Modell (Ich kann die Datenbank nicht ändern).

Sie haben jeweils eine Reihe von Daten, die in ihnen, die wie folgt aussieht:

data

Ich führte die folgende Abfrage die Join funktionieren würde, um sicherzustellen,

validation

Jetzt Ich habe die folgenden Einheiten:

public class Two 
{ 
    public long TwoId { get; set; } 
    public string OneId { get; set; } 
    public virtual One One { get; set; } 
} 

public class One 
{ 
    public string OneId { get; set; } 
    public DateTime DeliveryDate { get; set; } 
    public virtual ICollection<Two> Twos { get; private set; } 

    public void AddTwo(Two two) 
    { 
     if (two == null) 
      throw new ArgumentNullException("two"); 

     if (Twos == null) 
      Twos = new List<Two>(); 

     if (!Twos.Contains(two)) 
      Twos.Add(two); 

     two.One = this; 
    } 
} 

Und dies ist der Kontext:

public class TestContext : DbContext 
{ 
    public TestContext(string conectionString) 
     : base(conectionString) 
    { 
     Configuration.LazyLoadingEnabled = true; 
     Ones = Set<One>(); 
     Twos = Set<Two>(); 
    } 
    public DbSet<One> Ones { get; private set; } 
    public DbSet<Two> Twos { get; private set; } 

    protected override void OnModelCreating(DbModelBuilder modelBuilder) 
    { 
     var one = modelBuilder.Entity<One>(); 
     one.ToTable("One"); 
     one.HasKey(d => d.OneId); 

     var two = modelBuilder.Entity<Two>(); 
     two.ToTable("Two"); 
     two.HasKey(d => new 
          { 
           d.OneId, 
           d.TwoId 
          }); 
     two.Property(p => p.TwoId) 
      .HasDatabaseGeneratedOption(DatabaseGeneratedOption.None); 
     two.HasRequired(t => t.One) 
      .WithMany(o => o.Twos) 
      .HasForeignKey(o => o.OneId); 
     base.OnModelCreating(modelBuilder); 
    } 
} 

Als ich dieses Stück Code ausführen ich Why is this printed? gedruckt auf meine Konsole bekommen - was ich erwarte nicht, wie Sie, dass die Navigationseigenschaft sehen können, sollte in (I gefüllt werden auch enthalten explizit):

using (var ctx = new TestContext(@"......")) 
{ 
    const string oneId = "111348718"; 
    var one = ctx.Ones.Single(o => o.OneId.Equals(oneId)); 

    if (one != null) 
    { 
     var sdi = ctx 
      .Twos 
      .Include(s => s.One) 
      .Single(s => s.OneId.Equals(oneId)); 

     if (sdi.One == null) 
     { 
      Console.WriteLine("Why is this printed?"); 
     } 
     else 
     { 
      Console.WriteLine("This is what I expect"); 
     } 
    } 
} 

Nun dies die wirklich ungerade Bit: Wenn ich einfach die DeliveryDate Eigenschaft aus der One Klasse kommentieren sie es funktioniert gut (ich This is what I expect gedruckt t o die Konsole).

Was ist hier falsch und wie kann ich es lösen?

HINWEIS: Wenn ich an der DeliveryDate Eigenschaft auf den one Variable sehe es auf den erwarteten Wert richtig eingestellt wurde, so das Datum in der Datenbank und Entity Framework in Ordnung sein muss, ist durchaus in der Lage, es zu verwirklichen.

Weitere Informationen: Die Tatsache, dass es sich um ein Datum spielt keine Rolle - wenn es, sagen wir ein nvarchar es immer noch nicht - jede einfache Eigenschaft scheint die ganze Sache verursacht umfallen - auf das fühlt sich an wie ein Bug Ich ...

Antwort

2

OK, also habe ich auf den Grund dieser und ich denke, das ist ein Bug in Entity Framework, aber ich kann es umgehen. Hier ist, was es ist ...

Die OneId Spalte in der Tabelle Zwei hatte ein einziges Leerzeichen am Ende.

Yup - gehen Figur - wie schrecklich böse ist das !?

Also ich klopfte ein SQL-Skript, das eine LTRIM(RTRIM( auf jeder Nvarchar-Spalte in der Datenbank tat und alles funktioniert gut.

Nun, da SQL Server kümmert sich nicht um die Leerzeichen (wie durch die Abfrage mit der Join in der Frage belegt) Ich mag nicht wirklich, dass Entity Framework kümmert sich, vor allem wie es völlig komisch und inkonsequent ist hier. Sicher sollte der Punkt, wo ich Single anrufen, um die Variable sdi Variable zu bekommen geworfen haben oder die Navigationseigenschaft sollte ausgefüllt worden sein - das eine oder das andere, aber dieses Verhalten ist nur wirklich verwirrend (meiner Meinung nach) - was hat irgendeine willkürliche Eigenschaft zu tun mit dem Preis von Fisch ??

Nur der Vollständigkeit halber ist hier etwas, um dies wirklich einfach zu reproduzieren. Erstellen Sie eine neue Datenbank, und dann diesen SQL-Skript ausführen darauf:

SET ANSI_NULLS ON 
GO 

SET QUOTED_IDENTIFIER ON 
GO 

CREATE TABLE [dbo].[One](
    [OneId] [nvarchar](10) NOT NULL, 
    [SomeInt] [int] NOT NULL, 
CONSTRAINT [PK_Delivery] PRIMARY KEY CLUSTERED 
(
    [OneId] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
) ON [PRIMARY] 
GO 

CREATE TABLE [dbo].[Two](
    [TwoId] [int] NOT NULL, 
    [OneId] [nvarchar](10) NOT NULL, 
CONSTRAINT [PK_DeliveryItem] PRIMARY KEY CLUSTERED 
(
    [TwoId] ASC, 
    [OneId] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
) ON [PRIMARY] 

GO 

INSERT INTO Two(TwoId, OneId) VALUES (1, '1 ') 
INSERT INTO One(OneId, SomeInt) VALUES ('1', 1.0) 

Jetzt aC# Konsole-Anwendung erstellen, fügen Sie einen Verweis auf die EntityFramework (Version 4.3.1) und die System.Data.Entity Assemblys, setzen Sie diesen Code und führen Sie es - es wird SHOULD NOT BE PRINTED!!! drucken.

using System; 
using System.Collections.Generic; 
using System.ComponentModel.DataAnnotations; 
using System.Data.Entity; 
using System.Linq; 

namespace EFTest 
{ 
    public class Two 
    { 
     public int TwoId { get; set; } 
     public string OneId { get; set; } 
     public virtual One One { get; set; } 
    } 

    public class One 
    { 
     public string OneId { get; set; } 
     public virtual ICollection<Two> Twos { get; private set; } 

     // Comment out this property and it will work 
     public int SomeInt { get; set; } 

     public void AddTwo(Two two) 
     { 
      if (two == null) 
       throw new ArgumentNullException("two"); 

      if (Twos == null) 
       Twos = new List<Two>(); 

      if (!Twos.Contains(two)) 
       Twos.Add(two); 

      two.One = this; 
     } 
    } 

    public class Context : DbContext 
    { 
     public Context(string connectionString) 
      : base(connectionString) 
     { 
      Configuration.LazyLoadingEnabled = true; 
      Ones = Set<One>(); 
      Twos = Set<Two>(); 
     } 

     public DbSet<One> Ones { get; private set; } 
     public DbSet<Two> Twos { get; private set; } 

     protected override void OnModelCreating(DbModelBuilder modelBuilder) 
     { 
      modelBuilder 
       .Entity<One>() 
       .HasKey(d => d.OneId) 
       .ToTable("One"); 

      var two = modelBuilder.Entity<Two>(); 
      two.ToTable("Two"); 
      two.HasKey(d => new 
           { 
            d.OneId, 
            d.TwoId 
           }); 

      two.Property(d => d.TwoId) 
       .HasDatabaseGeneratedOption(DatabaseGeneratedOption.None); 

      two.HasRequired(m => m.One) 
       .WithMany(t => t.Twos) 
       .HasForeignKey(d => d.OneId); 

      base.OnModelCreating(modelBuilder); 
     } 
    } 

    internal class Program 
    { 
     private static void Main() 
     { 
      using (var ctx = new Context(@"your connection string")) 
      { 
       const string oneId = "1"; 
       var one = ctx.Ones.Single(o => o.OneId.Equals(oneId)); 

       if (one == null) 
       { 
        Console.WriteLine("No row with one ID in the database"); 
        return; 
       } 

       var two = ctx 
        .Twos 
        .Include(s => s.One) 
        .Single(s => s.OneId.Equals(oneId)); 

       Console.WriteLine(two.One == null 
             ? "SHOULD NOT BE PRINTED!!!" 
             : "SHOULD BE PRINTED"); 
      } 
     } 
    } 
} 

dann einen von ihnen:

  • Kommentieren Sie die SomeInt Eigenschaft auf der One Klasse
  • den Raum in der Datenbank wegschneiden (UPDATE Two SET OneId = RTRIM(OneId)).

Entweder wird funktionieren (offensichtlich ist das Trimmen die einzig vernünftige Lösung für den wirklichen Leben).