2013-05-13 49 views
16

Chúng tôi đang sử dụng EF 5.0 làm ORM của chúng tôi trong giải pháp kinh doanh của chúng tôi, được cấu trúc theo kiểu n lớp với mọi thứ được tách rời và một gốc thành phần đẹp với ninject.Entity Framework 5 loại dữ liệu sai trong truy vấn

Gần đây, chúng tôi đã xây dựng cơ sở dữ liệu sử dụng phân vùng bên dưới và chúng tôi có một số chỉ mục quan trọng trên các cột DATE.

Các cột được khai báo chính xác trên Sql Server 2008. Chúng tôi cũng đã thêm đúng kiểu dữ liệu trong ánh xạ EF, với lệnh HasColumnType("Date").

Tuy nhiên, khi truy vấn bảng thông qua LINQ to Entities, các tham số mà chúng tôi lọc ngày được tạo kiểu DateTime2 và thậm chí các cột được đúc thành DateTime2 trong các truy vấn sao cho loại khớp với tham số.

Hành vi này có một số vấn đề. Trước hết, nếu tôi nói với EF engine rằng cột trên cơ sở dữ liệu là DATE tại sao nó phải đưa nó vào DateTime2?

Thứ hai, diễn viên này làm cho cơ sở dữ liệu bỏ qua các chỉ mục, do đó không sử dụng phân vùng. Chúng tôi có một năm cho mỗi phân đoạn phisical, và nếu tôi yêu cầu một phạm vi ngày, giả sử, tháng 2 năm 2013 đến tháng 3 năm 2013 việc quét chỉ được thực hiện trên một phân vùng vật lý. Nó hoạt động chính xác nếu sử dụng đúng kiểu dữ liệu DATE nhưng với việc truyền tới DateTime2 tất cả các phân vùng sẽ được quét, giảm hiệu suất đáng kể.

Bây giờ, tôi chắc chắn rằng tôi đang bỏ lỡ một cái gì đó, bởi vì nó sẽ là khá ngu ngốc rằng Microsoft ORM không hoạt động tốt trên Microsoft Sql Server.

Tôi không thể tìm thấy bất kỳ tài liệu nào về cách EF sử dụng đúng loại dữ liệu trong các truy vấn, vì vậy tôi hỏi tại đây. Bất kỳ trợ giúp sẽ được đánh giá cao.

Cảm ơn.

Trả lời

4

Tôi không tin rằng điều này có thể thực hiện trong Khung thực thể. This requested enhancement có thể sẽ làm những gì bạn cần. This MSDN page hiển thị ánh xạ giữa các kiểu máy chủ SQL và kiểu CLR. Lưu ý rằng date được hỗ trợ và được ánh xạ tới DateTime, nhưng vì một số loại SQL ánh xạ tới cùng một loại CLR, EF rõ ràng là chọn một loại SQL là ưu tiên eqivalent của loại CLR.

Bạn có thể bao bọc mã lựa chọn của mình trong quy trình được lưu trữ không? Nếu vậy, điều này có vẻ là một giải pháp hợp lý. Bạn có thể sử dụng DbSet{T}.SqlQuery để materialize đối tượng từ thực hiện sp.

Mã mẫu

Ứng dụng bảng điều khiển ngắn sau đây minh họa khái niệm. Lưu ý cách các thực thể liên quan được tải thành công.

using System; 
using System.Collections.Generic; 
using System.Collections.ObjectModel; 
using System.ComponentModel.DataAnnotations; 
using System.ComponentModel.DataAnnotations.Schema; 
using System.Data.Entity; 
using System.Data.SqlClient; 
using System.Linq; 

namespace ConsoleApplication1 
{ 
    [Table("MyEntity")]  
    public class MyEntity 
    { 
     private Collection<MyRelatedEntity> relatedEntities; 

     [Key] 
     public virtual int MyEntityId { get; set; } 

     [DataType(DataType.Date)] 
     public virtual DateTime MyDate { get; set; } 

     [InverseProperty("MyEntity")] 
     public virtual ICollection<MyRelatedEntity> RelatedEntities 
     { 
      get 
      { 
       if (this.relatedEntities == null) 
       { 
        this.relatedEntities = new Collection<MyRelatedEntity>(); 
       } 

       return this.relatedEntities; 
      } 
     } 

     public override string ToString() 
     { 
      return string.Format("Date: {0}; Related: {1}", this.MyDate, string.Join(", ", this.RelatedEntities.Select(q => q.SomeString).ToArray())); 
     } 
    } 

    public class MyRelatedEntity 
    { 
     [Key] 
     public virtual int MyRelatedEntityId { get; set; } 

     public virtual int MyEntityId { get; set; } 

     [ForeignKey("MyEntityId")] 
     public virtual MyEntity MyEntity { get; set; } 

     public virtual string SomeString { get;set;} 
    } 

    public class MyContext : DbContext 
    { 
     public DbSet<MyEntity> MyEntities 
     { 
      get { return this.Set<MyEntity>(); } 
     } 
    } 

    class Program 
    { 
     const string SqlQuery = @"DECLARE @date date; SET @date = @dateIn; SELECT * FROM MyEntity WHERE MyDate > @date"; 

     static void Main(string[] args) 
     { 
      Database.SetInitializer(new DropCreateDatabaseAlways<MyContext>()); 

      using (MyContext context = new MyContext()) 
      { 
       context.MyEntities.Add(new MyEntity 
        { 
         MyDate = DateTime.Today.AddDays(-2), 
         RelatedEntities = 
         { 
          new MyRelatedEntity { SomeString = "Fish" }, 
          new MyRelatedEntity { SomeString = "Haddock" } 
         } 
        }); 

       context.MyEntities.Add(new MyEntity 
       { 
        MyDate = DateTime.Today.AddDays(1), 
        RelatedEntities = 
         { 
          new MyRelatedEntity { SomeString = "Sheep" }, 
          new MyRelatedEntity { SomeString = "Cow" } 
         } 
       }); 

       context.SaveChanges(); 
      } 

      using (MyContext context = new MyContext()) 
      { 
       IEnumerable<MyEntity> matches = context.MyEntities.SqlQuery(
        SqlQuery, 
        new SqlParameter("@dateIn", DateTime.Today)).ToList(); 

       // The implicit ToString method call here invokes lazy-loading of the related entities. 
       Console.WriteLine("Count: {0}; First: {1}.", matches.Count(), matches.First().ToString()); 
      } 

      Console.Read(); 
     } 
    } 
} 
+0

Tôi đang sử dụng tải chậm trên một tấn tài sản điều hướng. Tôi sẽ cần phải háo hức tải mọi thứ và tự tạo đồ thị đối tượng của tôi, rất phức tạp và sâu sắc, vì vậy đó không phải là một lựa chọn. Có thể chuyển sang một ORM khác như NHibernate sẽ là lựa chọn đúng đắn. –

+0

@MatteoMosca - Tôi không nghĩ đó là trường hợp, trừ khi tôi đã hiểu lầm vấn đề. EF cho phép bạn thực hiện các kết quả của một sp như các thực thể * đính kèm * ("được theo dõi bởi ngữ cảnh" theo MSDN), do đó tải chậm nên hoạt động. Những gì bạn không thể làm là hỗ trợ * háo hức * tải bằng cách sử dụng phương pháp này. – Olly

+0

Điều đó có thể thú vị. Tôi nghĩ rằng tôi đã bỏ lỡ tính năng này. Tôi sẽ xem xét nó càng sớm càng tốt. –

2

Tôi không có giải pháp. Tôi chưa bao giờ nhìn thấy một truy vấn LINQ-to-Entites với tham số .NET DateTime có liên quan đã sử dụng một kiểu tham số trong truy vấn SQL khác với datetime2(7). Tôi nghi ngờ rằng bạn có thể thoát khỏi điều đó. Chỉ cần cố gắng giải thích lý do vì sao:

Giả sử bạn có một pháp nhân có tài sản SomeNumber loại int. Kết quả những gì bạn mong chờ cho một truy vấn như thế này:

....Where(e => e.SomeNumber >= 7.3).... 

Có lẽ tất cả các đơn vị nơi SomeNumber8 hoặc cao hơn. Nếu tham số (số thập phân dấu chấm động) 7.3 sẽ được chuyển sang loại int được lưu trữ trong cơ sở dữ liệu bạn phải quyết định cách làm tròn 7.3 - đến 7 (sẽ dẫn đến kết quả sai) hoặc 8? OK, bạn có thể nói, vì truy vấn của tôi cho biết >= và tôi biết loại trong DB là một số nguyên, làm tròn thành 8 phải chính xác. Nếu tôi sử dụng <= thì làm tròn thành 7 phải chính xác. Nếu tôi sử dụng ==, oh ... tôi không được làm tròn chút nào hoặc tôi biết rằng kết quả phải trống và tôi có thể dịch trực tiếp mệnh đề này Where sang false. Và != đến true. Nhưng một tham số của 7.0 là một trường hợp đặc biệt. Vv ....

Vâng, tiến thoái lưỡng nan trong ví dụ này có một giải pháp dễ dàng: Quyết định phía khách hàng những gì bạn muốn bằng cách sử dụng thông số int (7 hoặc 8) ở nơi đầu tiên.

Giải pháp với DateTime không đơn giản như vậy vì .NET không có loại Date. Truy vấn với DateTime thông số sẽ luôn luôn có hình thức ...

DateTime dateTime = new DateTime(2013, 5, 13, 10, 30, 0); 
....Where(e => e.SomeDateTime >= dateTime).... 

... và nếu SomeDateTime được lưu giữ như date trong SQL Server bạn có một lần nữa tiến thoái lưỡng nan tròn. Tôi có phải truyền tới 2013.05.13 hoặc 2013.05.14 không? Đối với truy vấn phía trên máy khách, chắc chắn sẽ mong đợi tất cả các thực thể có ngày 14 và sau đó.

Vâng, bạn có thể làm điều đó thông minh, như: nếu phần thời gian của thông số DateTime của tôi là nửa đêm, hãy truyền đến phần ngày. Nếu tôi sử dụng >= truyền vào ngày tiếp theo, v.v., v.v. Hoặc bạn luôn có thể truyền tới datetime2(7). Sau đó, kết quả của truy vấn luôn chính xác và vì máy khách (.NET) mong đợi nó. Đúng ... nhưng có lẽ với việc sử dụng chỉ số tối ưu.

+0

Tôi không hiểu tại sao ví dụ đầu tiên của bạn lại là vấn đề. SQL Server đã cho phép lọc một cột số nguyên bằng cách sử dụng một giá trị không nguyên. Xem xét tập lệnh này: https: //gist.github.com/kappa7194/5574037 Tôi có thể lọc 'MyColumn' bằng cách sử dụng một phao, và SQL Server không đúc cột vào một phao để làm điều đó: https://gist.github.com/kappa7194/5574040 vậy tại sao nó vặn nó lên khi Entity Framework có liên quan? – Albireo

+0

Giải thích của bạn là rõ ràng, nhưng tôi vẫn nghĩ rằng một cái gì đó là mất tích. Nếu SQL có kiểu ngày, EF có thể bỏ qua phần thời gian của đối tượng DateTime .Net. Nó thậm chí còn có một thuộc tính .Date trả về một DateTime với thời gian nửa đêm. Bạn có thể gợi ý rằng chúng ta nên sử dụng trong cơ sở dữ liệu của chúng tôi chỉ các loại được tuân thủ .Net? Điều đó có vẻ khá hạn chế. –

+0

Đây là ví dụ phù hợp hơn: https://gist.github.com/kappa7194/5574997 Ở đây SQL Server thực hiện 'Tìm kiếm chỉ mục theo cụm 'tìm kiếm các giá trị' GT' 'DATETIME' mà tôi đã chỉ định, tôi nghĩ khung Entity sẽ đơn giản chuyển các giá trị cho cơ sở dữ liệu bên dưới (nếu nó hỗ trợ chúng) và để nó làm việc của nó. – Albireo

4

Phạm vi loại DateTime trong máy chủ .NET và SQL khác nhau.

NET DateTime Phạm vi đó là: 0000-Jan-01-9999-Dec-31 loạt SQL DateTime là: 1900-Jan-01 2079-Jun-06

Để phù hợp với phạm vi, EF chuyển đổi của bạn .NET DateTime đến máy chủ SQL Kiểu DateTime2 có phạm vi tương tự như phạm vi ngày giờ .NET.

Tôi cho rằng sự cố của bạn chỉ xảy ra khi bạn có thuộc tính ngày không được gán và chuyển cho máy chủ SQL qua EF. Khi ngày không được gán với giá trị cụ thể, nó là mặc định để DateTime.Min đó là 0000-Jan-01 và đó là gây ra việc chuyển đổi để DateTime2.

Tôi nghĩ bạn có thể làm cho thuộc tính DateTime của bạn không thể dùng được -> DateTime? hoặc viết một trình trợ giúp để chuyển đổi DateTime.Min của bạn để đáp ứng phạm vi ngày giờ SQL.

Hy vọng điều này sẽ hữu ích.

+1

Thực ra, không phải vậy. Một ví dụ về vấn đề của chúng tôi liên quan đến tìm kiếm bằng cách sử dụng hai ngày làm phạm vi tìm kiếm. Các cột trên db là DATE không phải DateTime hoặc DateTime2. Tôi gửi cho EF hai giá trị DateTime hợp lệ .net, hoàn toàn trong phạm vi (ví dụ: tháng 1 năm 2013 và có thể 1 21013) và chuyển đổi vẫn xảy ra. –

Các vấn đề liên quan