繁体   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