diese dumme Domain Bedenken Sie:NHibernate QueryOver Coalesce eine Eigenschaft auf eine andere Eigenschaft
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; }
}
}
Die Idee ist, dass jeder Arbeitseinheit an mehrere Mitarbeiter mit unterschiedlichen Details zugeordnet werden kann, möglicherweise überschrieben Start-/Enddaten der Arbeits Artikel selbst. Wenn diese Überschreibungen null sind, sollten sie stattdessen vom Arbeitselement übernommen werden.
Jetzt möchte ich eine Abfrage mit Einschränkungen für die effektive Daten durchführen. Ich habe diese zuerst versucht:
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();
Leider hat es nicht kompiliert als Coalesce
eine Konstante erwartet, nicht eine Eigenschaft Ausdruck.
OK, ich habe dies versucht:
.Where(() => (taskAlias.StartDateOverride == null
? wiAlias.StartDate
: taskAlias.StartDateOverride) <= end)
.And(() => (taskAlias.EndDateOverride == null
? wiAlias.EndDate
: taskAlias.EndDateOverride) >= start)
Dies wirft Nullreferenceexception. Nicht sicher, warum, aber wahrscheinlich entweder, weil NHibernate diesen ternären Operator nicht richtig übersetzt (und versucht, ihn stattdessen tatsächlich aufzurufen) oder weil == null
nicht genau der richtige Weg ist, um nach Nullen zu suchen. Jedenfalls habe ich nicht einmal erwartet, dass es funktioniert.
Schließlich diese funktioniert:
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();
Aber es gibt keine Möglichkeit, die ich sauberen Code aufrufen können. Vielleicht kann ich bestimmte Ausdrücke in separate Methoden extrahieren, um sie ein wenig zu bereinigen, aber es wäre viel besser, Ausdruckssyntax zu verwenden als diese hässlichen Projektionen. Gibt es einen Weg, es zu tun? Gibt es einen Grund, warum NHibernate keine Eigenschaftsausdrücke in der Erweiterung Coalesce
unterstützt?
Eine offensichtliche Alternative ist, alles auszuwählen und dann die Ergebnisse mit Linq oder was auch immer zu filtern. Aber es könnte ein Performance-Problem mit einer großen Anzahl von Gesamtreihen werden. Hier
ist voll Code, falls jemand will es versuchen:
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);
}
}
und die Konfiguration ist denkbar einfach:
class ExampleConfig : DefaultAutomappingConfiguration
{
public override bool ShouldMap(Type type)
{
return type.Namespace == "TryHibernate.Example";
}
}
Noch ein Downvote Maniac? Ich habe meine Nachforschungen angestellt, ich habe tatsächlich eine funktionierende Lösung gefunden, ich habe mein Problem klar dargelegt und sogar ein Beispiel gegeben. Als ob -2 Rep tatsächlich etwas bedeutet! –
Ich stimme dem obigen Kommentar voll zu. Alles, was ich brauchte, war ein neues Konsolenprojekt zu erstellen, zwei NuGet-Pakete zu installieren, den bereitgestellten Code zu kopieren und einzufügen und damit zu spielen. Keine Vermutungen, keine Tippfehler, alles kompiliert/tut genau das, was im Beitrag erklärt oder im Code kommentiert wird. Es ist wirklich außergewöhnlich, so gute 'mcve' hier für ein nicht triviales Thema zu finden. –