I'm in the process of early research to replace EF with NHibernate in a system that is implemented using a custom written app engine.
The client creates an entity say, Person, gets the property values from the user and sends it to the server. In the server, we used context.CreateObject<Person>() to obtain a proxy of a Person.
Then the property values where applied to the proxy and we were able to Use Lazy loading to access related objects. For instance, if the person had a property MajorID and a navigation property Major, we set MajorID to 5 and if an expression accesses Major, the correct Major is lazily loaded into this field. A similar process happens for updates.
In NHibernate, I don't have a CreateOject<>() method, so the object I have is not proxied, and therefore lacks Lazy-Loading. Also, in Update, if I Change MajorID and then access Major, it still points to the old, wrong Major.
I'm quite new to NHibernate, can I have it behave like EF in this scenario?
Thanks
You don't need CreateObject at all in NHibernate.
The implementation of proxies in NH is completely different from EF, and it's based on entities, not properties.
Here's how this would work in NH:
var person = new Person(); //no proxy needed; person is a new entity
person.Major = session.Load<Major>(majorId); //creates Major proxy, no DB call
//...somewhere else
var majorId = person.Major.Id; //getting the Id does not cause loading, ever
var foo = person.Major.Foo; //this causes lazy-loading if not already loaded.
An interesting thing to note is that there is no MajorId property, nor there is need for one, because unlike EF, you can create proxies for already persisted entities.
I think you can achieve wat you use by session.Get<> and passing the ID of the person you want to load. For the id part, you generally should not works with it directly when you maniulate associations. Why dont you just try something like:
var p = session.Get<Person>(personId);
p.Major = session.Get<Majors>(mayorId);
session.Update(p);
Related
I need to create DTOs from NHibernate POCO objects. The problem is that the POCO objects contain dynamic proxies, which should not be copied to the DTO. I eager load all the collections and references I need to transfer in advance, I don't want NHibernate to start loading referenced collections which I did not load in advance.
Several similar questions on SO received answers which either:
Suggest Session.GetSessionImplementation().PersistenceContext.Unproxy();
Suggest turning off Lazy Loading.
In my case the first suggestion is irrelevant, as according to my understanding it causes eager loading to replace the proxies. In reality, it doesn't even work - it doesn't remove the proxies in my objects. (Any explanation why?)
The second suggestion, turning off lazy loading seems to cause all references and collections to eager load, basically loading the entire DB. My expectation was that if lazy loading is off, and I have not requested a collection, it will not be loaded. (Am I correct that NHibernate offers no such option?)
I am using NHibernate 3.3.1 with fluent configuration.
To reiterate my main question, I need to create DTOs clean of proxies, copied from POCOs which contain proxies, and I don't want to load the data behind those proxies.
Any helpful suggestion which includes example code and automates the process with ValueInjecter / AutoMapper will be immensely helpful.
Edit #1:
Following Roger Alsing's suggestion to use projections, I realized that what I'm actually looking for is a ValueInjecter-like convention based mapping. Here is why. Initially, my DTOs will be defined the same as the model's POCOs. This is due to a large code base which depends on the existing POCOs being transferred on the client-side project.
Using projections, I will have to specify which subset of fields have to be copied, and this subset may be different in each context (as, ideally, a DTO would differ). This will mean a lot of new code introduced to the server side, when there should be the second option.
Using ValueInjecter, I will be able to populate the DTOs by convention in one call, without writing specific projections, or having to maintain those into the future. That is, if I am able to have ValueInjecter ignore Proxy objects.
Given that using projections is a good but not ideal solution in my situation, is there a way to configure something like ValueInjecter to copy POCOs without copying proxies or triggering eager/lazy loads on copy?
I solve this by selecting DTO's as projections using Linq or whatever query language the O/R Mapper may have.
e.g.
return from c in customers
select new CustomerDTO
{
Name = c.Name ,
Orders = c.Orders.Select (o => new OrderDTO {...} )
};
This way, you don't need to resort to reflection magic or any other fancy stuff.
And the query fetches exactly what you need in one go, thus, this is usually much more efficient than fetching entities and then transforming them to DTO's in mem.
(it can be less efficient in some cases incase the resulting SQL query contains extra joins for whatever reason..)
I'm using the following ValueResolver with AutoMapper:
/// <summary>
/// ValueResolver that will set NHibernate proxy objects to null, instead of triggering a lazy load of the object
/// </summary>
public class IgnoreNHibernateProxyValueResolver : IValueResolver
{
public ResolutionResult Resolve(ResolutionResult source)
{
var prop = source.Type.GetProperty(source.Context.MemberName).GetValue(source.Value, null);
var proxy = prop as INHibernateProxy;
if (proxy != null && proxy.HibernateLazyInitializer.IsUninitialized)
{
return source.Ignore();
}
return source.New(prop);
}
}
for ValueInjecter solution I recommend using SmartConventionInjection (you need to copy the code from the linked page into your solution)
and after specify a convention that won't touch the proxy properties
here's a start:
public class MapPoco: SmartConventionInjection
{
protected override bool Match(SmartConventionInfo c)
{
return c.SourceProp.Name == c.TargetProp.Name;
}
}
Take a look on Projections in Introduction to QueryOver in NH 3.0
CatSummary summaryDto = null;
IList<CatSummary> catReport =
session.QueryOver<Cat>()
.SelectList(list => list
.SelectGroup(c => c.Name).WithAlias(() => summaryDto.Name)
.SelectAvg(c => c.Age).WithAlias(() => summaryDto.AverageAge))
.TransformUsing(Transformers.AliasToBean<CatSummary>())
.List<CatSummary>();
I use LinqToEntitiesDomainService class to update database with Silverlight 4 client.
There's AttachAsModified extended method for entity framework ObjectContext which allows you supply original entity property values:
Order original = this.ChangeSet.GetOriginal(currentOrder);
this.ObjectContext.Orders.AttachAsModified(currentOrder, original);
By default, WCF RIA Services doesn't send original values to the server, so one needs to
apply [RoundtripOriginal()] attribute to his/her entity.
However, even if I supply original values, SQL generated by Entity framework updates all columns, not only changed ones. Since AttachAsModified() method isn't native ObjectContext class method (it's extended method defined in ObjectContextExtensions class), I tried to use
ApplyOriginalValues method which is defined in ObjectSet class. No change.
It seems entity framework 4.1, which was released recently may have solution (not sure). How about entity framework 4? Is it possible EF to generate sql to update only changed columns?
AttachAsModified will mark the entity as modified. Subsequently (quote from MSDN):
When you change the EntityState of an
entity object entry to Modified, all
of the properties of the object are
marked as modified, regardless of the
current or original values.
Caveat; I haven't done this but, it should work.
Instead of using AttachAsModified, mark the entity as UnChanged using the ChangeState method.
Then use the SetModifiedProperty method on the properties that have changed to have them included in an update.
EDIT: If you want a way to find which properties have changed, there are a couple of articles out there explaining how to do so using the ObjectStateManager such as this one
I did ask similar question on MSDN forums, and it is confirmed that WCF RIA Services will change all columns. Alternative is,
You can fetch a copy from database, compare and mark SetModifiedProperty manually by using reflection.
// change state of entity as Unmodified/Unchanged...
original.EntityState = Unchanged;
// this is copy form database...
// Use different context
MyOrderContext context = new MyOrderContext();
Order dbOriginal = context.Orders.First( x=>x.OrderID == original.OrderID);
foreach(PropertyInfo p in copy.GetTypes().GetProperties()){
Object originalValue = p.GetValue(dbOriginal);
Object newValue = p.GetValue(original);
if(originalValue!=null && newValue!=null
&& originalValue.Equals(newValue)){
continue;
}
// resetting this will
// make entity's only current
// property as changed
p.SetValue(original,originalValue);
p.SetValue(original,newValue);
}
You may have to change code as per situation, check if property is readonly or not and this is just a sample but it will help you to build upon it.
I managed to do this by first attaching the object and then calling ApplyOriginalValues on the EntitySet. You'll need an object with the original values to do this. This method can also be used to prevent a column from being updated, e.g. for row level security.
NOTE: This unfortunately does not work without retrieving the original entity from the database first. Otherwise only properties that are set to its default value are excluded from the update...
I am using Model-Glue/Coldspring for a new application and I thought I would throw CF9 ORM into the mix.
The only issue I am having right now is with populating an entity with an object. More or less the code below verifies that only one username can exist. There is some other logic that is not displayed.
My first thought was to using something like this:
var entity = entityload('UserAccount' ,{UserName=arguments.UserAccount.getUserName()},"true")
entity = arguments.UserAccount;
How ever this does not work the way that I expected. Is it even possible to populate an entity with an object or do I need to use the setters?
Not sure if this is what you're looking for. If you have...
component persistent="true" entityName="Foo"
{
property a;
property b;
}
You can pass a struct in the 2nd param to init the entity (added in CF9.0.1 I believe)
EntityNew("Foo", {a="1",b="2"});
To populate Foo with another object, you can use the Memento pattern, and implement a GetMemento() function to your object that returns a struct of all its properties.
EntityNew("Foo", bar.getMemento());
However, CF does NOT call your custom setters! If you want to set them using setters, you may add calls to the setters in your init() constructor, or use your MVC framework of choice to populate the bean. In Model-Glue, it is makeEventBean().
Update: Or... Here's hack...
EntityNew("Foo", DeserializeJSON(SerializeJSON(valueObject)));
Use this at your own risk. JSON might do weird things to your numbers and the 'yes','no','true','false' strings. :)
Is it even possible to populate an entity with an object or do I need to use the setters?
If you mean "Is it possible to create load an ORM Entity from an instance of that persistent CFC that already exists and has properties set?", then yes you can using EntityLoadByExample( object,[unique] )
entity = EntityLoadByExample( arguments.userAccount,true );
This assumes the userAccount CFC has been defined as persistent, and its username value has been set before being passed in (which seems to be the case in your situation).
Bear in mind that if any other properties have been set in the object you are passing, including empty strings, they will be used as filters to load the entity, so if they do not exactly match a record in your database, nothing will be loaded.
The setup
Some of the "old old old" tables of our database use an exotic primary key generation scheme [1] and I'm trying to overlay this part of the database with NHibernate. This generation scheme is mostly hidden away in a stored procedure called, say, 'ShootMeInTheFace.GetNextSeededId'.
I have written an IIdentifierGenerator that calls this stored proc:
public class LegacyIdentityGenerator : IIdentifierGenerator, IConfigurable
{
// ... snip ...
public object Generate(ISessionImplementor session, object obj)
{
var connection = session.Connection;
using (var command = connection.CreateCommand())
{
SqlParameter param;
session.ConnectionManager.Transaction.Enlist(command);
command.CommandText = "ShootMeInTheFace.GetNextSeededId";
command.CommandType = CommandType.StoredProcedure;
param = command.CreateParameter() as SqlParameter;
param.Direction = ParameterDirection.Input;
param.ParameterName = "#sTableName";
param.SqlDbType = SqlDbType.VarChar;
param.Value = this.table;
command.Parameters.Add(param);
// ... snip ...
command.ExecuteNonQuery();
// ... snip ...
return ((IDataParameter)command
.Parameters["#sTrimmedNewId"]).Value as string);
}
}
The problem
I can map this in the XML mapping files and it works great, BUT....
It doesn't work when NHibernate tries to batch inserts, such as in a cascade, or when the session is not Flush()ed after every call to Save() on a transient entity that depends on this generator.
That's because NHibernate seems to be doing something like
for (each thing that I need to save)
{
[generate its id]
[add it to the batch]
}
[execute the sql in one big batch]
This doesn't work because, since the generator is asking the database every time, NHibernate just ends up getting the same ID generated multiple times, since it hasn't actually saved anything yet.
The other NHibernate generators like IncrementGenerator seem to get around this by asking the database for the seed value once and then incrementing the value in memory during subsequent calls in the same session. I would rather not do this in my implementation if I have to, since all of the code that I need is sitting in the database already, just waiting for me to call it correctly.
Is there a way to make NHibernate actually issue the INSERT after each call to generating an ID for entities of a certain type? Fiddling with the batch size settings don't seem to help.
Do you have any suggestions/other workarounds besides re-implementing the generation code in memory or bolting on some triggers to the legacy database? I guess I could always treat these as "assigned" generators and try to hide that fact somehow within the guts of the domain model....
Thanks for any advice.
The update: 2 months later
It was suggested in the answers below that I use an IPreInsertEventListener to implement this functionality. While this sounds reasonable, there were a few problems with this.
The first problem was that setting the id of an entity to the AssignedGenerator and then not actually assigning anything in code (since I was expecting my new IPreInsertEventListener implementation to do the work) resulted in an exception being thrown by the AssignedGenerator, since its Generate() method essentially does nothing but check to make sure that the id is not null, throwing an exception otherwise. This is worked around easily enough by creating my own IIdentifierGenerator that is like AssignedGenerator without the exception.
The second problem was that returning null from my new IIdentifierGenerator (the one I wrote to overcome the problems with the AssignedGenerator resulted in the innards of NHibernate throwing an exception, complaining that a null id was generated. Okay, fine, I changed my IIdentifierGenerator to return a sentinel string value, say, "NOT-REALLY-THE-REAL-ID", knowing that my IPreInsertEventListener would replace it with the correct value.
The third problem, and the ultimate deal-breaker, was that IPreInsertEventListener runs so late in the process that you need to update both the actual entity object as well as an array of state values that NHibernate uses. Typically this is not a problem and you can just follow Ayende's example. But there are three issues with the id field relating to the IPreInsertEventListeners:
The property is not in the #event.State array but instead in its own Id property.
The Id property does not have a public set accessor.
Updating only the entity but not the Id property results in the "NOT-REALLY-THE-REAL-ID" sentinel value being passed through to the database since the IPreInsertEventListener was unable to insert in the right places.
So my choice at this point was to use reflection to get at that NHibernate property, or to really sit down and say "look, the tool just wasn't meant to be used this way."
So I went back to my original IIdentifierGenreator and made it work for lazy flushes: it got the high value from the database on the first call, and then I re-implemented that ID generation function in C# for subsequent calls, modeling this after the Increment generator:
private string lastGenerated;
public object Generate(ISessionImplementor session, object obj)
{
string identity;
if (this.lastGenerated == null)
{
identity = GetTheValueFromTheDatabase();
}
else
{
identity = GenerateTheNextValueInCode();
}
this.lastGenerated = identity;
return identity;
}
This seems to work fine for a while, but like the increment generator, we might as well call it the TimeBombGenerator. If there are multiple worker processes executing this code in non-serializable transactions, or if there are multiple entities mapped to the same database table (it's an old database, it happened), then we will get multiple instances of this generator with the same lastGenerated seed value, resulting in duplicate identities.
##$##$#.
My solution at this point was to make the generator cache a dictionary of WeakReferences to ISessions and their lastGenerated values. This way, the lastGenerated is effectively local to the lifetime of a particular ISession, not the lifetime of the IIdentifierGenerator, and because I'm holding WeakReferences and culling them out at the beginning of each Generate() call, this won't explode in memory consumption. And since each ISession is going to hit the database table on its first call, we'll get the necessary row locks (assuming we're in a transaction) we need to prevent duplicate identities from happening (and if they do, such as from a phantom row, only the ISession needs to be thrown away, not the entire process).
It is ugly, but more feasible than changing the primary key scheme of a 10-year-old database. FWIW.
[1] If you care to know about the ID generation, you take a substring(len - 2) of all of the values currently in the PK column, cast them to integers and find the max, add one to that number, add all of that number's digits, and append the sum of those digits as a checksum. (If the database has one row containing "1000001", then we would get max 10000, +1 equals 10001, checksum is 02, resulting new PK is "1000102". Don't ask me why.
A potential workaround is to generate and assign the ID in an event listener rather than using an IIdentifierGenerator implementation. The listener should implement IPreInsertEventListener and assign the ID in OnPreInsert.
Why dont you just make private string lastGenerated; static?
If I call Save on a new object, then populate its properties, NHibernate generates and insert statement that contains only the default values for the properties. For example (session is an open ISession):
var homer = new Person();
session.Save(homer);
homer.Name = "Homer J. Simpson";
session.Flush();
I thought that calling Save would make homer persistent and that NH would track any changes and include them in the insert. Instead, it issues an insert with the name property parameter set to null. If I put the Save call after the assignment then it works. This object has a GUID id assigned by NH so it's not doing a premature insert to get an identity.
ETA I'm using session-per-request in an ASP.NET app and the pattern I want to follow is:
MyObject myObject;
if (id == null)
{
myObject = new MyObject();
repository.Add(myObject);
}
else
{
myObject = repository.GetMyObject(id);
}
// populate myObject's properties
// NH magic happens here when the HTTP request ends
I think your assumption in this case is simply incorrect.
Reading the code sample you provided, you could just as well expect NHibernate to insert the object, and then subsequently change the Name and then issue an Update. That, however, would assume that Flush implicitly saves the changed state.
I also wonder why this happens. NH should really wait to insert the object to the database.
Reasons why could do this:
the id, you already said that you are using guids, so this shouldn't be the reason.
there is a query. To ensure that it is performed on actual data, the session is flushed.
there are calculated columns, which need to be read back from the database
there might be other reasons I don't remember.
Is this really the code you are running to reproduce the test?
How does the mapping file look like?
You just mentioned it in the answer to my (perhaps rather naive) comment. You have set session FlushMode to Auto. Change that to Manual and you're more likely to see the behavior you are seeking.
It's still a rather wild guess, simply because so many other properties of your configuration can be at play.