NHibernate - flagging specific properties as 'dirty' - nhibernate

I am working on an NHibernate project and have a question regarding updating transient entities.
Basically the workflow is as follows:
Create a DTO (projection) and send over the wire to client. This has a small subset of properties from the entity.
Client sends back the changed DTO
Map the DTO properties back onto the appropriate enitity so an UPDATE statement can be generated and executed by NH.
Save the entity
Point 4 is where I have the issue. Currently I can achieve this update using the session.Merge() method, however it must first load the entity from the db (assume no 2LC) before updating. So, both a select and an update statement are fired.
What I would like to do is create a transient instance of the entity, map the new values from the DTO, then have NH generate a SQL statement using only the properties I have changed. The additional select should be unnecessary as I already have the entity ID and the values required for the SET clause. Is this possible in NH?
Currently using session.Update(), all properties will be included in the update statement and an exception is raised due to the uninitialized properties that are not part of the DTO.
Essentially I need a way to specify which entity properties are dirty so only these are included in the update.
== EDIT ==
For example...
public class Person
{
public virtual int PersonId { get; set; }
public virtual string Firstname { get; set; }
public virtual string Nickname { get; set; }
public virtual string Surname { get; set; }
public virtual DateTime BirthDate { get; set; }
}
And the test case.
// Create the transient entity
Person p = new Person()
p.id = 1;
using (ISession session = factory.OpenSession())
{
session.Update(p);
// Update the entity – now attached to session
p.Firstname = “Bob”;
session.Flush();
}
I was hoping to generate a SQL statement similar to ‘UPDATE Persons SET Firstname = ‘Bob’ WHERE PersonID = 1’. Instead I get a DateTime out of range exception due to BirthDate not being initialised. It shouldn’t need BirthDate as it is not required for the SQL statement. Maybe this isn’t possible?
== /EDIT ==
Thanks in advance,
John

Dynamic-update is what you're looking for. In your mapping file (hbm.xml):
<class name="Foo" dynamic-update="true">
<!-- remainder of your class map -->
Be aware of the potential problems that this may cause. Let's say you have some domain logic that says either FirstName or Nickname must not be null. (Completely making this up.) Two people update Jon "Jonboy" Jonson at the same time. One removes his FirstName. Because dynamic-update is true, the update statement just nulls out Jon and the record is now "Jonboy" Jonson. The other simultaneous update removes his Nickname. The intent is Jon Jonboy. But only the null-out of the Nickname gets sent to the database. You now have a record with no FirstName or Nickname. If dynamic-update had been false, the second update would have set it to Jon Jonboy. Maybe this isn't an issue in your situation, but setting dynamic-update="true" has consequences and you should think through the implications.
UPDATE: Thanks for the code. That helped. The basic problem is NHibernate not having enough information. When you say session.Update(p), NHibernate has to associated a disconnected entity with the current session. It has a non-default PK. So NHibernate knows that it's an update and not an insert. When you say session.Update(p), NHibernate sees the whole entity as dirty and sends it to the database. (If you use session.Merge(obj), NHibernate selects the entity from the database and merges obj with it.) This is not what you really mean. You want to associate your object with the current session, but mark it as clean. The API is somewhat non-intuitive. You use session.Lock(obj, LockMode.None) as below.
using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
var p = new Person {PersonId = 1};
session.Lock(p, LockMode.None); // <-- This is the secret sauce!
p.Firstname = "Bob";
// No need to call session.Update(p) since p is already associated with the session.
tx.Commit();
}
(N.B. dynamic-update="true" is specified in my mapping.)
This results in the following SQL:
UPDATE Person
SET Firstname = 'Bob' /* #p0_0 */
WHERE PersonId = 1 /* #p1_0 */

Related

How to use LINQs Include in SQL

In LINQ I have written a simple query where I am searching for an animal using the ID property. However, I am also including the Farm the animal belongs using the Include property.
I want to write the same LINQ query in SQL where I can include Farm. How can I include Farm using SQL. I have an incomplete SQL syntax below. Can anyone help me out.
LINQ
await _dbContext.Animals.Where(x => x.id == 1)
.Include(x => x.Farm)
.ToListAsync();
SQL
select * from Animals where id = 1;
Apparently your database has a table with Animals and a table with Farms. There seems to be a one-to-many relation between Animals and Farms: on every Farm live zero or more Animals; every Animal lives on exactly one Farm, namely the Farm that the foreign key refers to.
I think you will have classes similar to the following:
class Farm
{
public int Id {get; set;}
public string Name {get; set;}
... // etc
// Every Farm has zero or more Animals (one-to-many)
public virtual ICollection<Animal> {get; set;}
}
class Animal
{
public int Id {get; set;}
public string Name {get; set;}
... // etc
// Every Animal lives on exactly one Farm, using foreign key
public int FarmId {get; set;}
public virtual Farm Farm {get; set;}
}
I want to write the same LINQ query in SQL where I can include Farm.
A small trick: if you want to know the SQL code generated by Entity Framework, use property DbContext.Database.Log.
using (var dbContext = new DbContext())
{
// Log generated SQL to debug window:
dbContext.Database.Log = System.Diagnostics.Debug.Write;
// execute your LINQ:
var fetchedAnimals = _dbContext.Animals.Where(x => x.id == 1)
.Include(x => x.Farm)
.ToList();
}
Write your own SQL
You'll have to join Animals with Farms, and keep only the Animal with ID = 1:
See SQL Join
// Select only the properties of Animals and Farms that you actually plan to use
SELECT Animals.Id, Animals.Name, ...,
Farms.Id, Farms.Name, ...
FROM Animals INNER JOIN Farms
ON Animals.FarmId = Farm.Id
WHERE Animals.Id = 1
You should not use "" to fetch everything. If Farm [10] Has 5000 Chickens, then every Chicken will have a foreign key with a value 10. If you use "" you will transfer this value 10 more than 5000 times, while you already know the value of the foreign key.
There's room for improvement
When using entity framework to fetch data, always use Select, and select only the properties that you plan to use, even if you Select all properties. Only omit Select and / or use Include if you plan to change / update the fetched data.
The reason is, that fetching data without using Select is not very efficient.
If you fetch data without using Select, entity framework will put the fetched item in the DbContext.ChangeTracker, together with a copy of the fetched item. You get a reference to the copy. Whenever you change properties of the fetched item, you change the copy in the ChangeTracker. When you call DbContext.SaveChanges, the original is compared with the copy, property per property to see which properties are changed, and thus need to be updated in the database.
So if you don't plan to change the fetched data, it would be a waste of processing power to put this data AND a copy in the ChangeTracker. Hence: always use Select, unless you plan to update the fetched data.

Query for entities which are not joined with some other certain entities

I am trying to implement a custom task scheduler system.
I have a following (simplified) entity model:
class User
{
public virtual long UserId { get; set; }
public virtual string Name { get; set; }
}
class Task
{
public virtual long TaskId { get; set; }
public virtual long? UserId { get; set; }
public virtual string TaskName { get; set; }
public virtual Guid SchedulerSessionUid { get; set; }
}
For now, corresponding SQL tables are straight forward with fields mapping exactly as they appear in the classes above.
The scheduler is a C# Windows console app. It will run once per day.
It should work in a following way (simplified):
generate a Guid for current session
try selecting next User entity, which does not already have a task scheduled in the current scheduler session (Guid compare); if no such a User found, go to the step 5.
add a new Task for the user selected in the step 2.
go to step 2.
exit the application
It seems a pretty trivial problem, but I have a problem implementing the second step. I have tried various queries, but there are some rules which always stop me.
Here are some rules which I have to obey while implementing the step 1:
I am not allowed to modify the User class or User SQL table
I may modify Task class and table, if it will help to solve the problem
I may add a new table or a stored procedure, if it will help
I have to implement it in a way which is as compatible with NHibernate and LINQ as possible
there might exist also some tasks which are not associated with any User object (the scheduler will ignore those, but still I have to keep that in mind while designing the SQL/LINQ query and NHibernate mapping).
Here are some real world example how it should work.
Users
-----------------
UserId Name
-----------------
1 First
2 Second
Tasks
--------------------------------------------------------
TaskId UserId SchedulerSessionUid
--------------------------------------------------------
1 NULL 6d8e48d0-4e92-477e-82fa-cd957e7dc201
2 1 d213cfc8-23d6-49fb-b4e3-9ff3b60af6c4
3 1 9ee042df-88a7-447e-adbd-e7551ed50ae5
1.Now when the Scheduler runs, it generates a current session id = 76ea57fa-8c89-4c05-9ca2-a450b1f8a032.
Now it should issue the magical LINQ query to NHibernate LINQ
provider to get a User entity
In the first iteration the query should return the User entity with
UserId=1 because there are no tasks in the current session for that
User yet
Now the Scheduler creates a new task with UserId=1,
SchedulerSessionUid=76ea57fa-8c89-4c05-9ca2-a450b1f8a032.
In the next iteration the Scheduler should get a User with UserId=2. Again, a new task is inserted with UserId=2, SchedulerSessionUid=76ea57fa-8c89-4c05-9ca2-a450b1f8a03.
In the next iteration the Scheduler should get no users, so it exits.
What LINQ query could I use to get the User for the step 2? What changes in my SQL schema and entity model do I need?
If I now follow you correctly, you need to get a (just one) user for which there are no tasks with the given session id. Am I correct?
Users.Where(u => !Tasks.Any(t = > t.UserId == u.UserId && t.SchedulerSessionUid == curSession)).FirstOrDefault()
Edit:
Since you're doing several spins through this, would you perhaps be faster doing:
foreach(var user toDealWith Users.Where(u => !Tasks.Any(t = > t.UserId == u.UserId && t.SchedulerSessionUid == curSession)))
{
//do stuff
}
Rather than keep hitting the database each time?

NHibernate - How do I update more than one boolean field at a time

I am using NHibernate in a web application I'm building. The user can subscript to zero or more mailing-lists (there are a total of 8). This is represented on the screen with a checkbox for each mailing-list.
I would like to use NHibernate to update these in one go. A very simple sql query would be:
update mail_subscriptions set subscribed = true where mailing_list_id in (21,14,15,19) and user_id = 'me'
What is the cleanest way to perform this update via NHibernate so that I can make a single round trip to the database?
Thanks in advance
JP
NHibernate might not be able to update the mail_subscriptions in the way you have shown above but it can do it in a single round trip to the DB using batched queries.
This example considers Subscriptions mapped as a HasMany using Component although roughly the same technique can be used if the mapping was just a plain HasMany. I am also assuming that each user already has rows in the mail_subscriptions table for each mailing list set to false for subscribed.
public class User{
public virtual string Id {get; set;}
public virtual IList<MailSubscription> Subscriptions {get; set;}
}
public class MailSubscription{
public virtual int ListId {get; set;}
public virtual bool Subscribed {get; set;}
}
public void UpdateSubscriptions(string userid, int[] mailingListIds){
var user = session.Get<User>(userid);
foreach(var sub in
user.Subscriptions.Where(x=> mailingListIds.Contains(x.ListId))){
sub.Subscribed=true;
}
session.Update(user);
}
Now when the unit of work completes you should see SQL like this produced sent as a single round trip to the DB.
update mail_subscriptions set subscribed=true where user_id='me' and listid=21
update mail_subscriptions set subscribed=true where user_id='me' and listid=14
update mail_subscriptions set subscribed=true where user_id='me' and listid=15
update mail_subscriptions set subscribed=true where user_id='me' and listid=19
I think the NHibernate feature you seek is known as Executable DML.
Ayende has a blog post giving an example at http://ayende.com/blog/4037/nhibernate-executable-dml .
Depending on your names of your entities and their properties, and assuming you have an ISession instance variable called session, you would need to execute an HQL query something like:
session.CreateQuery("update MailSubscriptions set Subscribed = true where MailingList.Id in (21,14,15,19) and User.Id = 'me'")
.ExecuteUpdate();
Now, having said that, I think in the use case you describe (updating a handful of entries within a collection on a single aggregate root), there is no need to use Executable DML. Mark Perry has the right idea - you should simply modify the booleans on the appropriate entities and flush the session in the usual way. If ADO.NET batching is configured appropriately, then the child entries will cause multiple update statements to be sent to the RDBMS in a single database call.

EF : MVC : ASP: TPH Error - Store update, insert, or delete ... number of rows (0). Entities ... modified or deleted ... Refresh ObjectStateManager

Error Message: "Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. Refresh ObjectStateManager entries."
Hello All,
I've created a Code First TPH(Table Per Hierarchy) within MVC and EF with a SQL compact db.
Here's the Class diagram/Hierarchy:
Class Diagram
Client and SalesRep both inherit the BaseUser class. The Key is "UserID" and it's coded with the Data Annotation [Key](I'm aware that 'ID' should set it as well)
Here's where I'm at: I can seed the database with a few entries. When I try to set the "UserID" in the seeding method it seems to ignore it and just apply the UserID in numerical order...(Seems ok to me?)
furthermore here's my DbContext
public class SiteDB:DbContext
{
public DbSet<BaseUser> AllUsers { get; set; }//enable TPH
public DbSet<SalesRep> SalesReps { get; set; }
public DbSet<Client> Clients { get; set; }
}
Next,
I have created a controller for Clients -> ClientsController with strongly typed Razor Views. With this. I now have a CRUD for the Clients. I can create new Clients without any issue, but when I try to edit a Client entry, I get the error message stated above.
I did notice something interesting I stepped through the code and the error is happening on the db.SaveChanges();
When the client is passed back into the ActionResult Edit method the UserID=0! Bizarre? I'm not sure if this is a bug or if it's an actual issue that's causing this.
UserID=0
Your help with this is appreciated. Thanks!
To modify an entity you need to get it from the database: when you pass back the modified values in the Edit action you need to retrive the entity from the db object, getting it by ID, apply the modified values and save it.
Here an example:
public ActionResult Edit(int id, MyModel model){
using (SiteDBdb = new SiteDB()){
Client cl = (from c in db.Clients where c.id == id select c).First();
cl.MyProp = model.MyProp;
...
db.SaveChanges();
}
...
}

How do I Insert or Update (or overwrite) a record using NHibernate?

I need to write a row to the database regardless of whether it already exists or not. Before using NHibernate this was done with a stored procedure. The procedure would attempt an update and if no rows were modified it would fallback to an insert. This worked well because the application doesn't care if the record exists.
With NHibernate, the solutions I have found require loading the entity and modifying it, or deleting the entity so the new one can be inserted. The application does have to care if the record already exists. Is there a way around that?
Does the Id Matter?
Assigned Id
The object has a keyword as an assigned id and is the primary key in the table.
I understand that SaveOrUpdate() will call the Save() or Update() method as appropriate based on the Id. Using an assigned id, this won't work because the id isn't an unsaved-value. However a Version or Timestamp field could be used as an indicator instead. In reality, this isn't relevant because this only reflects on whether the object in memory has been associated with a record in the database; it does not indicate if the record exists or not in the database.
Generated Id
If the assigned id were truly the cause of the problem, I could use a generated id instead of the keyword as the primary key. This would avoid the NHibernate Insert/Update issue as it would effectively always insert. However, I still need to prevent duplicate keywords. With a unique index on the keyword column it will still throw an exception for a duplicate keyword even if the primary key is different.
Another Approach?
Perhaps the problem isn't really with NHibernate, but the way this is modeled. Unlike other areas of the application, this is more data-centric rather object-centric. It is nice that NHibernate makes it easy to read/write and eliminates the stored procedures. But the desire to simply write without regard to existing values doesn't fit well with the model of an object's identity model. Is there a better way to approach this?
I`m using
public IList<T> GetByExample<T>(T exampleInstance)
{
return _session.CreateCriteria(typeof(T))
.Add(Example.Create(exampleInstance))
.List<T>();
}
public void InsertOrUpdate<T>(T target)
{
ITransaction transaction = _session.BeginTransaction();
try
{
var res=GetByExample<T>(target);
if( res!=null && res.Count>0 )
_session.SaveOrUpdate(target);
else
_session.Save(target);
transaction.Commit();
}
catch (Exception)
{
transaction.Rollback();
throw;
}
finally
{
transaction.Dispose();
}
}
but FindByExample method returns all objects alike not objects with the exact ID what do you suggest ? since I have only object as parameter I don't have access to its specific ID field so I cannot use session.get(Object.class(), id);
Typically, NHibernate can rely on the unsaved-value to determine whether it should insert or create the entity. However, since you are assigning the ID, to NHibernate it looks like your entity has already been persisted. Therefore, you need to rely on versioning your object to let NHibernate know that it is a new object. See the following link for how to version your entity:
http://web.archive.org/web/20090831032934/http://devlicio.us/blogs/mike_nichols/archive/2008/07/29/when-flushing-goes-bad-assigned-ids-in-nhibernate.aspx
Use the session.SaveOrUpdate(object) method.
You can do
Obj j = session.get(Object.class(), id);
if (j != null)
session.merge(myObj);
else
session.saveOrUpdate(myObj);
Query objects where keyword = x, take FirstOrDefault. If it's null, Add new object, if it exists, update object that you got and call saveOrUpdate on it.
This worked for me:
Implementation
public void InsertOrUpdate<TEntity, TId>(TEntity entity) where TEntity : IIdentificableNh<TId>
{
var anyy = session.Get<TEntity>(entity.Id);
if (anyy != null)
{
session.Evict(anyy); //dispatch all data loaded, to allow updating 'entity' object.
session.Update(entity);
}
else
{
session.Save(entity);
}
session.Flush();
}
Entity
public class Caracteristica : IIdentificableNh<int>
{
public virtual int Id { get; set; }
public virtual string Descripcion { get; set; }
}
I had to create an interface (IIdentificableNh) that allows me to access the Id property value.
Usage example:
session.InsertOrUpdate<Caracteristica, int>(new Caracteristica { Id = 2, Descripcion = "Caracteristica2" });
call hibernate.saveOrUpdate() which will check if the object is in the database, update it if it is, and save (i.e. insert) it if it is not.