I'm beginning to wonder if I am in fact too dumb to use NHibernate. I'm currently using FluentNHibernate to create simple db mappings and that's worked well for us. When dealing w/ our various classes in isolation, we're able to read and write, perform updates, etc. to all of those classes. My problem however is in trying to build up a query that involves anything more complex than filtering upon fields of the entity type. A perfect example is illustrated below--
The pertinent portions of the mappings:
public class UserMap : ClassMap<User> {
Id(u => u.Id).Column("UserID").GeneratedBy.Identity();
//other non-pertinent fields
}
public class RoleMap : ClassMap<Role> {
Id(r => r.Id).Column("RoleId").GeneratedByIdentity();
//snip
}
public class RoleMapMap : ClassMap<RoleMap> {
Id(rm => rm.Id).Column("RoleMapId").GeneratedByIdentity();
Map(rm => rm.UserId);
Map(rm => rm.RoleId);
//snip
}
The intent is to generate a query w/ the Criteria API to retrieve all users of a specific role--at a high level, filter rolemap based on a specific role ID, then join to Users, and return only those users.
Attempted with following, but my usage of CreateAlias is obviously flawed, as the runtime exception basically tells me that it has no idea what "RoleMap" in the below is as it relates to the User object.
var criteria = session.CreateCriteria<User>().
CreateAlias("RoleMap", "rm").
Add(Expression.Eq("rm.UserId", "UserId")).
Add(Expression.Eq("rm.RoleId", 99)).
SetResultTransformer(new
DistinctRootEntityResultTransformer());
var users = criteria.List<User>();
Can someone point me in the right direction? I'd prefer not to edit the underlying objects to expose collections--(e.g. a User.Roles[] collection) as there's cases where we specifically have tables used solely for joins but we don't want floating to the middle tier. So learning how to join isolated classes is going to matter to us.
Your mapping contains no way to navigate from User to RoleMap, yet that is what you are trying to do in your Criteria API call. You have multiple options. Here are a couple:
1) Allow User to navigate to RoleMap in your mapping. This is the easiest and how it's normally done.
2) Use two queries, one to get a list of UserIds based on the RoleMap to Role relationship and then a second query to get all the Users for those UserIds.
You say you don't want a User.Roles collection in your middle tier, but NHibernate should exist in your data layer, not necessarily your business layer. You can allow NHibernate to know about User.Roles while effectively hiding it from your business layer.
Joining isolated classes isn't really what ORMs are built for. ORMs are built for joining related classes. Related classes are generally mapped to related tables at the database level. To join isolated classes, you are going to need to do things like option 2 above where you run multiple queries and/or work around the lack of a relationship in custom code.
Related
I'm building a custom ORM using Dapper. I have the following tables:
User
Role
Permission
RolePermission
My sproc has 2 selects: 1 for basic user info, 1 for user-role info. The basic user info gets deserialized into a user object. The RolePermission info gets deserialized into a custom RolePermission object and then programatically associated to the User object. How complex would you make the object graph implementation in a custom ORM? For example, EF takes a nice and easy OO approach to get role name like this: User.UserRoles.Role.Name. However, this is accomplished through the use of a complex and sophisticated f/w which automatically maps the entity joins based on db fk associations. It also comes with performance overhead. My thought is to create my entity classes as follows:
MyCompany.Entities.Entity.User.User
MyCompany.Entities.Entity.User.RolePermission
Therefore, my RolePermission object would be completely tailored to the User entity with no external dependencies. This would keep the RolePermission object as lightweight as possible in the context of the User object. It would not need any additional properties to support other entities/domains/usages. This seems like an elegant (easy, effective, efficient) solution. What are your thoughts on this type of approach for creating a complex object in a custom ORM?
I do this sort of thing all the time and it's really quite easy. You'll do it in two queries. The two queries can be in the same sproc and return different resultsets or can be two separate calls to the database. I typically do the latter even though we use mssql which allows multiple resultsets returned.
So first off: you're querying Users (shallow) and optionally details of a User including role information (deep).
public IEnumerable<User> GetUsers(int? userID, bool withDetails)
{
var users = db.Query<User>(#" select *
from dbo.Users u
where (#userID IS NULL OR u.Id = #userID)", new { userID });
if (withDetails)
{
var rolePermissions = db.Query<RolePermission>(#" select *
from dbo.RolePermissions rp
where rp.UserId IN (select val from dbo.udf_ConvertIntListToTable(#userId_list))", new { userId_list = String.Join(',', users.Select(s => s.UserId)) });
foreach(var user in users)
{
user.RolePermissions = rolePermissions.Where(w => w.UserId == user.UserId);
}
}
}
A few notes:
dbo.udf_ConvertIntListToTable is very well established and performs great. Search the web and you'll find it. I'll load it up as a gist if you can't find it.
RolePermission needs to have a reference to the outer User (I used UserId). Also, that query will likely map the additional data you mentioned (e.g. Role, Permission, etc.)
I have tons of extensions that tidy up the above. I kept it as raw as possible so you can see what's going on.
You now have a very light query (withDetails=false) for lists and such, and a heavier query with all the data nicely wrapped up into its aggregate root (User).
Disclaimer: I'm outlining simplified picture to emphasize main point of my question.
The application I'm working on maintains a set of resources. We have a corresponding table and mapped entity in NHibernate. Each resource identified by integer id. Also we have user table and corresponding entity to maintain user profiles.
We need to log user accesses to the application and retrieve access history. For repository class we have to introduce 2 methods:
IEnumerable GetUserLog(User user) to retrieve user access history order by date in descending order and
void WriteLogEntry(User user, Resource resource) to write new entry
I have tried to simply define LogEntry entity as following:
public class LogEntry
{
public virtual User User {get; set;}
public virtual Resource Resource {get; set;}
public virtual DateTime Date {get; set;}
}
and map it using Fluent NHibernate as usually. To retrieve/update log entries we can simply use
Session.Query<LogEntry>().Where(entry => entry.User = currentUser).OrderByDesc(entry => entry.Date)
Session.Save(new LogEntry() {
User = currentUser,
Resource = resource,
Date = DateTime.Now
})
This is the most convenient way to deal with this issue for us.
Problem
The problem is that NHibernate requires id mapping. We have to use composite id here and the only option is to map User, Resource and Date columns because only this combination provides uniqueness. Also in case of composite id we have to override Equals and GetHashCode and all this seems to be overkill for such a simple task. Another problem that lazy loading cannot be used for id fields and it's too much as well. We do not want to load all related Resource entities in advance.
Another possible solution is to define plain class, not entity and then use SetResultTransformer(Transformers.AliasToBean()) to retrieve results. In that case we have to construct queries manually, retrieve related entities manually and this way it's not better in general then dealing with raw connection.
I would like to ask expert opinion because I'm confident people around had similar experience and can help. Thanks in advance.
P.S. This is ASP.NET MVC application using NHibernate 3 (+ Fluent). Log information will be used to display last 5-10 resources user accessed.
have you considered introducing an Id field for LogEntry table as well?
many DBAs will recommend it and it seems like the easiest solution.
Given a multi level object graph being called using Future as:
var Dads = db.Session.Query<Parent>().Where(P => P.EntityKey == Id)
.ToFuture<Parent>();
var Kids = db.Session.Query<Kid>().Where(K => K.Parent.EntityKey == Id)
.ToFuture<Kid>();
when I call var Dad = dads.ToList() I see the batch go across the wire and show in profiler.
Problem is when enumerating the collection it is still sending one off queries to the db
Eg.
for each (Kid kid in Dad.Kids) // This seems to hit the database
{
Teach(kid);
}
Sends a SQL query and hits the database to get each kid. Why is the object graph not populated? or is this expected behavior?
That behaviour is to be expected. You are simply telling NHibernate to get two collections from the database in a batch, which it is doing as told. However, you are not telling it that they are related. NH Queries with Futures do not put entities together after executing them unless they are told to do so with a join.
If you executed the separate queries without Futures you would not expect the Parent entity to suddenly have the child collection filled. Basically, Futures allow you to run things in one roundtrip. If the queries happen to have a common root with several child collections (e.g. to avoid a cartesian product), then NH is able to "combine" several collections into one entity.
Unfortunately joins with the NH LINQ Api and the ToFuture() method seem to pose a problem in the current (NH 3.0 or 3.1) implementation. You may need to use the QueryOver Api in that case.
On a side note, I think the method name is not appropriate.
Edit: After Edit of the question the method name is now ok.
I have an object called "Customer" which will be used in the other tables as foreign keys.
The problem is that I want to know if a "Customer" can be deleted (ie, it is not being referenced in any other tables).
Is this possible with Nhibernate?
What you are asking is to find the existence of the Customer PK value in the referenced tables FK column.
There are many ways you can go about this:
as kgiannakakis noted, try to do the delete and if an exception is thrown rollback. Effective but ugly and not useful. This also requires that you have set a CASCADE="RESTRICT" in your database. This solution has the drawback that you have to try to delete the object to find out that you can't
Map the entities that reference Customer as collections and then for each collection if their Count > 0 then do not allow the delete. This is good because this is safe against schema changes as long as the mapping is complete. It is also a bad solution because additional selects will have to be made.
Have a method that performs a query like bool IsReferenced(Customer cust). Good because you can have a single query which you will use when you want. Not so good because it may be susceptible to errors due to schema and/or domain changes (depending on the type of query you will do: sql/hql/criteria).
A computed property on the class it self with a mapping element like <property name="IsReferenced" type="long" formula="sql-query that sums the Customer id usage in the referenced tables" />. Good because its a fast solution (at least as fast as your DB is), no additional queries. Not so good because it is susceptible to schema changes so when you change your DB you mustn't forget to update this query.
crazy solution: create a schema bound view that makes the calculation. Make the query on it when you want. Good because its schema-bound and is less susceptible to schema changes, good because the query is quick, not-so-good because you still have to do an additional query (or you map this view's result on solution 4.)
2,3,4 are also good because you can also project this behavior to your UI (don't allow the delete)
Personally i would go for 4,3,5 with that preference
I want to know if a "Customer" can be deleted (ie, it is not being referenced in any other tables).
It is not really the database responsibility to determine if the Customer can be deleted. It is rather part of your business logic.
You are asking to check the referential integrity on the database.
It is ok in non OOP world.
But when dealing with objects (like you do) you better add the logic to your objects (objects have state and behavior; DB - only the state).
So, I would add a method to the Customer class to determine if it can be deleted or not. This way you can properly (unit) test the functionality.
For example, let's say we have a rule Customer can only be deleted if he has no orders and has not participated in forum.
Then you will have Customer object similar to this (simplest possible case):
public class Customer
{
public virtual ISet<Order> Orders { get; protected set; }
public virtual ISet<ForumPost> ForumPosts { get; protected set; }
public virtual bool CanBedeleted
{
get
{
return Orders.Count == 0 && ForumPosts.Count == 0
}
}
}
This is very clean and simple design that is easy to use, test and does not heavily relies on NHibernate or underlying database.
You can use it like this:
if (myCustomer.CanBeDeleted)
session.Delete(mycustomer)
In addition to that you can fine-tune NHibernate to delete related orders and other associations if required.
The note: of course the example above is just simplest possible illustrative solution. You might want to make such a rule part of the validation that should be enforced when deleting the object.
Thinking in entities and relations instead of tables and foreign keys, there are these different situations:
Customer has a one-to-many relation which builds a part of the customer, for instance his phone numbers. They should also be deleted by means of cascading.
Customer has a one-to-many or many-to-many relation which is not part of the customer, but they are known/reachable by the customer.
Some other entity has a relation to the Customer. It could also be an any-type (which is not a foreign key in the database). For instance orders of the customer. The orders are not known by the customer. This is the hardest case.
As far as I know, there is no direct solution from NHibernate. There is the meta-data API, which allows you to explore the mapping definitions at runtime. IMHO, this is the wrong way to do it.
In my opinion, it is the responsibility of the business logic to validate if an entity can be deleted or not. (Even if there are foreign keys and constraints which ensures integrity of the database, it is still business logic).
We implemented a service which is called before deletion of an entity. Other parts of the software register for certain types. They can veto against the deletion (eg. by throwing an exception).
For instance, the order system registers for deletion of customers. If a customer should be deleted, the order system searches for orders by this customer and throws if it found one.
It's not possible directly. Presumably your domain model includes Customer's related objects, such as Addresses, Orders, etc. You should use the specification pattern for this.
public class CustomerCanBeDeleted
{
public bool IsSatisfiedBy(Customer customer)
{
// Check that related objects are null and related collections are empty
// Plus any business logic that determines if a Customer can be deleted
}
}
Edited to add:
Perhaps the most straightforward method would be to create a stored procedure that performs this check and call it before deleting. You can access an IDbCommand from NHibernate (ISession.Connection.CreateCommand()) so that the call is database agnostic.
See also the responses to this question.
It might be worth looking at the cascade property, in particular all-delete-orphan in your hbm.xml files and this may take care of it for you.
See here, 16.3 - Cascading Lifecycle
A naive solution will be to use a transaction. Start a transaction and delete the object. An exception will inform you that the object can't be deleted. In any case, do a roll-back.
Map the entities that reference Customer as collections. Name each collection in your Customer class with a particular suffix.For example if your Customer entity has some Orders, name the Orders collection as below:
public virtual ISet<Order> Orders_NHBSet { get; set; } // add "_NHBSet" at the end
Now by using Reflection you can get all properties of Customer at run time and get those properties that their names ends with your defined suffix( In this case "_NHBSet" ) Then check each collection if they contain any element and if so avoid deleting customer.
public static void DeleteCustomer(Customer customer)
{
using (var session = sessions.OpenSession())
{
using (var transaction = session.BeginTransaction())
{
var listOfProperties =typeof(Customer).GetProperties();
foreach (var classProperty in listOfProperties )
{
if (classProperty.Name.EndsWith("_NHBSet"))
{
PropertyInfo myPropInfo = typeof(Customer).GetProperty(classProperty.Name);
dynamic Collection = myPropInfo.GetValue(customer, null);
if (Enumerable.FirstOrDefault(Collection) !=null)// Check if collection contains any element
{
MessageBox.Show("Customer Cannot be deleted");
return;
}
}
}
session.Delete(customer);
transaction.Commit();
}
}
}
The Advantage of this approach is that you don't have to change your code later if you add new collections to your customer class.And you don't need change your sql query as Jaguar suggested.
The only thing you must care about is to add the particular suffix to your newly added collections.
Lets say I have a class representing a course.The course has its own attributes like subject name, descriprion, start- and an ending date and so on.
Then, the course has attributes like a list of participants. In the database this is obviously represented in two classes; the course and a participant table in a one-to-may relationship.
My question is about how to set the list of participant in the course class: Should it be the course class itself that is fetching the data (through a data access layer, or a layer above), or should one delegate the fetching and the setting of the participants to some kind of helper class, making the course class it's self more or less a dumb object, only holding data?
In RDD(Resopnsible driven design) it tells us to make smart object and abstract away the differentce between data and behaviours. In this regards it sounds obvious that the course class should handle the fetching of the participants. Doing this howerver, is creating a direct dependence on a data access object (or a level above), making it more couple up.
Any thought on this would be helpful.
Should it be the course class itself that is fetching the data
This is a pattern known as Active Record. Be aware that many feel that active record is an anti-pattern.
or should one delegate the fetching and the setting of the participants to some kind of helper class
This is a pattern known as Repository.
making the course class it's self more or less a dumb object, only holding data?
Removing the responsibility of saving and retrieving data from the entity doesn't make that entity a dumb object, only holding data. The entity can still hold domain logic, which is a common practice when practicing Domain-Driven Design. In DDD, entities as simple data containers without behavior are called anemic domain model.
The Course class shouldn't be responsible for fetching the participants, in fact, it shouldn't be responsible for fetching the Courses. You bring up the correct point of data access layers, but the course class itself should not work with this layer, it must just represent a course.
You create a class which has the responsibility of fetching the data, i.e the data access layer. You can name this something similar to CourseDao, but the important part is; that it simply gets the data from the database, and returns it to the client as Courses.
This class has methods like Create, Read, Update and Delete.
Now, you want to do the same for participants, with one small difference. Since your Participant table has a foreign key to Course, your ParticipantDao will have an overloaded Read.
Example:
public class ParticipantDao{
public void create(Participant participant){
//Insert participant in db
}
public Participant read(int id){
//read participant from db
}
public List<Participant> read(){
//read all participants from db
}
public List<Participant> read(Course course){
//read all participants in this course from the db
}
public void Update(Participant participant){
//update and so on.
}
}
And your CourseDao can use this ParticipantDao by going something like:
foreach(Course course in read()){
course.setParticipants(this.participantDao.read(course));
}
In short, you have an object to access the data in the database, which is not the same object that represents said data. When you have a One-To-Many relation, these access objects can work together to retrieve the correct data.