NHibernate - truly dynamic sorting - nhibernate

Using NHibernate, I need to be able to configure my application to sort a specific collection of entities exactly as needed.
The configurable sort:
can involve multiple properties
can specify the sorted properties in any order
can specify asc/desc on the sorted properties
can sort by custom properties (i.e. there is no corresponding SQL/C# property - it is calculated)
This functionality is inherited from an existing app where parts of the SQL are specified by an administrator and the SQL statement is built/executed dynamically.
Every time I try thinking through a solution I start getting in muddy waters with all kinds of alarms going off in my head regarding maintainability, performance, scalability, security, etc..
For example, I figure the admin can specify a comma delimited string like so:
"Date asc, FirstName asc, LastName desc"
I can split the string and go through a loop matching the property/sort pairings in a case statement and calling .AddOrder(Order.Asc("FirstName")) as necessary. But then, how do I handle custom properties? I could allow the user to specify SQL for calculating custom properties and then allow the user to sort on those like they would FirstName, but I'm seemingly back at dirty/kludge again.
Is there a clean/appropriate way to handle this requirement?

After much thought and a stroke of luck, I may have a solution.
public class CustomOrder : Order
{
private string customOrderSql;
public CustomOrder(string customOrderSql) : base("", true)
{
this.customOrderSql = customOrderSql;
}
public override NHibernate.SqlCommand.SqlString ToSqlString(
ICriteria criteria, ICriteriaQuery criteriaQuery)
{
return new NHibernate.SqlCommand.SqlString(this.customOrderSql);
}
}
I can pass a custom sort string to my repository where I add my CustomOrder as follows:
.AddOrder(new CustomOrder(customSort))
I still can't sort by custom properties but maybe I can get away with applying case statements in the order by clause. I'm still open for better suggestions if they exist.

Related

Using another list in an Entity Framework query

Looking to achieve the below but it is failing as the locations.Any() is being treated as an IEnumerable instead of an IQueryable and scalar functions invoked via EF require IQueryable. I need this filter to happen at the database level (not materialize the list first).
How can I get the locations.Any() to be treated as an IQueryable here? I understand the list doesn't exist in the database but is there a way for Entity Framework to understand this any and build and AND statement with nested OR in SQL?
public Address GetAddresses(List<Loctions> locations)
{
_context.Addresses.
Where(a => locations.Any(l => MyContext.CustomFunction(l.PropA,l.PropB, a.PropA, a.ProbB) > 1 ))
}
[DbFunction("fn_DistanceBetweenCoordinates", "dbo")]
public static decimal CustomFunction(decimal SourceLatitude, decimal SourceLongitude, decimal TargetLatitude, decimal TargetLongitude) {
throw new NotImplementedException();
}
You could achieve this by moving CustomFunction into the database and use that server side function when querying from EF.
Please read User defined function mapping and try to adapt the sample according to your use case.
We haven't seen the body of the CustomFunction, so it's impossible to tell if it's viable to do the transfer from client based UDF to server based.
We also don't know how the Locations list is populated. Depending on how that is done, the adaptation of the example code might become more cumbersome.

Does CF ORM have an Active Record type Update()?

Currently I am working partly with cfwheels and its Active Record ORM (which is great), and partly raw cfml with its Hibernate ORM (which is also great).
Both work well for applicable situations, but the thing I do miss most when using CF ORM is the model.update() method that is available in cfwheels, where you can just pass a form struct to the method, and it will map up the struct elements with the model properties and update the records.. really good for updating and maintaining large tables. In CF ORM, it seems the only way to to update a record is to set each column individually, then do a save. Is this the case?
Does cf9 ORM have an Active Record type update() (or equivalent) method which can just receive a struct with values to update and update the object without having to specify each one?
For example, instead of current:
member = entityLoadByPK('member',arguments.id);
member.setName(arguments.name);
member.setEmail(arguments.email);
is there a way to do something like this in CF ORM?
member = entityLoadByPK('member',arguments.id);
member.update(arguments);
Many thanks in advance
In my apps I usually create two helper functions for models which handle the task:
/*
* Get properties as key-value structure
* #limit Limit output to listed properties
*/
public struct function getMemento(string limit = "") {
local.props = {};
for (local.key in variables) {
if (isSimpleValue(variables[local.key]) AND (arguments.limit EQ "" OR ListFind(arguments.limit, local.key))) {
local.props[local.key] = variables[local.key];
}
}
return local.props;
}
/*
* Populate the model with given properties collection
* #props Properties collection
*/
public void function setMemento(required struct props) {
for (local.key in arguments.props) {
variables[local.key] = arguments.props[local.key];
}
}
For better security of setMemento it is possible to check existence of local.key in variables scope, but this will skip nullable properties.
So you can make myObject.setMemento(dataAsStruct); and then save it.
There's not a method exactly like the one you want, but EntityNew() does take an optional struct as a second argument, which will set the object's properties, although depending on how your code currently works, it may be clunky to use this method and I don;t know whether it'll have any bearing on whether a create/update is executed when you flush the ORM session.
If your ORM entities inherit form a master CFC, then you could add a method there. Alternatively, you could write one as a function and mix it into your objects.
I'm sure you're aware, but that update() feature can be a source of security problems (known as the mass assignment problem) if used with unsanitized user input (such as the raw FORM scope).

NHibernate: why field.camelcase?

Can someone tell me why in NHibernate mapping we can set access="field.camelcase", since we have access="field" and access="property"?
EDIT: my question is "why we can do this", not "what does it mean". I think this can be source of error for developper.
I guess you wonder what use field.camelcase have when we can do the same with just field? That's true, but that would give (NH) properties unintuive names when eg writing queries or reference the property from other mappings.
Let's say you have something you want to map using the field, eg
private string _name;
public string Name { get { return _name; } }
You sure can map the field using "field" but then you would have to write "_name" when eg writing HQL queries.
select a from Foo a where a._name = ...
If you instead using field.camelcase the data, the same query would look like
select a from Foo a where a.Name...
EDIT
I now saw you wrote "field.camelcase" but my answer is about "field.camelcase-underscore". The principles are the same and I guess you get the point ;)
the portion after the '.' is the so called naming strategy, that you should specify when the name you write in the hbm differ from the backing field. In the case of field.camelcase you are allowed to write CustomerName in the hbm, and NHibernate would look for a field with name customerName in the class. The reason for that is NHibernate not forcing you to choose a name convention to be compliant, NH will works with almost any naming convention.
There are cases where the properties are not suitable for NH to set values.
They may
have no setter at all
call validation on the data that is set, which is not used when loading from the database
do some other stuff that is only used when the value is changed by the business logic (eg. set other properties)
convert the value in some way, which would cause NH performing unnecessary updates.
Then you don't want NH to call the property setter. Instead of mapping the field, you still map the property, but tell NH to use the field when reading / writing the value. Roger has a good explanation why mapping the property is a good thing.

NHibernate: Sorting result by children (of children...) properties

I am trying to build a generic solution to a problem that is probably much more complicated than I realize.
For simplicity, consider that I have the following interface:
PagedResult<T> ToPagedResult<T>(this ICriteria, criteria, string sortName);
sortName is ideally a json-style path of access. E.g : Registration.Class.Curriculum.Description, where description is the property that we want to sort on.
In the case where I want to sort on a property of Class, I have been successful with the following:
ICriteria pageCriteria = criteria.CreateCriteria("Class", "Class").AddOrder(Order.Desc(sortName));
In this case, sortName might equal "Class.Name".
Now, is there a way where I could arbitrarily allow sorting on deeper children?
ICriteria pageCriteria = criteria
.CreateCriteria("Class", "Class")
.CreateCriteria("Class.Foo", "Foo")
.AddOrder(Order.Desc("Foo.Bar"));

Complex Searching with NHibernate

I am curious about what methods do you use for complex searching with NHibernate ?
I am using Ayende's
What is yours ?
Thanks for your advices and answers.
If we have a complex dynamic search, we will usually construct a SearchParameter object and then pass that into a method that will build our criteria for us.
For example, if we were searching for a person we might have a search object that looks like this:
public class PersonSearchParameters
{
public string FirstName {get; set;}
public string LastName {get; set;}
public ICriteria GetSearchCriteria()
{
DetachedCriteria query = DetachedCriteria.For(typeof (Person));
//Add query parameters
Return query;
}
}
Then for each type of search, we'll be able to create the single criteria from the class, or we could have multple search parameter classes and chain them together
We use HQL, but we're still trying to wrap our heads around the Criteria API for complex queries. We have to manage a lot of duplication when using HQL.
I use pretty much Ayende's too jsut a bit more complex, what do you want to do that you cant do with that?
Basically what we added is that we have an interface where we define all the fields where we want to search and we call this when we are about to make the search which means that we can easily change what we are searching for.
Also we are using Active Record in the project ( on top of Hibernate) and tis pretty cool, loads of tasks gets simplified , thou the lack of docs does hurt sometimes
Cheer