Insert new object by attaching existing entity properties in Nhibernate - asp.net-mvc

We have create screens with duplicate option for each object. For ex: While creating new Customer along with details, user can chose existing customer to copy groups associated from existing to new user. So I would like to know how to assign properties of new customer for 1:n and m:n scenario.
For "Customer" and "CustomerGroups". Will the below approach work fine?
Customer existing = repo<Customer>(id);
Customer newCust = new Customer();
for(var group in existing.Groups)
newCust.CustomerGroups.Add(new CustomerGroup(){ **AllpropertiesexceptID**, **Customer=newCust** } );
For Order and OrderItems, since its m:n relationship, just attaching existing items with new order.
Orders existing = repo(id);
Order newOrder = new Order();
for(var item in existing.Items)
newOrder.Items.Add(item);
Is it required to do Session.Evict for the existing Order or Customer for performing these operations.

You might be thinking too much. :) Just go ahead and create separate lookalike objects, pretending NHibernate isn't even there. As long as the new instances have different (unset) identity properties, NHibernate won't even realize they are "copies".

Related

Does NHibernate have in-memory entities for updates

I am new to NHibernate. We are using Dapper for retrieval and planning to use NHibernate for CRUD.
I am trying to remove the child object through parent list object in one to many relationship.
This works when I retrieve the object using NHibernate session and remove the item.
var mercedes = Read("chevrolet"); //queries from nhibernate session
var model = mercedes.Models.Where(c => c.Id == 181).Single();
mercedes.Models.Remove(model);
When I manually create the object and attach thecars models, it is unable to remove it.
var mercedes = new Make() { Id = 77, Name = "chevrolet" };//manually created the object
mercedes.Models = GetAllModels(77);//I have it in-memory
var model = mercedes.Models.Where(c => c.Id == 173).Single();
mercedes.Models.Remove(model);
I think I am doing something weird. But I am able to add/update the models using second approach, so why can't i remove it. Any insights please.
When you create a new domain object it isn't attached to the NHibernate session (unless you are creating a new object and call Save, for instance). As such, deleting from the models collection doesn't actually do anything.
Your second example doesn't seem like a particularly good practice but you can recreate an object and attach it to your NHibernate session using Session.Lock (Merge or Update are fairly normal depending on your preferred behavior).

Updating issue with NHibernate

I have Customer, CustomerGroups, Groups tables. Customer has 1:n with CustomerGroups and Groups has 1:n with CustomerGroups. In the create customer screen, user can choose the groups information of existing customer. So when I create a new customer, I am retrieving groups from existing customer and adding them
///
Customer cust = new Customer();
foreach(var item in getothercustomergroups())
cust.groups.add(item);
commit
This code is generating update statement and updating the customergroups table with newly added customerid, instead of inserting new records with new customerid. Due to this, all groups with prev. customer are gone. Could anyone please explain this behavior.
From your description, I would guess getcustomergroups has gotten customergroups from an existing customer via NHibernate. If so, NHibernate has associated each item in the foreach loop with the existing customer, so when you add item to a new customer, the new customer assumes ownership of that item.
You can avoid this by cloning the item. See How do I copy an object with NHibernate

Attaching an existing but modified entity to the context

In my model I have two classes Categories and Products. There is a relation many- to many between them.
I set states of all categories on modified manually and when I watched in the debugger before saveChanges() I saw that all of these categories were marked as modified. But after request mapping between categories and product weren't updated in my database. Code of update function.
public void UpdateProduct(Product product)
{
using (EFDbContext context = new EFDbContext())
{
context.Products.Attach(product);
if (product.Categories != null)
{
foreach (var item in product.Categories)
{
context.Entry(item).State = EntityState.Modified;
}
}
context.Entry(product).State = EntityState.Modified;
context.SaveChanges();
}
}
Setting entity to modified says that you have changed its properties (not navigation properties) and you want to save them. If you changed relations (navigation properties) by for example creating new relation between existing product and category or removing relation between existing product and category setting state to modified will not help you. This is actually very hard to solve (it is same in all current EF versions) because that relation has its own state which must be set and state of relation cannot be Modified = you must know if you added or removed relation. Especially removing is hard because you probably don't have information about relations you have removed from Categories navigation property when you are going to attach entity to the context. Moreover DbContext doesn't offer access to state of the relation so you must convert it to ObjectContext and use ObjectStateManager.
The easiest way to solve this issue is to load product with categories from database prior to saving and manually synchronize your detached object graph (the one you are trying to save at the moment) with loaded attached graph. Once you synchronize all changes in attached graph you will save it back to database. Attached graph will know which relations to categories were added or removed.

Entity framework many to many relation bottleneck in inserting data

i have a view that shows the user a form and the user should upload a file and choose all the categories associated with it.
the controller that is responsible in submitting the data should
retrieve the file info and
insert data in the file category
retrieve the related category ids and
insert them as well in the table
that is abstracted by the EF just
insert the file and the category ids.
this is my problem the controller just gets some info about the category not all of it. basically it only needs the ids for the insertion
i can't use
[HttpPost]
public ActionResult SaveFile(File file, List<Category> Checkbox, HttpPostedFileBase FileUpload)
{
//some stuff
//for example got the first category and named it to category1
file.Categories.Add(category1)
}
i asked someone and he told me you have to select the category you want to insert
is this really necessary ? i only need a category id and a file id to make the insert why would i fire another request to the database that i don't really need
i am using
EF 4
MVC 3
It is better to select category first because it will save you a lot of possible problems but it is not necessary. You can use dummy category object:
var category = new Category { Id = receivedId };
file.Categories.Add(category);
You will only create new category and you will set its PK. Now you need to handle file insertion where you must explicitly instruct ObjectContext to insert only file (because your categories exists in database):
context.Files.Attach(file); // now whole object graph is attached but marked as Unchanged
context.ObjectStateManager.ChangeObjectState(file, EntityState.Added); // mark only file entity as inserted
context.SaveChanges();
You can also take opposite direction:
context.Files.AddObject(file); // all objects in object graph are marked for insertion
foreach (var category in file.Categories)
{
// you don't want to insert categories again
context.ObjectStateManager.ChangeObjectState(category, EntityState.Unchanged);
}
context.SaveChanges();
This scenario works if you know that all categories exist in your database. If you want to insert new categories together with saving file you will need to query categories first or add some information about which category is new and which is existing.

How to handle relationships to users from other tables in ASP.Net MVC?

At the table level I'm setting aspnet_User.UserID as foreign key of UserID in the other tables (like Posts.UserID, so each post has an owner). Is that the way to go?
When I create the LINQ to SQL model, should I include the aspnet_User table?
When I create a post (record in the Posts table, a Post object), how do I set the relationship to the logged in user (User object in the controller)?
I don't include the aspnet_User table to my linq-to-sql as I don't really need it. I use the built in way of accessing membership data.
But for your table Posts, it might be easier for you to include it so that you can easily display the User's Name by doing myPost.User.Name
edit:
MembershipUser user = Membership.GetUser();
Guid userGuid = (Guid)user.ProviderUserKey;
Post post = new Post
{
UserId =userGuid,
Message = message
};
In your database schema, you should definately have a the UserID in the Post table be a foreign key to the aspnet_user table, on the UserID field. This way, you are making sure your data is clean. I would even add a cascade delete & update on that relationship.
Then, refer to my instructions in a previous question you asked, about how to get the user data.
(Thomas Stock summed it up briefly, above, though :) )

Resources