I'm starting a new project and plan on using nhibernate. I'm struggling with whether or not I should keep my domain model clean of persistence information by leaving out foreign keys. I keep feeling like some of my "model" isn't really necessary and that performance is going to be an issue. For example:
In my db I have the following table:
Posting
Id
StatusId
...
Which has an FK relationship with this table:
PostingStatus
Id
Name
In my model I've defined 2 classes:
class Posting
{
virtual int Id { get; set; }
virtual PostingStatus Status { get; set; }
// ..
}
class PostingStatus
{
virtual int Id { get; set; }
virtual string Name { get; set; }
}
Does PostingStatus belong in my model? and in cases where I know the FK ahead of time, like updating a posting after a submit, isn't it a pretty heavy performance hit (or just useless work) to have nhibernate fetch a PostingStatus instance instead of just setting a FK?
I'm pretty sure this issue has been discussed before but I keep finding bits an pieces of the discussion that have shotty relevance. Thoughts or resources on this issue would be greatly appreciated.
If the Domain Model requires relationships to be enforced then you need Foreign Keys.
Don't worry about performance until you actually have a performance problem.
What Mitch said. NHibernate was put together by people who thought about these issues thoroughly. Do what's right, worry about optimization when and if you have a problem.
(nhibernate's probably going to be fetching the posting sttaus from cache, anyway.)
Besides, nhibernate, depending on how you set it up, may need FK constraints to map your database to your objects.
If PostingStatus is an domain entity, you should keep it. If it's not, remove it for that reason, not in an attempt to prematurely optimize.
In this very special case you might actually replace the entity PostingStatus with Enum. If you specify values for your enum properly they can map to a table and be enforced by FK.
And NH will not 'just cache' stuff. I'd suggest using 2nd level cache with lazy loading of PostingStatus heavily for this kind of situations.
Related
Source:
DictionaryTable:
DictID int
RUWordID int -> FK to RUWordsTable.RUWordID
ENWordID int -> FK to ENWordsTable.ENWordID
RUWordTable:
RUWordID int
Word string
ENWordTable:
ENWordID int
Word string
I want be able read/write data to table that uses following structure:
RUWord ENWord
Привет Hello
...
What the best option in terms of speed and easy data access from MVC views to do what i want. As per my understanding there is options to create SQL view and use it (but not sure about possible INSERTS of data). Or just create the same but using Entity Framework in Visual Studio designer.
Basically one word could be transalted differently (have several entries in other table). My goal to find a way how to add words to dictionary with automatic inserts (when source or translation not found add it to reference table, if found - just use existing entry)
It depends much on the amount of data you need to read in one go. If you want to look up translations of one or a few words an Entity Framework model will do a perfect job. If you need to fetch massive amounts of data a view or stored procedure will be better. But I don't expect the latter to happen. In an MVC view you'll probably use paging.
As for inserting data EF will be a viable choice. You already have a junction table defined (DictionaryTable). You could set up EF in a way that the junction table is transparent:
class RuWord
{
public int Id { get; set; }
public string Word { get; set; }
public ICollection<EnWord> EnWords { get; set; }
}
class EnWord
{
public int Id { get; set; }
public string Word { get; set; }
public ICollection<RuWord> RuWords { get; set; }
}
(supposing you'd work code-first)
In data entry you can add a new Russian word to the RussianWords DbSet of the context and add new or existing English words to the word's EnWords collection and call SaveChanges(). EF will insert the words and the appropriate records in the junction table (having both foreign keys as its composite primary key).
But... In real life I hardly ever see a pure junction table. I bring this in because in your case I can hardly imagine that just registering the associations between Russian and English words will be sufficient. Are you not (at least) going to need some degree of preference? Like there are more translations of the word "date", but the preferred one would be the calendar thing (unless you're working for a dating site, but even then...). Anyway, if there is something you want to record about the association you need to map the junction table explicitly and create the association records will all their details in code.
I hope this gives some directions.
Edit (after your comment)
If you use an explicit junction entity (i.e. a class that corresponds with the junction table). This entity can have the two properties EnWord and RuWord as references to the two tables mentioned above. Data entry would imply creating a new DictionaryTable instance and setting its properties (which can be new or existing words).
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.
Okay, I think I have a scenario I haven't seen elsewhere. I have a situation where I have an object type that needs to be able to be changed over time while retaining the history of the changes within the same table (as opposed to just in an audit table).
public class Item
{
public virtual int Id { get; set; }
public virtual ....
public virtual Item Replaces { get; set; }
public virtual Item ReplacedBy { get; set; }
}
I am stuck as to how to represent this in Fluent NHIbernate. In the database, I don't need both a Replaces and ReplacedBy field - I can get the info from just one of them. But I think I need both in my object for NHibernate to figure it out.
Currently, I have this, but it generates no database mappings at all:
mapping.HasOne(t => t.ReplacedBy).ForeignKey("Id").Class(typeof(Item));
As always, any thoughts greatly appreciated!
A HasOne or one-to-one relationship is a bit of a special relationship in NHibernate. It's (typically) an inferred relationship between two separate tables, whereby records that share a primary key value are associated. It's unlikely you'd be able to get a HasOne working for your situation. I have a post on my blog, I think you mean a many-to-one, sir, which goes into some of the misconceptions of HasOne relationships.
Instead you should look at using References, which is a many-to-one relationship, and does support self referential relationships.
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.
I'm using Castle ActiveRecord, but this question applies to NHibernate, too, since a solution that works with NHibernate should work for ActiveRecord. Anyway, what I have is an underlying table structure like this:
TableA -hasMany-> TableB
I have corresponding objects EntityA and EntityB. EntityA has an IList of EntityB objects. This part works fine. Now, I want EntityB to have some kind of reference back to EntityA. I know I can use the BelongsTo attribute on EntityB to give it an actual reference back to the full EntityA type, like:
[BelongsTo("tableAid")]
public EntityA Parent { get; set; }
But what I'd really like to do is:
[BelongsTo("tableAid")]
public int ParentId { get; set; }
So, EntityB would store only the ID of the parent object, not a reference to the actual object. This is a trivial example, but I have good reasons for wanting to go with this approach. In the application I'm working on, we have pages that display specific EntityB-like objects, and we'd like for those pages to include links (as in hyperlinks) to the corresponding parent pages. We can do that by using the first approach above, but that requires that the entire EntityA object be loaded when all I really need is the ID. It's not a huge deal, but it just seems wasteful. I know I can use lazy-loading, but again, that seems more like a hack to me...
I have tried flagging the foreign key with the [Property] attribute like so:
[Property]
public int ParentId { get; set; }
The problem with this approach is that EntityB.ParentId remains null when you do a EntityA.SaveAndFlush() on a new object tree. The correct value is being written to the database, and I can force the value back into EntityB.ParentId by doing an EntityA.Refresh(), but again, that seems like a bit of a hack.
Lazy loading is exactly what you want - and it's not a hack either, it's a well tested and baked in part of NHIbernate and an important tool when performance tuning any substantial NHibernate app.
If you were to mark your "parent" EntityA as lazy loaded, referring to EntityB.Parent.Id would not load EntityA at all (as behind the scenes NHIbernate has already loaded EntityA's id when loading EntityB) - thus letting you setup your links without incurring a performance penalty.
Just this:
[Property] public int ParentId { get; set; }
...assuming ParentId is the actual column name.
A couple of other comments.
First, you should consider lazy loading many-to-one properties anyway. If you eagerly load them, you must be aware of possible cascades of eager loads, which can make a serious performance hit. To do this you must mark all public members of the lazily loaded class as virtual.
Second, be aware that any time you have a one-to-many association with no corresponding relation from the child back to the parent, you must make the FK nullable in the database. That's because when NH creates new child items, it will insert it with the parent id null and then in a second step update it.