remove from collection without load all collection data. confused which collection mapping to use - nhibernate

I have a many-to-many relationship between Assignment and User
When trying to delete an user from an assignment, I see all users are loaded in the collection.
How to I avoid that?
public class User
{
public virtual int Id { get; private set; }
public virtual IList<Assignment> Assignments { get; set; }
}
public class Assignment
{
public virtual int Id { get; private set; }
public virtual ICollection<User> Users { get; set; }
}
Mappings:
HasManyToMany(user => user.Assignments).Table("UserToAssignment").ParentKeyColumn("UserId").ChildKeyColumn("AssignmentId").Inverse().ExtraLazyLoad();
HasManyToMany(productAssignment => productAssignment.Users).AsSet().Table("UserToAssignment").ParentKeyColumn("AssignmentId").ChildKeyColumn("UserId").LazyLoad();
Calling code:
assignment.Users.Remove(user)
Initially I used Bag instead of Set for Assignment mapping, but when updating it, it was deleting and then reinserting alot of rows in the AssignmentsToUsers table. So I changed to using Set.
But now I see a problem with using Set: it brings all data in memory.
What is the recommended way of doing this?

You can't avoid this and I would ignore it if performance is acceptable. If performance is a problem, there are three ways I can think of to tackle it:
If the other side of the collection (User.Assignments) is lighter weight then remove the assignment from the user instead.
Model the many-to-many table and delete the object directly. You would have to be certain that the Users collection is not going to be loaded prior to this because the in-memory representation will still contain the deleted record.
Direct delete using SQL -- this has the same caveat as #2.

You should use extra lazy mode also for Assignment.Users.

Related

Nhibernate envers modified columns doesn't correctly track removals from collection

I've got a model that looks like:
class Case
{
public virtual ISet<CaseToCaseTag> CaseTags { get; set; }
public virtual DateTime LastModified { get; set; }
}
class CaseToCaseTag
{
public virtual Case Left { get; set; }
}
When auditing this relationship with envers, I'm running into problems with the modified property tracking when removing values from the set.
If the only thing I change in the case-entity when removing a value from the set is the "set-removal" then envers correctly tracks that the CaseTags property is modified in the revision. However, if I also change any other property, for instance the LastModified property, I don't get the CaseTags_MOD flag correctly set.
I've tracked this down (I think) to the AuditProcess.AddWorkUnit method.
When I only remove from the set only one WorkUnit gets added (a CollectionChangeWorkUnit), but when I also change another property, another WorkUnit (a ModWorkUnit) also gets added.
What seems to happen then is that the CollectionChangeWorkUnit is discarded in favor of the ModWorkUnit, which in turn doesn't pick up the changes to the collection.
Is what I'm trying to do not supported, or is there a way around it?
Sounds like a bug. Please create a pull request with minimal mapping to reproduce the issue.

NHibernate skips certain properties to update, possible?

I defined my entities with bunch of columns and created mapping.
public class PurchaseRecord {
public virtual int? Id {
get;
set;
}
public virtual DateTime? PurchasedDate {
get;
set;
}
public virtual string Comment {
get;
set;
}
public virtual IList<PurchaseRecordExtendedProperty> ExtendedPropertyValues {
get;
set;
}
public class PurchaseRecordMap : ClassMap<PurchaseRecord> {
public PurchaseRecordMap() {
Table("PurchaseRecords");
Id(x => x.Id, "RecordID").GeneratedBy.Identity();
Map(x => x.PurchasedDate, "PurchaseDate").Not.Nullable();
Map(x => x.Comment, "Comment");
HasMany(x => x.ExtendedPropertyValues).KeyColumn("ExtendedPropertyID").Cascade.All();
}
It works well in most of the cases, howerver in some certain situation I want to skip updating certain column (such as child collection ExtendedPropertyValues). When I create the PurchaseRecord object I don't even bother to load the data of ExtendedPropertyValues. But if the property is null NHibernate tries to delete the child records from database.
I know there are some scenario that the ExtendedPropertyValues will never be changed. For performance consideration I don't want to load the data I don't need, is there a way I can force NH to skip designated properties if I don't need to update?
Thanks for any suggestion.
If you enable lazy loading, NHibernate will not try to load any child collections, they will be initialized to a proxy which will only load them if you access them. If you set the child collection to null, that is effectively telling NHibernate to delete all entries in that relationship (unless you mark the relationship as inverse).
NHibernate will not try to update the child collections unless they change (which setting it to null would do).
In summary, enable lazy-loading, and mark ExtendedPropertyValues as inverse, and it should not update it unless you change ExtendedPropertyValues, it also will not load ExtendedPropertyValues unless you access it.

How to create a NHibernate proxy object with some initiliazed fields (other than Id)?

I want to create an object proxy, similar to what ISession.Load is returning, but with some fields initialized. For other properties, when accessed, the proxy will fetch the entire object from database.
Consider the following example:
public class User
{
protected User() {
}
public User(int id, string username, string email) {
// ...
}
// initialize the following fields from other datasources
public virtual int Id { get; set; }
public virtual string UserName { get; set; }
public virtual string Email { get; set; }
// the rest of fields when accessed will trigger a select by id in the database
public virtual string Field1 { get; set; }
public virtual string Field2 { get; set; }
public virtual DateTime Field3 { get; set; }
public virtual ISet<Comment> Comments { get; set; }
}
The Id, UserName, Email are well-known in my case, so I could create an object proxy containing these fields, and for the others leave the default proxy behavior. In addition to throwing an exception if this id is not found in the database, i could throw an exception if preinitialized fields do not match or overwrite them silently. I am using NHibernate.ByteCode.Castle for proxy factories.
Edit:
The purpose for this is to be able to have some projection properties from an entity which can be queried elsewhere (say. a lucene index) and to avoid database calls. Then instead of wrapping these fields in a custom component class containing only these subset of properties, I want to use the proxy object directly so that I am able to load the rest of fields if needed. In the best case scenario I wouldn't hit the database at all, but in some corner cases I'd like to access other fields, too. The SELECT N+1 problem's impact can be greatly reduced by using batching.
An hypothetical version of code I want to use would be:
// create User object proxy with some fields initialized
var user = Session.Load<User>(5, new { UserName = "admin", Email = "admin#company.com" });
Console.WriteLine(user.Id); // doesn't hit the database
Console.WriteLine(user.UserName); // doesn't hit the database
Console.WriteLine(user.FullName); // doesn't hit the database
if (somecondition) {
Console.WriteLine(user.Field1); // fetches all other fields
}
You can specify an eager fetch inside the query to actually retrieve the needed associations. This could be done in different ways depending on what query style ( Criteria,Hql or LINQto NH ) you are using. But the key is changing the fetch mode.
for non-collection properties, I wouldn't do that;
the cost of prefetching them from the DB when you load your entity is (usually) so small that I wouldn't even bother.
for collection properties, just mark the collection fetch strategy as 'lazy=true'.
The only case where I would think about doing something like that is when I have a very large number of properties which I don't need (in your example- say Field1..Field20).
In that case I would either:
1. Define those properties together as a component, or
2. create a DTO for fetching only a subset of your entity's properties.
specifying lazy = "true" (or Not.LazyLoad() for Fluent NHib) on properties Field1, Field2, Field3, Comments mappings may help, though not sure about Select N+1 issue.
another way to go is specifying lazy = "false" for UserName, Email

Is there something analogous on NHibernate regarding Entity Framework's navigation property?

Is there something analogous on NHibernate regarding Entity Framework's navigation property? For example, instead of:
s.Save(new Product { Category = s.Get<Category>("FD"), Name = "Pizza" });
I wish I could write:
s.Save(new Product { CategoryId = "FD", Name = "Pizza" });
Can I inform NHibernate not to use the Product's Category property as a mechanism to save the Product's category? I want to use CategoryId instead(Read: I don't want to use DTO). Entity Framework seems able to facilitate avoiding DTO patterns altogether, while at the same time offering the full benefit of ORM(can avoid joins using navigation properties). I want the EF's offering the best of both worlds(lean mechanism for saving objects, i.e. no need to retrieve the property's object) and navigation mechanism for querying stuff
Sample from EF: http://blogs.msdn.com/b/adonet/archive/2011/03/15/ef-4-1-code-first-walkthrough.aspx
public class Category
{
public virtual string CategoryId { get; set; }
public virtual string Name { get; set; }
public virtual IList<Product> Products { get; set; }
}
public class Product
{
public virtual int ProductId { get; set; }
public virtual string Name { get; set; }
public virtual string CategoryId { get; set; }
public virtual Category Category { get; set; }
}
[UPDATE]
Regarding James answer, I tried seeing the NHibernate's actions in SQL Server Profiler.
// this act didn't hit the Category table from the database
var c = s.Load<Category>("FD");
// neither this hit the Category table from the database
var px = new Product { Category = c, Name = "Pizza" };
// this too, neither hit the Category table from the database
s.Save(px);
Only when you actually access the Category object that NHibernate will hit the database
Console.WriteLine("{0} {1}", c.CategoryId, c.Name);
If I understand your question, you want to save a Product with a Category without hitting the database to load the Category object. NHibernate absolutely supports this and you almost have the right code. Here is how you do it in NHibernate:
s.Save(new Product { Category = s.Load<Category>("FD"), Name = "Pizza" });
This will not hit the database to fetch the actual Category, but it will simply save a Product with the correct Category.Id. Note that you don't need (and I would recommend getting rid of Product.CategoryId).
Now why does this work with session.Load(), but not session.Get()... With session.Get(), NHibernate has to return the object or null. In .NET, there is no way for an object to replace itself with null after the fact. So NHibernate is forced to go to the database (or L1 cache) to verify that the "FD" Category actually exists. If it exists, it returns an object. If not, it must return null.
Let's look at session.Load(). If the object is not present in the database, it throws an exception. So NHibernate can return a proxy object from session.Load() and delay actually hitting the database. When you actually access the object, NHibernate will check the database and can throw an exception at that point if the object doesn't exist. In this case, we're saving a Product to the database. All NHibernate needs is the Category's PK, which it has in the proxy. So it doesn't have to query the database for the Category object. NHibernate never actually needs to hydrate an actual Category object to satisfy the save request.

Do I have to implement Add/Delete methods in my NHibernate entities?

This is a sample from the Fluent NHibernate website:
Compared to the Entitiy Framework I have ADD methods in my POCO in this code sample using NHibernate. With the EF I did context.Add or context.AddObject etc... the context had the methods to put one entity into the others entity collection!
Do I really have to implement Add/Delete/Update methods (I do not mean the real database CRUD operations!) in a NHibernate entity ?
public class Store
{
public virtual int Id { get; private set; }
public virtual string Name { get; set; }
public virtual IList<Product> Products { get; set; }
public virtual IList<Employee> Staff { get; set; }
public Store()
{
Products = new List<Product>();
Staff = new List<Employee>();
}
public virtual void AddProduct(Product product)
{
product.StoresStockedIn.Add(this);
Products.Add(product);
}
public virtual void AddEmployee(Employee employee)
{
employee.Store = this;
Staff.Add(employee);
}
}
You don't have to do this for nhibernate, you have to do this for keep in-memory consistence and not repeat yourself.
Consistence in memory
If you have a two way relationship, lets say Order has Lines, and Line as a relationship to order. You don't want to have a reference from one side and not from the other.
If you just do:
order.Lines.Add(line);
You have made a reference from Order to Line, but Line.Order property remains null. So your in-memory instances are not consistent.
Don't Repeat Yourself
You can use the following code :
order.Lines.Add(line);
line.Order = order;
but you will be repeating yourself, so it is better to put this code in only one place, and the best place is as order.AddLine(..).
You don't have to. You could just call SomeStore.Products.Add(someProduct) directly from outside of your entity. But it's often good practice to make the collections 'read-only' from a public perspective, and using an add method in the entity for adding items.
One benefit of this is that you can put additional logic in there. For instance in your store example, you could set a 'storesStockedIn' collection (if there was such a thing) in the same method, and so keep all the logic about to creating that relationship in one place.
This isn't really a NHibernate thing, but rather an OOP thing. (Although I'm not familiar with EF - maybe it automates some of this for you). The design decisions are exactly the same as if it was just an unpersisted poco (without NHibernate).