7

Używam Entity Framework 4.3.1 przy użyciu podejścia DbContext POCO przeciwko bazie danych SQL Server 2012.Dlaczego datetime uniemożliwi załadowanie właściwości nawigacji?

Mam tylko dwie tabele w bazie danych i wyglądają tak:

tables

UWAGA: Brak kluczy obcych określone w bazie danych w ogóle - mam egzekwowania tylko relacje w model (nie mogę zmienić bazy danych).

każdy z nich ma jeden wiersz danych w nich, który wygląda tak:

data

Wykonałem następujące zapytanie do zapewnienia przyłączenia będzie działać:

validation

teraz Mam następujące podmioty:

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; 
    } 
} 

I to jest kontekst:

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); 
    } 
} 

Gdy ten kawałek kodu otrzymuję Why is this printed? drukowane na konsolę - co nie spodziewam jak widać, że właściwość nawigacji powinny być wypełnione (I nawet jawnie włączyła ją):

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"); 
     } 
    } 
} 

teraz to jest naprawdę dziwne nieco: Gdybym po prostu skomentuj właściwość DeliveryDate z klasy One działa dobrze (mam This is what I expect nadrukiem T o konsola).

Co jest nie tak i jak mogę to rozwiązać?

UWAGA: Jeśli spojrzeć na nieruchomości DeliveryDate na one zmiennej został prawidłowo ustawiony do wartości oczekiwanej więc data musi być OK w bazie danych i ramy podmiot jest w stanie doskonale ją urzeczywistnić.

Więcej informacji: Fakt, że jest to data nie ma znaczenia - jeśli jest, powiedzmy nvarchar nadal się nie powiedzie - wydaje dowolny prosty właściwość powoduje, że całość przewrócenie - to czuje się jak robaka do ja ...

Odpowiedz

2

OK, więc doszedłem do sedna i myślę, że to jest błąd w Entity Framework, ale mogę obejść to. Oto, co to jest ...

Kolumna OneId w tabeli Dwie ma jedno miejsce na końcu.

Yup - idź - jak okropnie jest paskudnie !?

Więc zapukałem skrypt SQL, który zrobił LTRIM(RTRIM( na każdej kolumnie nvarchar w bazie danych i wszystko działa poprawnie.

Teraz, biorąc pod uwagę, że serwer SQL nie dba o spacje (co zostało potwierdzone przez zapytanie z dołączeniem do pytania), nie podoba mi się to, że Entity Framework się tym przejmuje, tym bardziej, że jest tu zupełnie dziwacznie i niekonsekwentnie. Z pewnością punkt, w którym zadzwonię pod numer Single, aby uzyskać zmienną sdi, powinien był zostać zgłoszony lub właściwość nawigacji powinna zostać wypełniona - jedna lub druga, ale to zachowanie jest po prostu bardzo mylące (według mnie) - co ma pewne arbitralne właściwości do zrobienia z ceną ryb?

Tylko ze względu na kompletność, jest to coś, co można łatwo odtworzyć. Utworzyć nową bazę danych, a następnie uruchomić ten skrypt SQL od niego:

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) 

teraz utworzyć aplikację konsoli # ac, dodać odwołanie do EntityFramework (wersja 4.3.1) i System.Data.Entity Złożenia, włóż ten kod i uruchom go - wydrukuje on SHOULD NOT BE PRINTED!!!.

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"); 
      } 
     } 
    } 
} 

następnie wykonaj jedną z nich:

  • Wykomentuj właściwość SomeInt od klasy
  • Odetnij przestrzeń w bazie danych (UPDATE Two SET OneId = RTRIM(OneId)) One.

Albo zadziała (oczywiście przycinanie jest jedyną rozsądną naprawą).

Powiązane problemy