[英]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)....
可能是SomeNumber
為8
或更大的所有實體。 如果(浮點十進制)參數7.3
將被轉換為存儲在數據庫中的int
類型,則必須決定如何將7.3
- 7
(將導致錯誤結果)或8
? 好吧,你可以說,因為我的查詢說>=
並且我知道DB中的類型是整數,舍入到8
必須是正確的。 如果我使用<=
,那么舍入到7
必須是正確的。 如果我會使用==
,哦......我一定不能完全舍入或者我知道結果必須為空,我可以直接將這個Where
子句翻譯為false
。 而且!=
true
。 但7.0
的參數是一個特例。 等等....
好吧,這個例子中的兩難問題有一個簡單的解決方案:首先使用int
參數( 7
或8
)確定客戶端的內容。
使用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.13
或2013.05.14
? 對於上面的查詢,客戶端肯定會期望所有實體的日期為14及更高。
好吧,你可以聰明地做,比如:如果我的DateTime
參數的時間部分是午夜,則轉換為日期部分。 如果我使用>=
強制轉換到第二天,等等....或者你總是可以datetime2(7)
為datetime2(7)
。 然后查詢的結果總是正確的,並且(.NET)客戶端期望它。 正確......但也許索引使用次之。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.