I am having hard times convincing NHibernate (EF was able to do it) to filter based on the Hour property of a DateTime entity property. My entity goes something like :
public class Invoice {
// ...
public DateTime Time { get; set; }
// ...
}
I need to retrieve all the invoices that were "made" at a certain hour (let's say 22). A possible query could be :
from i in s.Linq<Invoice>()
where i.Type.Id == Invoice.Type.Local &&
i.TimeOfRegister.Hour == 22
select i
However I am being thrown an exception stating that the property Hour of TimeOfRegister could not be resolved...
LE : The details of the exception : could not resolve property: TimeOfRegister.Hour of: MyCompany.Entities.Invoice
Sounds like NHibernate does not see the a DateTime as a component which has properties it can select on, it sees it as a single property. You probably have to use a function to obtain the Hour part of the DateTime.
In HQL for SqlServer2005: hour(i.TimeOfRegister)
Well I was advised by a colleague to take another route :
Define an int (or a byte) property on the entity (let's say "HourOfRegister") and then in the mapping class map it to a formula :
Map(a => a.HourOfRegister).Formula("DATEPART(HOUR, TimeOfRegister)");
Then I can use it in the query as I like it.
Related
I have a field on Database called name but when I retrieve this I would that be called label.
In raw SQL I can do that like this:
SELECT name AS label FROM ...
Theres a "quick way" to do this on find without need to deal with entity, virtual fields etc...
I did it using virtual properties like this on Entity:
protected $_virtual = ['label'];
protected function _getlabel()
{
return $this->_properties['name'];
}
A potential gotcha could be... I'm was using _serialize to return the result and it was not working because you have to expose the virtual properties using $_virtual.
Base entity interface is IEntity which requires only "object ID {get;set;}" to be implemented.
Now, in almost every case ID is of Guid type (except for membership etc).
I am using following code to do mapping of interface
...
AnyPart<IEntity> primaryMap = ReferencesAny(x => x.Primary)
.IdentityType<object>() // tried with .IdentityType<Guid>()
.EntityIdentifierColumn("PrimaryID")
.EntityTypeColumn("PrimaryType")
.MetaType<string>();
...
Of course, next I am adding meta values.
So, Now getting error
Source array was not long enough. Check srcIndex and length, and the array's lower bound
And with .IdentityType<Guid>()
could not resolve property: Primary.ID of: Founder.Connection [.SingleOrDefault[Founder.Connection](NHibernate.Linq.NhQueryable`1[Founder.Connection], Quote((x, ) => (OrElse(AndAlso(Equal(x.Primary.ID, 35c2142a-4c17-4b77-96fd-a2570028a211), Equal(x.Secondary.ID, 35c2142a-4c17-4b77-96fd-a2570028a211)), AndAlso(Equal(x.Secondary.ID, 35c2142a-4c17-4b77-96fd-a2570028a211), Equal(x.Primary.ID, 35c2142a-4c17-4b77-96fd-a2570028a211))))), )]
UPDATE:
I tried also with .IdentityType(x=>x.ID) but same problem (Source array was not long enough)
UPDATE II:
Query (Actually whole method containing query) that this error occurs on is bellow:
public IQueryable<Connection> GetConnections(IEntity connectable)
{
IQueryable<Connection> query =
Query().Where(
x => x.Primary.ID == connectable.ID || x.Secondary.ID == connectable.ID);
return query;
}
Try this in the query: x.Primary == connectable (without ID).
The problem is that you reference an object (or another unmapped type, that's why you need an any mapping). There is no ID on object.
By the way, using HQL would allow you to access the id by using the keyword id, which is not available in Linq (technically it could be made available as extension method, but I don't know Linq to NH good enough to say if it had been implemented). Every any reference conceptually has an id and a class.
I have an application that uses a few data warehousing principles such as dimensional modeling to do reporting on a fairly simple database.
An example (simplified) entity named Call looks like this:
public virtual long Id { get; set; }
public virtual string OriginatorNumber { get; set; }
public virtual string DestinationNumber { get; set; }
public virtual DateDimension DateDimension { get; set; }
A few of the properties of the real model have been removed as they are irrelevant. The simplified DateDimension looks like this:
public virtual long Id { get; set; }
public virtual DateTime Date { get; set; }
public virtual int DayOfMonth { get; set; }
public virtual int Weekday { get; set; }
There are a LOT more columns like this - they are prepopulated for the current decade by application setup. So each date in the entire decade has a row in this table, and each Call has a link to the date that it occured. This is all mapped in Fluent NHibernate and working fine.
If I want to do some reporting, I can do this easily with the improved NHibernate LINQ provider in 3.0. We would like to use LINQ for the improved maintainability it gives us, but if we really MUST, we'll consider HQL, ICriteria or even plain SQL.
So say I want to build a report that shows the number of calls from a certain number, divided by the day of the week they occur. I can do that easily this way:
var query = Calls
.Where(c => c.OriginatorNumber == "402")
.GroupBy(c => c.DateDimension.Weekday)
.Select(g => new { Day = g.Key, Calls = g.Count() } );
In this example, "Calls" is basically an IQueryable returned from NHibernates LINQ provider (Query) through a repository interface. The query above gives me the correct results, NHibernate Profiler shows me that the SQL is pretty optimal, all is well.
However, if I want to do something slightly more advanced, I get stuck. Say I want the average number of calls per weekday. Not too far from the above, right? I just need to figure out the number of unique dates each weekday has in the result set, divide the total number of calls by it, and we're all set - right? Well, no, this is where I start to hit the limitations of the NHibernate LINQ provider. With LINQ to objects I could construct a query to do it - something along the lines of
.Select(g => g.Count() / g.GroupBy(c => c.DateDimension.Date).Count());
However, this does not convert into the correct query when using it in NHibernate. Rather, it turns both .Count() calls in the above to the same count(*) of call records, so the result is always 1.
I COULD of course just query for each call, weekday and date as a new anonymous object, then do the math on the application side, but according to conventional wisdom, That's Just Wrong (tm). I could end up doing it in desperation, tho, even tho it means pain when the table grows to a million++ calls.
The below is an SQL query that gives me the result I am looking for.
select ss.Weekday, AVG(cast(ss.Count as decimal))
from
(
select dd.Weekday, dd.Date, COUNT(*) as Count
from Call c
left outer join DateDimension dd
on c.DateDimension_id = dd.Id
where c.OriginatorNumber = '402'
group by dd.Weekday, dd.Date
) ss
group by ss.Weekday
order by ss.Weekday
Is it possible to do this with the NHibernate LINQ provider? Or, if that is not possible, how close can I get before I have to let the application fetch the intermediary result and do the rest?
There are a lot of things you can't do with the LINQ provider. Using HQL or CreateCriteria is just something you'll have to accept with NHibernate.
I haven't tried it, but it looks like you should be able to do what you want to do using HQL or CreateCriteria (with DetatchedCriteria).
If you are desperate you can also fall back to plain SQL using CreateSqlQuery.
My current project is using NHibernate 3.0b1 and the NHibernate.Linq.Query<T>() API. I'm pretty fluent in LINQ, but I have absolutely no experience with HQL or the ICriteria API. One of my queries isn't supported by the IQueryable API, so I presume I need to use one of the previous APIs -- but I have no idea where to start.
I've tried searching the web for a good "getting started" guide to ICriteria, but the only examples I've found are either far too simplistic to apply here or far too advanced for me to understand. If anyone has some good learning materials to pass along, it would be greatly appreciated.
In any case, the object model I'm querying against looks like this (greatly simplified, non-relevant properties omitted):
class Ticket {
IEnumerable<TicketAction> Actions { get; set; }
}
abstract class TicketAction {
Person TakenBy { get; set; }
DateTime Timestamp { get; set; }
}
class CreateAction : TicketAction {}
class Person {
string Name { get; set; }
}
A Ticket has a collection of TicketAction describing its history. TicketAction subtypes include CreateAction, ReassignAction, CloseAction, etc. All tickets have a CreateAction added to this collection when created.
This LINQ query is searching for tickets created by someone with the given name.
var createdByName = "john".ToUpper();
var tickets = _session.Query<Ticket>()
.Where(t => t.Actions
.OfType<CreateAction>()
.Any(a => a.TakenBy.Name.ToUpper().Contains(createdByName));
The OfType<T>() method causes a NotSupportedException to be thrown. Can I do this using ICriteria instead?
try something like this. It's uncompiled, but it should work as long as IEnumerable<TicketAction> Actions and Person TakenBy is never null. If you set it to an empty list in the ticket constructor, that will solve a problem with nulls.
If you add a reference to the Ticket object in the TicketAction, you could do something like this:
ICriteria criteria = _session.CreateCriteria(typeof(CreateAction))
.Add(Expression.Eq("TakenBy.Name", createdByName));
var actions = criteria.List<CreateAction>();
var results = from a in criteria.List<>()
select a.Ticket;
In my experience, nhibernate has trouble with criteria when it comes to lists when the list is on the object side - such as is your case. When it is a list of values on the input side, you can use Expression.Eq. I've always had to find ways around this limitation through linq, where I get an initial result set filtered down as best as I can, then filter again with linq to get what I need.
OfType is supported. I'm not sure ToUpper is though, but as SQL ignores case it does not matter (as long as you are not also running the query in memory...). Here is a working unit test from the nHibernate.LINQ project:
var animals = (from animal in session.Linq<Animal>()
where animal.Children.OfType<Mammal>().Any(m => m.Pregnant)
select animal).ToArray();
Assert.AreEqual("789", animals.Single().SerialNumber);
Perhaps your query should look more like the following:
var animals = (from ticket in session.Linq<Ticket>()
where ticket.Actions.OfType<CreateAction>().Any(m => m.TakenBy.Name.Contains("john"))
select ticket).ToArray();
I have the following entity
public class Employee
{
public virtual int Id {get;set;}
public virtual ISet<Hour> XboxBreakHours{get;set}
public virtual ISet<Hour> CoffeeBreakHours {get;set}
}
public class Hour
{
public DateTime Time {get;set;}
}
(What I want to do here is store information that employee A plays Xbox everyday let's say at 9:00 13:30 and has a coffee break everyday at 7:00 12:30 18:00) - I am not sure if my approach is valid at all here.
The question is how should my (ideally fluent) mappings look like here? It is not necessary (from my point of view) for Hour class to have Id or be accessible from some kind of repository.
Depending on how you want to do it, you either need to map your collection as an element mapping or as a component collection (that's <element> and <composite-element> in NHibernate terms). The former will need an IUserType defining, while the latter is for if you're going to have your Hour class have more than one property.
If you're sticking with a single property, you'll need to define an IUserType so NHibernate knows how to translate to and from your type to your database. Once you've done that, you can map it with Fluent NHibernate like so:
HasMany(x => x.XboxBreakHours)
.Element("value", x => x.CustomType<YourUserType>);
That specifies that your collection is stored in a table with a column called value containing the actual values. The CustomType call is what tells NHibernate to use the IUserType for this collection.
If you're going to have multiple properties in your Hour class, then you need to do the following (note: this is actually very similar to doing a Component mapping).
HasMany(x => x.XboxBreakHours)
.Component(comp =>
{
comp.Map(x => x.Time);
comp.Map(x => x.Another);
});