2012-05-15 34 views
5

là nơi một cách để viết một cái gì đó như thế này:NHibernate LINQ cung cấp datediff

public class Item 
{ 
    public DateTime Start { get; set; } 
    public DateTime Finish{ get; set; } 
} 

Sessin.Query<Item>.Where(x => x.Start.AddHours(3) > x.Finish); 

Bây giờ tôi nhận được một ngoại lệ

[NotSupportedException: System.DateTime AddHours (Double)]

+0

Bạn có sử dụng NH3.3? và lập bản đồ theo mã? – Rippo

+0

Có, tôi sử dụng cả hai –

Trả lời

7

Không có cách nào dễ dàng để làm cho LINQ của bạn truy vấn. Vấn đề với kịch bản của bạn là NHibernate không biết làm thế nào để dịch DateTime.AddHours(double hours) phương pháp. Nhưng bạn có thể sử dụng HQL để viết một truy vấn tương tự? Rõ ràng là không. Không có chức năng HQL AddHours chuẩn nào. Do đó bạn phải đăng ký chức năng mới này. NHibernate sử dụng các phương ngữ để dịch giữa cú pháp SQL của nhà cung cấp và SQL cụ thể. Để thực hiện điều này, bạn phải tạo một lớp phương ngữ mới xuất phát từ một phương thức xuất hiện và ghi đè lên phương thức RegisterFunctions. Nhưng điều này chỉ giải quyết được nửa đầu của vấn đề. Tiếp theo bạn phải hiển thị NHibernate làm thế nào để sử dụng chức năng này trong LINQ. Bạn phải "ánh xạ" giữa phương thức DateTime.AddHours(double hours) và hàm hql tùy chỉnh được đăng ký ưu tiên. NHibernate sử dụng một đăng ký cho mục đích này. Bạn sẽ phải mở rộng sổ đăng ký linq-to-hql mặc định.

tôi sẽ giới thiệu một ví dụ mà làm việc với NHibernate 3,3

Tạo một lớp phương ngữ mới (ví dụ của tôi sử dụng MsSql2008Dialect được xác định trước)

 
    public class EnhancedMsSql2008Dialect : MsSql2008Dialect 
    { 
     protected override void RegisterFunctions() { 
      base.RegisterFunctions(); 
      RegisterFunction("add_hours", new SQLFunctionTemplate(NHibernateUtil.DateTime, "dateadd(hour, ?1, ?2)")); 
     } 
    } 

Tạo một lớp phát LINQ-to-HQL mới biết làm thế nào để dịch AddHours phương pháp

 
    using NHibernate.Linq.Functions; 
    using NHibernate.Linq; 
    using NHibernate.Hql.Ast; 

    public class DateTimeMethodsHqlGenerator : BaseHqlGeneratorForMethod 
    { 
     public DateTimeMethodsHqlGenerator() { 
      SupportedMethods = new[] { 
       ReflectionHelper.GetMethodDefinition((DateTime x) => x.AddHours(1)) 
      }; 
     } 

     public override HqlTreeNode BuildHql(System.Reflection.MethodInfo method, System.Linq.Expressions.Expression targetObject, System.Collections.ObjectModel.ReadOnlyCollection arguments, HqlTreeBuilder treeBuilder, NHibernate.Linq.Visitors.IHqlExpressionVisitor visitor) { 
      return treeBuilder.MethodCall("add_hours", visitor.Visit(arguments[0]).AsExpression(), visitor.Visit(targetObject).AsExpression()); 
     } 
    } 

Mở rộng mặc định LINQ-to-HQL lớp registry

 
    public class EnhancedLinqToHqlGeneratorsRegistry : DefaultLinqToHqlGeneratorsRegistry 
    { 
     public EnhancedLinqToHqlGeneratorsRegistry() : base() { 
      // 
      RegisterGenerator(ReflectionHelper.GetMethodDefinition((DateTime x) => x.AddHours(1)), new DateTimeMethodsHqlGenerator()); 
     } 
    } 

Configure

 
    cfg.DataBaseIntegration(c => { 
     c.Dialect<EnhancedMsSql2008Dialect>(); 
    }); 
    cfg.LinqToHqlGeneratorsRegistry<EnhancedLinqToHqlGeneratorsRegistry>(); 
+0

Khá nhiều câu trả lời tương tự như tôi và trả lời năm phút sau :) – Rippo

+0

không nhấn "hiển thị câu trả lời mới" trong khi tôi đang chỉnh sửa, để lại giải pháp của tôi kể từ khi bạn didn ' t có phương ngữ tùy chỉnh trước khi chỉnh sửa – Vasea

+0

Cảm ơn bạn đã trả lời và mô tả tình huống tốt. BTW sử dụng phương pháp Merge trong DefaultLinqToHqlGeneratorsRegistry trông đẹp hơn một chút :) –

5

Nếu bạn đang sử dụng NH 3.3 và cấu hình Loquacious thì bạn có thể làm điều gì đó như thế này ..

Thêm LinqToHqlGeneratorsRegistry đối với cấu hình: -

 var configure = new Configuration() 
      .DataBaseIntegration(x => { 
       x.Dialect<CustomDialect>(); 
       x.ConnectionStringName = "db"; 
      }) 
      .LinqToHqlGeneratorsRegistry<MyLinqtoHqlGeneratorsRegistry() 
      .CurrentSessionContext<WebSessionContext>(); 

và thêm ba loại sau đây: -

public class MyLinqtoHqlGeneratorsRegistry : DefaultLinqToHqlGeneratorsRegistry 
{ 
    public MyLinqtoHqlGeneratorsRegistry() 
    { 
     this.Merge(new AddHoursGenerator()); 
    } 
} 

public class AddHoursGenerator : BaseHqlGeneratorForMethod 
{ 
    public AddHoursGenerator() 
    { 
     SupportedMethods = new[] { 
     ReflectionHelper.GetMethodDefinition<DateTime?>(d =>  
       d.Value.AddHours((double)0)) 
      }; 
    } 

    public override HqlTreeNode BuildHql(MethodInfo method, 
     System.Linq.Expressions.Expression targetObject, 
     ReadOnlyCollection<System.Linq.Expressions.Expression> arguments, 
     HqlTreeBuilder treeBuilder, IHqlExpressionVisitor visitor) 
    { 
     return treeBuilder.MethodCall("AddHours", 
       visitor.Visit(targetObject).AsExpression(), 
       visitor.Visit(arguments[0]).AsExpression() 
      ); 
    } 
} 

public class CustomDialect : MsSql2008Dialect 
{ 
    public CustomDialect() 
    { 
     RegisterFunction(
      "AddHours", 
      new SQLFunctionTemplate(
        NHibernateUtil.DateTime, 
        "dateadd(hh,?2,?1)" 
       ) 
      ); 
    } 
} 

tôi đã dựa trên blog post này bằng Fabio.

Bây giờ bạn có thể sử dụng mã của bạn như là: -

Session.Query<Item>.Where(x => x.Start.AddHours(3) > x.Finish); 

này cũng có thể trong 3,2 nhưng các thông số public override HqlTreeNode BuildHql(..) là hơi khác nhau ...

+0

Cảm ơn bạn đã giải pháp! Tôi xin lỗi tôi chấp nhận một câu trả lời bởi vì nó có descritpion tốt về những gì xảy ra và tại sao. –

+0

Đừng lo lắng tôi không quá bận tâm mặc dù một số phần của tôi cảm thấy câu trả lời khác nên được loại bỏ, bạn giành chiến thắng một số bạn mất một số. – Rippo

+0

Có thể làm việc này với DateTime.Subtract không? – nfplee