NHibernate save only part of an object - c#

Application components:
NHibernate 3.3
FluentNHibernate 1.3
I'm using Automapping only at the moment (maybe that's the problem).
I'm new to NHibernate so this may be a simple question.
Here's the basic structure (not actual classes).
class Family
{
public virtual Guid ID { get; set; }
public virtual IList<Person> Members { get; set; }
}
class Person
{
public virtual Guid ID { get; set; }
public virtual Family Family { get; set; }
public virtual string Name { get; set; }
}
I have an "Edit" view for the person, that takes the ID as a parameter. The Action then loads the Person object by ID and renders the view.
The issue is when I then change something and post it back, the Person object's Family member is null, and therefore when it's save, the Person no longer has a family :(
I tried adding a "FamilyID" property, and then having that as a hidden property in the view, but there is then a problem with the NHibernate persisting it (I can post the error if that is the way this is supposed to work).
I could load the Person object when it's posted back, then just copy over some of the information but that seems wrong to me...
Any help would be greatly appreciated.

I could load the Person object when it's posted back, then just copy
over some of the information but that seems wrong to me...
If you do not have corresponding input fields for the Family properties inside your view those values will never be sent back to your controller. So you should retrieve them using the FamilyID (which you would have sent with a hidden field) from the database before updating your Person object. That's the correct flow.
UPDATE:
This being said the best way to handle this scenario is to use view models (DUH). So you will design a view model which will contain only the properties that are supposed to be edited by the user in the view. Then you will make your view strongly typed to the view model and your HttpPost controller action will take the view model as argument. Inside this action you will load the entire Person domain that needs to be updated from the database, set the properties that were modified from the view model (trivially easy with a tool such as AutoMapper) and then persist back your domain model to the database. Only the properties that were part of the view will be modified from this Person domain model.

Related

How to retrieve existing context values in Controller from Entity Framework?

The way I am doing this at the moment (which works) feels a bit hacky. When I am editing an entity I don't want the user to change key auditing fields behind, for example when the entity is initially created it automatically populates DateAdded, AddedBy, EditedBy.
To stop people editing these values I don't bind them in the include when editing:
public ActionResult Edit([Bind(Include = "Id,AccountName")] Account account)
This means I have to do this to get the original values back otherwise they get set to null
account.DateAdded = db.Accounts.Where(c => c.Id == account.Id).Select(d => d.DateAdded).FirstOrDefault();
account.AddedBy = db.Accounts.Where(c => c.Id == account.Id).Select(a => a.AddedBy).FirstOrDefault();
account.EditedBy = User.Identity.Name;
Is there a better way to fix this, the way below works but doesn't feel efficient.
The recommend approach is to use a view model containing only the proeprties you need to display/edit say
public class AccountVM
{
public int ID { get; set; }
public string Name { get; set; }
}
which then then use in the view and post back to your controller, get the data model based on the ID property and map the view model properties to you data model and save. Note the [Bind] attribute is not required.
Using view models has numerous advantages including application of view specific attributes and prevention of overposting attacks. For more information refer the answers to What is ViewModel in MVC?
Tools such as automapper make it easier to map ata between you data and view models.

How do I add multiple tables using one model and one view?

I want to have a page where the user selects from a drop down list the category, then adds a small text about that category and uploads an image where the path of that image is saved in the database rather than the whole image. I have created a table "Categories" where the admin is authorized to fill it and the user only selects from the categories list.
Here is what I have done so far:
The create categories model:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity;
using System.Linq;
using System.Web;
namespace DemoIdentity.Models
{
public class CategoriesAdmin
{
public int ID { get; set; }
[Required(AllowEmptyStrings = false)]
[Display(Name = "category name")]
public string categoryName { get; set; }
}
public class DefaultConnection:DbContext
{
public DbSet<CategoriesAdmin> categories { get; set; }
}
}
Now I want to have another table (Data) which includes (ID, Category (category name selected from table categories), News, Image_Path). This table is in the Default Connection database. The category name is the selected category name from a drop down list, and the image path is an upload image which saves the path rather than the whole image.
I am unsure of how to achieve this.
It appears that you are confusing components of ASP.NET MVC and Entity Framework.
As the Entity Framework site states:
Entity Framework (EF) is an object-relational mapper that enables .NET
developers to work with relational data using domain-specific objects.
It eliminates the need for most of the data-access code that
developers usually need to write.
And the MVC site states that:
The ASP.NET MVC is an open source web application framework that
implements the model–view–controller (MVC) pattern.
The two frameworks meet through your model classes. MVC uses the model class to define the data, logic and rules of the application. In Entity Framework, your model class is mapped to tables in your database where it handles the direct reads and writes for you.
By creating your CategoriesAdmin model class and exposing it as a property in your DbContext class as such:
public class DefaultConnection:DbContext
{
public DbSet<CategoriesAdmin> categories { get; set; }
}
Entity Framework will have mapped your model class to a database table called CategoriesAdmins. If this table does not yet exist in your database, it will automatically create it for you. This approach in Entity Framework is known as Code First to a new Database.
Now since you already have a table that stores the available categories (CategoriesAdmin), you will need to create a second model class (called Data for the sake your example) which contains properties for the other bits of information that you want to store.
public class Data
{
// gets or sets the ID of this Data record.
public int ID {get;set;}
public string ImagePath {get;set;}
// other properties
...
}
Now that you have two model classes, you need to create a relationship between the two. In a SQL database this is achieved by Foreign Keys. In Entity Framework, you can achieve the same by using Navigational Properties.
So we update the Data model class as such:
public class Data
{
// gets or sets the ID of this Data record.
public int ID {get;set;}
public string ImagePath {get;set;}
// gets or sets the ID of the related CategoriesAdmin record.
public int CategoriesAdminId {get;set;}
// gets or sets the related CategoriesAdmin record. Entity Framework will
// automatically populate this property with an object for the related
// CategoriesAdmin record.
[ForeignKey("CategoriesAdminId")]
public virtual CategoriesAdmin CategoriesAdmin {get;set;}
// other properties
...
}
The ForeignKeyAttribute on the CategoriesAdmin property is there to give Entity Framework a further hint of the foreign key column to load the navigational property from.
Finally to be able to use your new Data model class with Entity Framework, you need to add another property to your DbContext class so that you have a means of accessing your data:
public class DefaultConnection:DbContext
{
public DbSet<CategoriesAdmin> Categories { get; set; }
public DbSet<Data> Data { get; set; }
}
Now that you have created your model classes and wired them into Entity Framework, you will now be able to use them in MVC. If you load your Data model into your view (using DefaultConnection.Data), you will be able to access the related CategoriesAdmin record by accessing the CategoriesAdmin property on the Data object.
In short: two tables means you need two models. Both models can be loaded into the single view.
Footnote: Apologies if there are large gaps in my answer as there is a lot to explain that have already been explained in other places far better than what I can. The references I have linked should hopefully fill in the gaps.
Should you need more help, please see all of the tutorials on the ASP.NET MVC website on working with data. They're much better written than my concise attempt. I would recommend following them exactly and getting the examples to work before completing your own project so that you have a better understanding of how the two frameworks work and interact with each other.

MVC 4 / Entity Framework 5 / Code First - Persisting a collection (list of objects) in a view - a few questions

My project has several models, some with 1:many relationships with other models. A user has a profile page, this is split up into several partial views - each grouping/representing specific attributes of the given model.
If for example, I want to display 5 of the 10 properties of a given model in a partial view, and want to persist the properties that aren't shown to the user I'm using Html.HiddenFor like so
#Html.HiddenFor(x => x.IsInGroup)
This works for single entry properties as above. Of the 5 hidden member attributes, one of these may be a list. I understand that a list cannot be persisted using HiddenFor. I've come across the Mvc Futures package and the Html.Serialize helper extension, but I'm not totally sure how to use it and havn't been able to find much good information on it.
Once a model is passed to a view everything associated with it (properties, collections) is available for us to access e.g. Model.Username. If for example we don't use HiddenFor with Username, does that mean it will be reset to a default value, or null? Is this the same for collections?
Is using HiddenFor the only/best way to persist a model's properties that are not shown to the user?
Is using Html.Serialize the only way persist a model's collection that is not shown to the user? Other options?
Could someone provide/link to a simple example of how to persist a collection in a situation similar to what I've described?
If I was to use Html.Serialize, does the whole model need to be serialised or can I just serialise the collection?
Feedback appreciated.
You don't need to "persist" any data from your model by creating all the fields (hidden or not) from the model. In controller, when updating the record in the database you will just update what you need, the rest will not be affected.
Let's say this is your Product model
class Product {
public int ID {get; set;}
public string Name {get; set;}
public string Description {get; set;}
public decimal Price {get; set;}
public virtual ICollection<Category> Categories { get; set; }
}
If you only want to edit Name and Description in your view, then you can only put those fields, and skip the rest (the ID would be in a hidden field). When the form is submitted, in your controller you would reference the Product record by the ID that got passed in and then you can update Name and Description to what you received from the form. Categories and the price will not be affected.
Somewhat more straight forward way to do this is to use a ViewModel specific to the view without non-editable properties. I found this post that gives more details how to do this:
Successful Model Editing without a bunch of hidden fields
On a smaller project you don't have use any kind of library (AutoMapper, InjectValues, ...), but you can do it yourself in places where you need to copy the values from a ViewModel into an actual record from DB.

How to build a collection of one-to-many entities without persisting parent entity to database first?

Not sure if I'm completely missing something but, I have a typical MVC web application using EF and role based authentication, as such:
public class User
{
public int UserId { get; set; }
public string Name { get; set; }
public IList<Role> UserRoles { get; set; }
}
public class Role
{
public int RoleId { get; set; }
public string Name { get; set; }
public IList<User> RoleUsers { get; set; }
}
Ideally, in the above case, I would like a single edit screen to update a users name that also allows add/delete operations on the users roles, e.g. a form with an input field for the name, a table of the users roles with each row having a delete button, and a role drop down list with an add button.
How would I perform edits on the views model (User) without persisting the role additions/deletions to the database until a save request is issued?
It may be best to think of an Add operation instead. If inserting a new user, how could you build a collection of that users roles without inserting the user first?
Thanks in advance.
After looking into this a bit more, Phil Haacked wrote an article on binding a view model to a list, found here...
http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx
I could probably have used this approach to have a single form with inputs for the entity properties and a collection of associated inputs bound using the above method for sub objects. Although I imagine this would probably require a duplicate list property for each navigation property on the parent entity, which is a strike for me.
If adding an entity with a collection of complex objects, this method also doesn't resolve how you might elegantly input all the properties for a complex object on the same view (as the parent entity only exists in memory) - the simplest approach probably being partial views containing the edit forms?
For simplicity, I opted to change the edit view to only edit the basic properties of the entity and have a separate link for sub objects that redirects to the relevant controller, using the User example, an add operation would follow as such:
User/Edit/{user_id} view -> Input the users name, email etc.
Click Save -> Redirect to User/Index or User/Details/{user_id}
A 'manage roles' link is available for this user, once clicked -> Redirect to /Role/Index/{user_id}
Page displays a table of roles for this user with the ability to add/edit etc.
Edit:
Also, in case this helps anyone else.
I set the /Role/Index action to list all roles by default, however it also accepts a UserId parameter (/Role/Index/{user_id}). When a UserId is specified, the action returns a different view (/Role/IndexForUser) whose data model is the User entity - it does the same as the default Index view, but only lists roles associated with the user, and displays more contextual links such as 'remove user from role'.

Best practices for displaying Foreign Key object names in c# objects

I have a method called GetCustomer that returns a Customer object.
Customer object is as below.
Public class Customer
{
public int Id { get; set;}
public string Name { get; set;}
public int CompanyId { get; set;}
}
Lets say Customer is related to a Company and I have to display this customer information on the screen UI. Now, when I call the GetCustomer method I only get back the details about the Customer. On the screen, I also need to display the companyname that this customer belongs to.
One easy way to do this would be to have a property called CompanyName in the customer object and fill it from your datalayer method. But I'm not sure if thats best practice. If the customer also belongs to a department, now I need to have DepartmentId and DeptName as properties. Ids are a must since sometimes I need to send the Id to get the full Dept/Company object.
I don't want to have the full objects for Department and Company in the Customer object.
I assume this is a common scenario where a particular object is linked to others using an ID and when you bring back the main object, you just need to display the name from the other linked objects.
What is the best to way to handle this. My main intention is to avoid an (or more) extra database call.
I'm NOT using Linq or Entity Framework, just regular ADO.NET.
This situation depends on your goals as;
1 - if you want to avoid extra DB calls, you have to code your UI via your db communicator with only one instance, open it only once and flush its members (adapter, command ..etc.) every time after making DB calls and close connection at the end of data transfers.
2 - for other purpose of your question, use lazy loading. put only id's on your entity and initialize and use the id's belonging entity if needed!
For example:
public class Customer
{
public int Id { get; set;}
public string Name { get; set;}
public int CompanyId { get; set;}
}
public class Company
{
public int CompanyId;
//company fields
}
// .. on your business layer if you need to use Company data:
// examine your Customer instance as "customer"
Company userCompany = GetCompanyWithId(customer.CompanyId);
but as you no doubt of your guess, data that is going to load is depends of your needs. Think simple. If your only need is Department and Company names, than you can create a view on your DB and can call it on your code. You can create an entity as CustomerWithFullData and you can put a Customer and a Department etc. in this entity and when you need to show full data you can fill this with DB View. Or dont bother to create entities. If you dont need entity, Call DB View directly DataSet and bind Tables. So you can transfer data collection work to DB and this is what we want to do.
As i said before, think simple.
What I would do is to retain an OO structure at the level of your business objects, and modify your DAL to return all the information you need with a single DB round-trip.
For example, you could have a stored procedure that returns two result sets: one for the Customers, and another for the Companies they reference. You could use table valued parameters to pass the SP a list of one or more Customers to look-up.
Depending on the size of your DB and your performance requirements, you also might be able to read the entire Customer and Company tables into memory when the app starts, cache the results, and manage insert/update/deletes against that data.

Categories

Resources