nHibernate Criteria, trying to order by enum name (alphabetical rather than value) - nhibernate

Lets say I have a class that contains a status type, which is defined as an enum like so:
public class MyObject
{
public virtual int Id { get; set; }
public virtual SomeEntity Data { get; set; }
public virtual MyStatusEnum Status { get; set; }
}
public enum MyStatusEnum
{
Active = 1,
Paused = 2,
Completed = 3
}
My mapping done via Fluent nHibernate looks like:
public class MyObjectMap: ClassMap<MyObject>
{
public MyObjectMap()
{
this.Table("my_object_table");
...
this.References(x => x.SomeEntity).Column("some_entity_id").Not.Nullable();
this.Map(x => x.Status).Column("status_type").CustomType<MyStatusEnum>().Not.Nullable();
}
}
Now that the setup is out of the way, my dilemma:
In my repository class, I want to sort all of the MyObject entities by the Status property, which nHibernate persists as an int. However, due to powers beyond my control, I cannot reorder MyStatusEnum so that the enum values are ordered alphabetically. When I create my criteria to select the list of MyObjects, and try to sort it by the Status property, it sorts by the int value of Status.
ICriteria criteria = this.Session.CreateCriteria<MyObject>("obj")
.AddOrder(Order.Asc("Status"))
.List()
I'd really like to be able to order by the enum name. Any ideas would be greatly appreciated.

If you want to sort it in the database, you'd have to sort by a projection with a case statement, but this won't be able to use an index, and might not work depending on your version of NHibernate (there are bugs when sorting by projections that aren't in the select).
Something like this might work:
.AddOrder(Order.Asc(
Projections.SqlProjection(
"CASE {alias}.Status "
+ "WHEN 1 THEN 0 "
+ "WHEN 2 THEN 3 "
+ "WHEN 3 THEN 2 END",
new string[0], new IType[0])))
Another option (better for performance) is to add a property to your class such as StatusOrder which you set equal to the relative position of the current status in your enum and just sort by that field (which you can index).
Yet another option is to define a formula property on your class (ie. specify a formula in the mapping) where the formula specifies a value to sort by depending on the status.

The easy way is to simply refactor your enum so that values and names have the same ordering:
public enum MyStatusEnum
{
Active = 1,
Paused = 3,
Completed = 2
}
But you can always use the List.Sort method to do the job:
enum MyEnum
{
Alpha,
Beta,
Gama
}
static void Main(string[] args)
{
List<MyEnum> list = new List<MyEnum>()
{
MyEnum.Gama,
MyEnum.Beta,
MyEnum.Alpha
};
list.Sort((x, y) => x.ToString().CompareTo(y.ToString()));
}
NHibernate sorts result sets based on how they are stored in the database. From what you said I'm guessing your enums are being stored as integers, hence you won't be able to ask SQL Server to order them by their names because these names are not known by SQL Server.
Unless you store your enums as strings, like discussed in this SO Question, your only option will be to perform the ordering "in memory" and not in the database.

Related

accessing Altering & querying Sql Server 2005 Table by a table-id or table-index

Is there any way to address/refer to a SQL SERVER table, by its Unique Index rather its TableName ?
my Application is using multiple html tables, which are acutally within a form,
for CRUD opporations,(more R/U than Create or delete) of an Existing SQL Table.
now... that i need to mix two separated tables, in same update-form (that's my first 'mix')
as i am using Jquery to pass parameters to C# code behind, so it will take care of the
update opporations, and will be able to differentiate the source of the update post/request
and i do know, i could address the table by its TableName,
i just wanted to make sure, as i was searching for some information on the web,
i've encountred results with the Terms sql server 2005... table index or id,
though all results seem to have something to do with, what i can call, some kind of "manipulation", in oreder to create some indexing system /schema (i think it's like hdds has a FAT table)
and I Emphasize "Create",cause I was actualy looking for an existing /built in, numeric value for a "TableID", just the same as a Table has rows and each row has its row IDENTITY - Column.
so at this point i am turning back to the real question at the top .
so that's what i came up with aventually : a simple struct + a class to hold values for database
public class DBMetaDetails
{
public struct DbTable
{
public DbTable(string tableName, int tableId): this()
{
this.Name = tableName;
this.ID = tableId;
}
public string HtmlOutput;
public string Name { get; private set; }
public int ID { get; private set; }
}
}
public sealed class tblsIDs
{
public const int firstTbl= 1, SecondTbl = 2;
}
another class should be for table names
public sealed class tblsNames
{
public const string firstTbl= "firstTbl", SecondTbl = "SecondTbl";
}
another that will hold tables columns names ...etc

Fluid NHibernate, Custom Types and Id mapping

I have an object in C# that I want to use as a primary key in a database that auto-increments when new objects are added. The object is basically a wrapper of a ulong value that uses some bits of the value for additional hints. I want to store it as a 'pure' ulong value in a database but I would like get an automatic conversion when the value is loaded / unloaded from DB. IE, apply the 'hint' bits to the value based on the table they come from.
I went on a journey of implementing my own IUserType object based on number of examples I found online ( tons of help on this forum ).
I have an ObjectId class that acts is an object ID
class ObjectIdType: IUserType
{
private static readonly NHibernate.SqlTypes.SqlType[] SQL_TYPES = { NHibernateUtil.UInt64.SqlType };
public NHibernate.SqlTypes.SqlType[] SqlTypes
{
get { return SQL_TYPES; }
}
public Type ReturnedType
{
get { return typeof(ObjectId); }
}
...
}
I have a mapping class that looks like this:
public class ObjectTableMap()
{
Id(x => x.Id)
.Column("instance_id")
.CustomType<ObjectIdType>()
.GeneratedBy.Native();
}
At this point I get an exception at config that Id can only be an integer. I guess that makes sense but I was half expecting that having the custom type implemented, the native ulong database type would take over and work.
I've tried to go down the path of creating a custom generator but its still a bit out of my skill level so I am stumbling though it.
My question is, is it possible for me to accomplish what I am trying to do with the mapping?
I think, it is not possible, because your mapping uses the native generator for the Id. This can only be used for integral types (and GUIDs). You can try to use assigned Ids with your custom type, so you are responsible for assigning the values to your Id property.
There is another alternative: Why not set your information bits on class level, instead depending on your table? Your entities represent the tables, so you should have the same information in your entity classes. Example:
class Entity
{
protected virtual ulong InternalId { get; set; } // Mapped as Id
public virtual ulong Id // This property is not mapped
{
get
{
var retVal = InternalId;
// Flip your hint bits here based on class information
return retVal;
}
}
}
You could also turn InternalId into a public property and make the setter protected.

Fluent NHibernate Map Enum as Lookup Table

I have the following (simplified)
public enum Level
{
Bronze,
Silver,
Gold
}
public class Member
{
public virtual Level MembershipLevel { get; set; }
}
public class MemberMap : ClassMap<Member>
{
Map(x => x.MembershipLevel);
}
This creates a table with a column called MembershipLevel with the value as the Enum string value.
What I want is for the entire Enum to be created as a lookup table, with the Member table referencing this with the integer value as the FK.
Also, I want to do this without altering my model.
To map an enum property as an int column, use method CustomType.
public class MemberMap : ClassMap<Member>
{
Map( x => x.MembershipLevel ).CustomType<int>();
}
In order to keep the enum and lookup table in sync, I would add the lookup table and data to your sql scripts. An integration test can verify that the enum and lookup table values are the same.
If you wanted SchemaExport to create this table, add a class and mapping for it.
public class MembershipLevel
{
public virtual int Id { get; set; }
public virtual string Code { get; set; }
}
public class MembershipLevelMap : ClassMap<MembershipLevel>
{
Id( x => x.Id );
Map( x => x.Code );
}
If you are creating the table with SchemaExport, you will need to populate it as well:
foreach (Level l in Enum.GetValues( typeof( Level ))) {
session.Save( new MembershipLevel{ Id = (int) l, Code = l.ToString() });
}
I wouldn't do that because your Enum declaration is not dynamic, or simpler, it doesn't change without recompiling, while your lookup table may change at any moment. If the Enum's and lookup table's values don't match, what's next?
Another reason is if you change the Enum (in code), you'd have to synchronise it with the database table. Since Enums don't have an incremental key (PK), they can't be synchronised so simple. Let's say you remove one Enum member from your code and recompile it, what is supposed to happen? And if you change a value?
I hope I made my objections to this approach clear. So I strongly recommend storing the name or the value of your enum members. To store it by name, just map like this:
public class MemberMap : ClassMap<Member>
{
Map(x => x.MembershipLevel, "level")
.CustomType<GenericEnumMapper<Level>>()
.Not.Nullable();
}
To store the values, do as #Lachlan posted in his answer.
Or if you really need a lookup table and wants to use an Enum with strict checking, create a normal model with PK (or use value for this), KEY and VALUE. Create your enum with your static members, and make the application query the database for the names and values when you start it. If things don't match, do whatever you need. Additionally, this doesn't guarantee your table won't change while your program is running, so you better be sure it doesn't.

NHibernate.Mapping.Attributes.Filter

I'm mapping my database tables using NHibernate with NHibernate.Mapping.Attributes library and I got stuck to get the Filter attributes to work.
Suppose a class A that has a set of objects of class B. So, I have, the following:
[NHibernate.Mapping.Attributes.Set(0, Inverse = true, Lazy = NHibernate.Mapping.Attributes.CollectionLazy.False)]
[NHibernate.Mapping.Attributes.Key(1, Column = "ClassAId")]
[NHibernate.Mapping.Attributes.OneToMany(2, Class = "ClassB, Assembly")]
public virtual ISet<ClassB> ClassBs { get; set; }
I want to create a filter on this collection to bring only class B objects that satisfy a given criteria, such as Status = 1.
How can I create such Filter?
The where parameter of the Set mapping should be able help you out. Per the documentation the where parameter:
where: (optional) specify an arbitrary
SQL WHERE condition to be used when
retrieving or removing the collection
(useful if the collection should
contain only a subset of the available
data)
So to filter on Status (assuming Status is a SQL column in the table mapped for ClassB - though this column does not have to be mapped in the NHibernate mapping).
[NHibernate.Mapping.Attributes.Set(0,...., Where = "Status = 1", .....)]
...
public virtual ISet<ClassB> ClassBs { get; set; }

Nhibernate mapping: Encapsulating a list within an object

I have two tables Deal and Cost. Costs can be of a number of types, eg Planned, Unplanned. Every deal can have one an only one of each type of cost. This is easy enough to map as a many-to-one on deal, reulting in an IList of Costs. But what I want is to create a costs object that has named properties for each cost. So that for a database that looks like this:
Deal: Cost
ID Name ID DealID Type Value
--------- -------------------------------
1 Test 1 1 Planned 10
2 1 Unplanned 5
it is accessible like this
Deal.Costs.Planned = 10m;
Deal.Costs.Unplanned = 5m;
How is the best way to go about mapping this? Should I even be mapping it or should I just write the properties by hand to query the underlying collection?
The difficulty as i see it is mapping the properties of cost so that they map to different rows in teh same table. Using one of the columns as a discriminator.
Write the properties methods by hand:
public class Deal
{
public virtual Cost GetPlannedCost()
{
return _costs.FirstOrDefault(x => x.IsPlanned());
}
public virtual Cost GetUnplannedCost() {}
{
return _costs.FirstOrDefault(x => x.IsUnplanned());
}
public virtual Cost[] GetAllCosts()
{
return _costs.ToArray();
}
private ISet<Cost> _costs = new HashedSet<Cost>();
}
This doesn't really sound to me like Costs is a list.
I would map the Cost as a property on Deal. If you want to keep the Deal.Costs.Planned syntax you can use a Component mapping.
I would map Costs as a private collection and expose Planned and Unplanned costs on Deal. This would allow you to enforce "Every deal can have one an only one of each type of cost" as a business rule. It would look something like this:
public class Deal
{
private IList<Cost> _costs = new List<Cost>(2); // map this collection
public Cost PlannedCost
{
get { return _costs.FirstOrDefault(c => c.Type == CostType.Planned); }
set
{
var plannedCost = _costs.FirstOrDefault(c => c.Type == CostType.Planned);
if (plannedCost != null)
{
_costs.Remove(plannedCost);
}
_costs.Add(value);
}
}
public Cost UnplannedCost { // similarly }
}
You can have formula properties in your class using Formula tag of NHibernate mapping.A formula is simply a SQL statement that will be inserted as a subquery in a mapping query.
Obviously Formula properties are read only and can't be updated.
Since you want to calculate these properties for each Deal you should add them as Deal properties (if you'd like to use formula) thus you will have:
Deal.UnPlannedCosts
Deal.PlannedCosts
Here's an article that explains formula properties.