NHibernate Criteria against an <ANY> Mapping - nhibernate

I have a Project model that has a property of type IProjectWorker, this could either be a single User or a Team. In Castle ActiveRecord it's defined like this:
[Any(typeof(int), MetaType = typeof(string), TypeColumn = "WorkerType", IdColumn = "WorkerID", Cascade = CascadeEnum.None)]
[Any.MetaValue("USER", typeof(User))]
[Any.MetaValue("TEAM", typeof(Team))]
public IProjectWorker Worker { get; set; }
Now I need to be able to search for projects where the worker's name contains some text. My initial reaction was something like this:
query
.CreateAlias("Worker", "Worker")
.Add(Restrictions.InsensitiveLike("Worker.WorkerName", SearchText, MatchMode.Anywhere));
But this gives me an error-- "any types do not have a unique referenced persister". This makes sense, it doesn't know how to handle joining to the two different tables for the search.
Can I make two different aliases for each table and do a Restrictions.Or() across them? I tried it, but couldn't quite get it right. Or is there some other way to do this using criteria that I'm missing? Or am I going to have to use HQL instead?

Related

Is it possible to use NHibernate Filters to filter through references?

Contrived example, but let's say I have a these entities:
public class Root
{
public virtual Customer Customer { get; set; }
}
public class Customer
{
public virtual CustomerData Data { get; set; }
}
public class CustomerData
{
public virtual string FooName { get; set; }
}
Now, let's say I want to create a filter for Root based on the value of FooName. Intuitively, I tried this in my FooMap class. Using Fluent mappings.
ApplyFilter("FooNameFilter", "Customer.Data.FooName in (:argument)");
This doesn't work. A SqlClient.SqlException is thrown stating The multi-part identifier "Customer.Data.FooName" could not be bound.
Is there a way to make filters work this way, or am I forced to move that logic into all Query<Root>()s instead?
What could be working, is to move the filter to the CustomerData object if possible, or to create "more sophisticated SQL condition" applied on the Customer mapping. But it is about pure SQL, no references. How do the filters work?
The filters are the same as the where clause, but could be adjusted in a runtime. The extract from documentation 18.1. NHibernate filters
NHibernate adds the ability to pre-define filter criteria and attach
those filters at both a class and a collection level. A filter
criteria is the ability to define a restriction clause very similiar
to the existing "where" attribute available on the class and various
collection elements. Except these filter conditions can be
parameterized. The application can then make the decision at runtime
whether given filters should be enabled and what their parameter
values should be. Filters can be used like database views, but
parameterized inside the application.
The definition of the where:
where (optional) specify an arbitrary SQL WHERE condition to be used when retrieving objects of this class
Other words, these settings act as "add on" to our mapping. They are extending it (both where and filter) with more SQL balast. The filter could be shared among many mappings and applied to all queries inside one session, but it must target the column:
condition=":myFilterParam = MY_FILTERED_COLUMN"

Need a concept on fetching data with HQL while three or more tables are in use

A small briefing on what I am trying to do.
I have three tables Content(contentId, body, timeofcreation), ContentAttachmentMap(contentId, attachmentId) and Attachment(attachmentId, resourceLocation).
The reason I adopted to create the mapping table because in future application the attachment can also be shared with different content.
Now I am using HQL to get data. My objectives is as follows:
Get All contents with/without Attachments
I have seen some examples in the internet like you can create an objective specific class (not POJO) and put the attribute name from the select statement within its constructor and the List of that Class object is returned.
For e.g. the HQL will be SELECT new com.mydomain.myclass(cont.id, cont.body) ..... and so on.
In my case I am looking for the following SELECT new com.mydomain.contentClass(cont.id, cont.body, List<Attachment>) FROM ...`. Yes, I want to have the resultList contain contentid, contentbody and List of its Attachments as a single result List item. If there are no attachments then it will return (cont.id, contentbody, null).
Is this possible? Also tell me how to write the SQL statements.
Thanks in advance.
I feel you are using Hibernate in a fundamentally wrong way. You should use Hibernate to view your domain entity, not to use it as exposing the underlying table.
You don't need to have that contentClass special value object for all these. Simply selecting the Content entity serves what you need.
I think it will be easier to have actual example.
In your application, you are not seeing it as "3 tables", you should see it as 2 entities, which is something look like:
#Entity
public class Content {
#Id
Long id;
#Column(...)
String content;
#ManyToMany
#JoinTable(name="ContentAttachmentMap")
List<Attachment> attachments;
}
#Entity
public class Attachment {
#Id
Long id;
#Column(...)
String resourceLocation
}
And, the result you are looking for is simply the result of HQL of something like
from Content where attachments IS EMPTY
I believe you can join fetch too in order to save DB access:
from Content c left join fetch c.attachments where c.attachments IS EMPTY

How do I express this LINQ query using the NHibernate ICriteria API?

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();

Fluent NHibernate Mapping Single Column to Composite Key

I have a situation where i have defined an entity in my domain model in which I would like to expose a single id column.
public class OfferedProduct
{
public virtual string Id {get; set;}
//other properties
}
The legacy database table this will map to is
CREATE TABLE ProductGrouping
MemberNumber INT NOT NULL,
GroupId CHAR NOT NULL,
...
I dont want to compromise the domain model by introducing two properties and mapping them using the "CompositeId" construct.
CompositeId().KeyProperty(x => x.MemberNumber).KeyProperty(x => x.GroupId)
What I want ideally is to concatenate the two values in the form {MemberNumber}{GroupId} and expose this as the Id value. I would then use a Custom Type to handle how these values are concatenated when retrieved from the DB and broken apart when saving/selecting.
I have noticed that the "CompositeId" method does not allow a customType as with the standard "Id" call; but the "Id" method does not provide the ability to set multiple columns. I have seen examples where people have used "Map" to combine two columns using a custom type, but not for id values.
I have noticed the "CompositeId" has an overload that can take a custom identity class but I am unsure how to use it in this scenario.
CompositeId<OfferedProductIdentifier>(x => x.?)
Any help would be greatly appreciated.
in case someone comes here
CompositeId()
.KeyProperty(t => t.Id, c =>
c.Type(typeof(MyUserType)).ColumnName("MemberNumber").ColumnName("GroupId"));

NHibernate projection help

Im having a problem creating a projection for my nhibernate detachedcriteria object.
I have a class Spa which is linked to table Address.
Address has a field called City which is a string.
public class Spa : IAggregateRoot
{
[BelongsTo("AddressID", Cascade = CascadeEnum.All)]
public Address Address { get; set; }
}
My ultimate goal is to get a distinct list of City names.
If i could get all spas with distinct cities i would be happy too.
All my attempts have been for naught and havent found any helpful posts.
So far i've tried:
DetachedCriteria query = DetachedCriteria.For<Spa>()
.CreateAlias("Address", "A")
query.SetProjection(
Projections.Distinct(Projections.ProjectionList()
.Add(Projections.Alias(Projections.Property("Address"), "A"))));
var Spas = ActiveRecordMediator<Spa>.FindAll(query);
I know the above is not correct, just trying to find somewhere to start.
Any help would be appreciated.
Also any simple projections tutorials would be appreciated, cant seem to find anything straight forward out there.
I also tried, but got cast error, looking into it:
DetachedCriteria query = DetachedCriteria.For<Spa>()
.CreateAlias("Address", "A")
.SetProjection(Projections.Distinct(Projections.Property("A.City")));
It seems to me there are two parts to your question.
1. What should my DetachedCriteria look like?
If you are not performing any other aggregations, GROUP BY should provide the same results as DISTINCT. This is the query I would use:
var query = DetachedCriteria.For<Spa>()
.CreateAlias("Address", "A")
.SetProjection(Projections.GroupProperty("A.City"));
2. How do I execute it with Castle ActiveRecord?
I have never used ActiveRecord, but based on the method signatures, I would expect something like this to work:
var cities = ActiveRecordMediator<string>.FindAll(query);
If you have access to the NHibernate session, you could also execute it this way:
var cities = query.GetExecutableCriteria(session).List<string>();