View Models with database - c#

I am having trouble understanding and implementing a view model. For example, say I have a Blog object, where each Blog object represents one blog post. I have a view that contains a list of each blog (title, text, date posted, etc...). Currently I am passing a list of blog objects to the view, but I would rather pass a list of BlogViewModel objects to the view. How do I do it? Does anyone have any good resources that will help me understand View Models?
Edit
The BlogViewModel I want to pass will contain abbreviated fields for the title and the text of the Blog. For example, I only want to show the first 10 characters of the title and the first 25 characters of the text.

Assuming you are currently doing something like:
public ActionResult GetBlogs()
{
var someService = new FooService();
var blogs = someService.GetMeMyBlogs();
return View("bloglist", blogs);
}
To use view models you need to either return them from your service, or convert the objects in the controller before sending them on to the view.
One option is to create an extension method for the Blog object.
Say we have a few properties something like:
public class BlogVM
{
public string Title {get;set;}
public string Body {get;set;}
public string AuthorName {get;set;}
public int Id {get;set;}
}
We could write an extension method:
public static BlogVM ToBlogVM(this Blog source)
{
return new BlogVM
{
Title = source.Title.SubString(0, 10),
Body = source.Body.SubString(0, 25),
AuthorName = source.Author.Name,//assuming you have some kind of Author table, I'm sure you get the idea..
Id = source.Id
};
}
Now in your controller you can do something like
public ActionResult GetBlogs()
{
var someService = new FooService();
var blogs = someService.GetMeMyBlogs();
return View("bloglist", blogs.Select(x => x.ToBlogVM()));
}
Which passes a list of BlogVM objects to your view.
edit: probably worth adding a few words on why ViewModels.
why send the view everything if it doesn't need it? In your example, your body might be a big block of text. If you are only going to display 25 chars, only send it 25 chars
some of the info in the object might be sensitive. You might want to send the Author's name, but certainly not other information you might hold such as his name, email or even password or address.
similarly, in a POST scenario you can control what information can potentially be sent back to you. If you allow the user to POST back to a full object, they can potentially send you back updated fields you might not expect. If you use a VM, you can control what information you will accept.
I find it easier/quicker for building views

Related

Do formatting helper methods belong in the model, the view model, or a separate class?

I have a model that stores company information, including tax IDs. In the US, these are 9 digit numbers and are typically displayed as ##-#######. However, in my system, I am storing these as strings with no hyphen - since other countries can have identification numbers that differ in length and format, I don't want be limited to a US standard.
Now I want to program my views to display US tax IDs in their "friendly" format. I have this working right now with a helper method I put in the Company model class:
public string FormatTaxID(string TaxID)
{
if (Address.Country == "United States")
return Regex.Replace(TaxID, #"(\d{2})(\d{7})", "$1-$2");
else
return TaxID;
}
Then in my view, I'm using:
#item.FormatTaxID(item.TaxID)
This all works fine, but it doesn't feel right to store a method like this in the model - it feels like this is more of a view/view model responsibility than a model responsibility, as it is solely for presentation.
I am using view models and thought of putting it there, but I I have multiple view models for the underlying model and don't want to repeat code if I don't have to. Also, my view model for the index uses collections and I'm not sure how I would work the method into it:
public class CompanyIndexViewModel
{
public IEnumerable<Company> Companies { get; set; }
public IEnumerable<Document> Documents { get; set; }
}
How would I apply this method to a collection like that?
Another option is creating a new helper/utility class and sticking it in there. What would MVC convention dictate?
For one-offs, I'd say use the view model. If it's something that you will reuse over and over, move it into a utility class that your views/view models/etc. can reference.
And, there's technically nothing wrong sort of doing it both ways. Put the method in a utility class and then add a property to your view model that returns this, e.g.:
public class CompanyIndexViewModel
{
...
public string TaxID { get; set; }
public string USFormattedTaxID
{
get { return Utilities.FormatTaxID(TaxID); }
}
}
The localized-to-the-Company-context TaxID of the company is properly a property of the Company, and is not a presentation detail.

How to create a single drop down for multiple data types?

I am using ASP.Net MVC 3 and I need to create a single drop down list which contains items that relate to multiple database tables.
Normally, if I need to do a drop down list for a single data type I can easily use the ID as the "value" for each drop down option and would do something like this:
#Html.DropDownListFor(x => x.SelectedID, Model.GetMyList())
But now I want to mix up multiple data types. So lets say for this example I want to create a single list to represent something like "Owner" and this can be either a "User" or a "Customer". In this example, both User and Customer are separate database tables and therefore the ID value alone is not enough to identify them correctly.
So what are the best ways to achieve such functionality?
Straight off the top of my head, my first thoughts are to create a "custom" value string which could then be parsed server side to work out the ID and data type, something like...
"USER|1"
"CUSTOMER|1"
I know I can make this work, but am I making this more complicated than it needs to be? Is there a built-in or advised way of doing this?
In your Model can you not do something like this:-
public class Model
{
public string Owner { get; set; }
public List<MyList> ListCollection { get; set; }
public class MyList
{
public int Id { get; set; }
public string Value { get; set; }
}
}
So then when you are checking which list item is selected you also have access to the "Owner" field which will tell you what table it belongs to ?
As nobody has come up with anything better, I can confirm that my original idea (as unwanted as it was) did the job.
When setting the value of the select options, a custom string should be created that can easily be parsed server side, this was achieved using a pipe separating the TYPE of entity, and the ID, for example:
"USER|1"
"USER|2"
"CUSTOMER|1"
"CUSTOMER|2"
Once the selected value is passed to the server, it can then be parsed something like the following:
string option = "USER|1";
string[] values = option.Split('|');
string entityType = values[0];
int entityId = Int.Parse(values[1]);
which can then be used something like this:
if(entityType == "USER")
UpdateUser(entityId);
else//CUSTOMER
UpdateCustomer(entityId);

How do I pass an entire model from my Index View to a completely different controller?

Here's the relevant part of my Index view (Index.cshtml):
#foreach (var item in Model) {
<li>
#Html.ActionLink(item.name, "Index", "Filler", new { cap = item }, null)
</li>
}
As you can see, the ActionLink is tied to the Index action on the Filler Controller, and is passing in the entire item (the model)- "item" is of type "capsule".
Now, on my Filler Controller, in the Index action:
public ActionResult Index(capsule cap)
{
var fillers = db.fillers.ToList();
return View(fillers);
}
The capsule class that was automatically generated by Entity Framework is:
namespace CapWorx.Models
{
using System;
using System.Collections.Generic;
public partial class capsule
{
public capsule()
{
this.fillers = new HashSet<filler>();
}
public int pk { get; set; }
public string name { get; set; }
public virtual ICollection<filler> fillers { get; set; }
}
}
The problem is "cap" is NULL in the above Index action. But, if I change the type to "object" instead of "capsule", I do get some weird non-null data, but I can't cast the object to "capsule". Does anyone know why this is NULL?
Thanks,
Mike
You usually just have to pass in the id to the action. For example, can you refactor your code so that it can take in a capsuleId, get the capsule from db and do whatever processing is needed. Adding the entire object to route values in ActionLink doesn't make any sense. Have a look at the link being generated. It is probably just something like ...?cap=Namespace.Capsule as the object would have be ToStringed
The first problem is in MVC you can't bind to an interface (ICollection). You'll need to change it to a List - List<filler>. The second problem you will face is that Lists/Arrays need to be represented in array notation for proper posting, something like name="books[0].book_id". Even though MVC does a lot of magic, the model in your link still has to be represented as a query string eventually.
Depending on what you are trying to do, you may be better off representing your model as a JSON object and posting with .ajax().
See this SO post for other ideas - Need help with binding Set with Spring MVC form
I'm not totally sure why this would work(I think you're nulling out the html attributes), but try to remove the "null" part of the actionlink.
Or, the controller which created the models is wrong.
Again, don't kill me for this.
#Html.ActionLink just generates an anchor element (...), so it makes no sense to attempt to bind a complete object to the routeValues parameter. As #manojlds says, it make much more sense to just pass the relevent key value, since you'll be performing the lookup then anyway (remember, the web is "stateless").

MVC Object Change Tracking

I've currently got an issue where I need to see which fields have been changed on an Edit field for auditing purposes, in which I have code for, but I think my problem lies within my MVC View.
I have (test code):
[HttpPost]
public ActionResult Adjustment(GroupPolicy groupPolicy)
{
if (ModelState.IsValid)
{
_service.SaveGroupPolicy(groupPolicy);
return RedirectToAction("Index");
}
return View(groupPolicy);
}
Which is fine, the Policy saves. However, take this into consideration:
GroupPolicy has, say, 3 fields (in reality there are, maybe, 60):
bool IsPolicy
string Name
string Description
Name and Description are on the form, so that's fine. IsPolicy isn't used on the form, so that gets defaulted to false when posted back to the GroupPolicy object in the Adjustment method.
I can't really put IsPolicy in a Hidden field on the form, as that won't be elegant for 60+ fields in my actual solution, the HTML would be all over the place.
Now that the bool is defaulted to false, it completely abolishes the chance of me knowing if the field has changed or not. All I really want is a method for this data to be preserved, whilst keeping the new information on the Edit form.
Is this possible, am I missing something obvious?
Well first of all, GroupPolicy should be a view model and not an entity - and as such it should be tailored for the view e.g.
public class GroupPolicyViewModel
{
[HiddenInput]
public Guid Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
...
}
Then in your action you don't need to worry about assigning values that have changed, you just map the view model directly across e.g.
public ActionList Adjustment(GroupPolicyViewModel viewModel)
{
if (ModelState.IsValid)
{
// pull actual entity from service
var groupPolicy = _service.GetGroupPolicy(viewModel.Id);
// update entity from view model
groupPolicy.Name = viewModel.Name;
groupPolicy.Description = viewModel.Description;
...
}
}
This keeps a clean separation between your view & business logic. Also, it allows you to add annotations for client-side validation without affecting your real model.
GroupPolicy has, say, 3 fields (in reality there are, maybe, 60)
I would recommend using AutoMapper for this e.g.
// call this once only e.g. Application_Start in the Global.asax
Mapper.CreateMap<GroupPolicyViewModel, GroupPolicy>();
...
// in your Adjustment action
var groupPolicy = _service.GetGroupPolicy(viewModel.Id);
groupPolicy = Mapper.Map<GroupPolicyViewModel, GroupPolicy>(viewModel, groupPolicy);
_service.SaveGroupPolicy(groupPolicy);
If IsPolicy not on the form then it shouldn't even be part of your model - this will prevent posting of this field into your model and so your check won't even be needed for IsPolicy.
Rather than accepting GroupPolicy as the parameter into the action, create a cut down object GroupPolicyInputModel with only fields that are on the form.
Then use your generic auditing to only compare all the posted fields, as per any other form.

Editing form fields via ajax and .net mvc

I have a number of inputs on my page. I would like to save the changes to the model on the input blur, so as I change the value of each input it gets saved back to server, like Google contacts.
<input id="FirstName" name="FirstName">Jack</input>
I create a blur event using jquery to post the value back to the server. It posts a structure with the name of the input, the value and an id of the entity.
$.post(url, { id: "2", key: "FirstName", value: "Jack" }, successFuction);
In my controller I have:
public ActionResult EditField(int id, string key, string value)
I then retrieve the entity using EntityFramework with the id. I then wanted to update the property on the model for the field.
var entity = _db.Get(id);
entity[key] = value;
return Content "Success";
Which I obviously can't do! The only way I can think off is multiple methods for each field so EditName, EditAddress etc. which seems wrong. I want this method to be able to handle each property of the model.
What is a better way to structure the controller instead of writing multiple methods for each individual field?
You could post your entire form (e.g. first name, last name, etc.) on each blur for any of your fields (this should be fine since you're saving all changes as the user progresses on the form anyway). Unless you're really trying to save bytes, posting the whole form seems fine.
You could just post the field name and then use reflection to look up the property of your object and set the value.
I think that you can do it if you are willing to model the entity in a general way:
public class FieldEntity {
public int Id { get; set; }
public string Key { get; set; }
public string Value { get; set; }
}
Then use it inside the context like:
var fieldEntity = db.Find(id);
fieldEntity.Key = key;
fieldEntity.Value = value;
db.SaveChanges();
However, it is usually better to structure data in a way that is meaningful. In the example you describe it looks like you might have a Person and Address entity. So why not have a Person entity that has a property Address?

Categories

Resources