Cân nhắc miền ngớ ngẩn này:NHibernate QueryOver liên hiệp một tài sản đối với tài sản khác
namespace TryHibernate.Example
{
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
}
public class WorkItem
{
public int Id { get; set; }
public string Description { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
}
public class Task
{
public int Id { get; set; }
public Employee Assignee { get; set; }
public WorkItem WorkItem { get; set; }
public string Details { get; set; }
public DateTime? StartDateOverride { get; set; }
public DateTime? EndDateOverride { get; set; }
}
}
Ý tưởng là mỗi hạng mục công trình có thể được gán cho nhiều người lao động với các chi tiết khác nhau, có khả năng trọng bắt đầu/kết thúc ngày với các hạng mục công trình chinh no. Nếu những ghi đè đó là null, chúng sẽ được lấy từ mục công việc thay thế.
Bây giờ tôi muốn thực hiện truy vấn có giới hạn về ngày hiệu quả. Tôi đã thử này đầu tiên:
IList<Task> tasks = db.QueryOver<Task>(() => taskAlias)
.JoinAlias(() => taskAlias.WorkItem,() => wiAlias)
.Where(() => taskAlias.StartDateOverride.Coalesce(() => wiAlias.StartDate) <= end)
.And(() => taskAlias.EndDateOverride.Coalesce(() => wiAlias.EndDate) >= start)
.List();
Thật không may, nó không biên dịch như Coalesce
hy vọng một, không phải là một biểu hiện bất động sản liên tục.
OK, tôi đã thử điều này:
.Where(() => (taskAlias.StartDateOverride == null
? wiAlias.StartDate
: taskAlias.StartDateOverride) <= end)
.And(() => (taskAlias.EndDateOverride == null
? wiAlias.EndDate
: taskAlias.EndDateOverride) >= start)
này ném NullReferenceException. Bạn không chắc chắn lý do tại sao, nhưng có lẽ hoặc vì NHibernate không đúng cách dịch toán tử ternary (và cố gắng thực sự gọi nó thay vì) hoặc vì == null
không chính xác là cách đúng để kiểm tra null. Dù sao, tôi thậm chí không mong đợi nó hoạt động.
Cuối cùng, chương trình này hoạt động:
IList<Task> tasks = db.QueryOver<Task>(() => taskAlias)
.JoinAlias(() => taskAlias.WorkItem,() => wiAlias)
.Where(Restrictions.LeProperty(
Projections.SqlFunction("COALESCE", NHibernateUtil.DateTime,
Projections.Property(() => taskAlias.StartDateOverride),
Projections.Property(() => wiAlias.StartDate)),
Projections.Constant(end)))
.And(Restrictions.GeProperty(
Projections.SqlFunction("COALESCE", NHibernateUtil.DateTime,
Projections.Property(() => taskAlias.EndDateOverride),
Projections.Property(() => wiAlias.EndDate)),
Projections.Constant(start)))
.List();
Nhưng không có cách nào tôi có thể gọi là mã sạch. Có lẽ tôi có thể trích xuất các biểu thức nhất định thành các phương pháp riêng biệt để làm sạch nó một chút, nhưng sẽ tốt hơn nếu sử dụng cú pháp biểu thức thay vì các dự báo xấu xí này. Có cách nào để làm điều đó? Có bất kỳ lý do nào đằng sau NHibernate không hỗ trợ biểu thức thuộc tính trong phần mở rộng Coalesce
không?
Một lựa chọn hiển nhiên là chọn tất cả mọi thứ và sau đó lọc kết quả bằng LINQ hoặc bất kỳ thứ gì. Nhưng nó có thể trở thành một vấn đề hiệu suất với số lượng lớn của tổng số hàng.
Đây là mã đầy đủ trong trường hợp ai đó muốn thử nó:
using (ISessionFactory sessionFactory = Fluently.Configure()
.Database(SQLiteConfiguration.Standard.UsingFile("temp.sqlite").ShowSql())
.Mappings(m => m.AutoMappings.Add(
AutoMap.AssemblyOf<Employee>(new ExampleConfig())
.Conventions.Add(DefaultLazy.Never())
.Conventions.Add(DefaultCascade.All())))
.ExposeConfiguration(c => new SchemaExport(c).Create(true, true))
.BuildSessionFactory())
{
using (ISession db = sessionFactory.OpenSession())
{
Employee empl = new Employee() { Name = "Joe" };
WorkItem wi = new WorkItem()
{
Description = "Important work",
StartDate = new DateTime(2016, 01, 01),
EndDate = new DateTime(2017, 01, 01)
};
Task task1 = new Task()
{
Assignee = empl,
WorkItem = wi,
Details = "Do this",
};
db.Save(task1);
Task task2 = new Task()
{
Assignee = empl,
WorkItem = wi,
Details = "Do that",
StartDateOverride = new DateTime(2016, 7, 1),
EndDateOverride = new DateTime(2017, 1, 1),
};
db.Save(task2);
Task taskAlias = null;
WorkItem wiAlias = null;
DateTime start = new DateTime(2016, 1, 1);
DateTime end = new DateTime(2016, 6, 30);
IList<Task> tasks = db.QueryOver<Task>(() => taskAlias)
.JoinAlias(() => taskAlias.WorkItem,() => wiAlias)
// This doesn't compile:
//.Where(() => taskAlias.StartDateOverride.Coalesce(() => wiAlias.StartDate) <= end)
//.And(() => taskAlias.EndDateOverride.Coalesce(() => wiAlias.EndDate) >= start)
// This throws NullReferenceException:
//.Where(() => (taskAlias.StartDateOverride == null ? wiAlias.StartDate : taskAlias.StartDateOverride) <= end)
//.And(() => (taskAlias.EndDateOverride == null ? wiAlias.EndDate : taskAlias.EndDateOverride) >= start)
// This works:
.Where(Restrictions.LeProperty(
Projections.SqlFunction("COALESCE", NHibernateUtil.DateTime,
Projections.Property(() => taskAlias.StartDateOverride),
Projections.Property(() => wiAlias.StartDate)),
Projections.Constant(end)))
.And(Restrictions.GeProperty(
Projections.SqlFunction("COALESCE", NHibernateUtil.DateTime,
Projections.Property(() => taskAlias.EndDateOverride),
Projections.Property(() => wiAlias.EndDate)),
Projections.Constant(start)))
.List();
foreach (Task t in tasks)
Console.WriteLine("Found task: {0}", t.Details);
}
}
Và cấu hình thực sự là đơn giản:
class ExampleConfig : DefaultAutomappingConfiguration
{
public override bool ShouldMap(Type type)
{
return type.Namespace == "TryHibernate.Example";
}
}
Một cơn ác mộng khác? Tôi đã thực hiện nghiên cứu của mình, tôi đã thực sự đưa ra giải pháp làm việc, tôi đã nêu rõ vấn đề của mình và thậm chí đã cung cấp một ví dụ. Như thể -2 đại diện thực sự có nghĩa là một cái gì đó! –
Tôi hoàn toàn đồng ý với nhận xét ở trên. Tất cả những gì tôi cần là tạo một dự án bàn điều khiển mới, cài đặt hai gói NuGet, sao chép/dán mã được cung cấp và bắt đầu chơi với nó. Không có dự đoán, không có lỗi chính tả, mọi thứ biên dịch/thực hiện chính xác những gì được giải thích trong bài đăng hoặc nhận xét trong mã. Nó thực sự đặc biệt để tìm tốt như vậy 'mcve' ở đây cho một vấn đề không tầm thường. –