簡體   English   中英

實體框架5查詢中的數據類型錯誤

[英]Entity Framework 5 wrong data type in query

我們在業務解決方案中使用EF 5.0作為我們的首選ORM,以n層方式構建,所有內容都解耦,並且使用ninject構建了一個很好的組合根。

最近,我們一直在構建一個使用下面的分區的數據庫,我們在DATE列上有一些重要的索引。

這些列在Sql Server 2008上正確聲明。我們還在EF映射中使用HasColumnType("Date")指令添加了正確的數據類型。

仍然,當通過Linq to Entities查詢表時,我們過濾日期的參數是按類型DateTime2創建的,甚至列也會在查詢中強制轉換為DateTime2 ,因此類型與參數匹配。

這種行為有幾個問題。 首先,如果我告訴EF引擎數據庫中的列是DATE為什么要將它轉換為DateTime2

其次,此強制轉換使數據庫忽略索引,因此不使用分區。 我們每個物理分區有一年,如果我問一個日期范圍,比方說,2013年2月到2013年3月,掃描應該僅在一個物理分區上進行。 如果手動使用正確的數據類型DATE但是使用強制轉換為DateTime2所有分區都會被掃描,從而大大降低了性能。

現在,我確定我錯過了一些東西,因為微軟ORM在Microsoft Sql Server上不能正常工作會相當愚蠢。

我一直無法找到有關EF如何在查詢中使用正確數據類型的任何文檔,所以我在這里問。 任何幫助將不勝感激。

謝謝。

.NET和SQL Server中的DateTime類型的范圍是不同的。

.NET DateTime范圍是:0000-Jan-01到9999-Dec-31 SQL DateTime范圍是:1900-Jan-01,2079-Jun-06

為了匹配范圍,EF將.NET DateTime轉換為SQL Server DateTime2類型,其范圍與.NET DateTime范圍相同。

我認為只有在沒有分配日期屬性並通過EF傳遞給SQL服務器時才會出現問題。 如果未使用特定值分配日期,則默認為DateTime.Min,即0000-Jan-01,這將導致轉換為DateTime2。

我想你可以使你的DateTime屬性可以為空 - > DateTime? 或編寫幫助程序來轉換DateTime.Min以滿足SQL DateTime范圍。

希望,這有幫助。

我不相信這在實體框架中是可行的。 這個要求的增強可能會做你需要的。 此MSDN頁面顯示SQL Server類型和CLR類型之間的映射。 請注意,支持date並映射到DateTime ,但由於多個SQL類型映射到相同的CLR類型,因此EF顯然選擇一種SQL類型作為CLR類型的首選等效項。

你可以將選擇代碼包裝在存儲過程中嗎? 如果是這樣,這似乎是一個合理的解決方案。 您可以使用DbSet {T} .SqlQuery來實現sp的執行。

代碼示例

以下簡短控制台應用程序演示了該概念。 注意相關實體是如何成功延遲加載的。

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

我沒有解決方案。 我從未見過涉及.NET DateTime參數的LINQ-to-Entites查詢,該參數在除datetime2(7)之外的SQL查詢中使用了參數類型。 我懷疑你可以擺脫它。 試着解釋它為什么會這樣:

假設您的實體具有類型為int的屬性SomeNumber 對於像這樣的查詢,您期望得到什么結果:

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

可能是SomeNumber8或更大的所有實體。 如果(浮點十進制)參數7.3將被轉換為存儲在數據庫中的int類型,則必須決定如何將7.3 - 7 (將導致錯誤結果)或8 好吧,你可以說,因為我的查詢說>=並且我知道DB中的類型是整數,舍入到8必須是正確的。 如果我使用<= ,那么舍入到7必須是正確的。 如果我會使用== ,哦......我一定不能完全舍入或者我知道結果必須為空,我可以直接將這個Where子句翻譯為false 而且!= true 7.0的參數是一個特例。 等等....

好吧,這個例子中的兩難問題有一個簡單的解決方案:首先使用int參數( 78 )確定客戶端的內容。

使用DateTime的解決方案並不那么簡單,因為.NET沒有Date類型。 使用DateTime參數的查詢將始終具有表單...

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

...如果SomeDateTime在SQL Server中存儲為date ,則再次出現了四舍五入的困境。 我是否必須投放到2013.05.132013.05.14 對於上面的查詢,客戶端肯定會期望所有實體的日期為14及更高。

好吧,你可以聰明地做,比如:如果我的DateTime參數的時間部分是午夜,則轉換為日期部分。 如果我使用>=強制轉換到第二天,等等....或者你總是可以datetime2(7)datetime2(7) 然后查詢的結果總是正確的,並且(.NET)客戶端期望它。 正確......但也許索引使用次之。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM