I'm very new to ASP.NET MVC, so forgive me if this is something I should know. I haven't seen any obvious documentation on it, so here goes:
I have a LINQ to Entities data model and a MVC project. I use a lot of javascript/jquery, so have opted to access my data from the client through a WebAPI as json objects. However, I don't want to pass all the entity object properties though to the client, so I have added separate models to my MVC project in which I handle MVC model validation and Binding to my Views. Also, in order to work with it in my jquery, I have created json versions of the models.
This is only the start of the project and I don't want to start it off on the wrong foot. Having three versions of my models for each entity in my business layer is going to be a nightmare! I am sure that the overall structure of my project is a very common one, but can't see many developers settling for such duplication of code. There must be a better way of implementing it.
Any thoughts? Really appreciate any input.
In answer to your comment above - you can create your javascript viewmodel as a standard js object. I tend to use Knockout.js so it would look like this:
jsController.Resource = function (data) {
self.UserId = ko.observable(data.UserId);
self.FullName = ko.observable(data.Name);
self.RoleName = ko.observable(data.RoleName);
self.RoleId = ko.observable(data.RoleId);
}
and then use an ajax post method to post it to your MVC action
jsController.addToUndertaking = function (resource, isAsync) {
mylog.log("UndertakingId at post = " + jsController.undertakingId);
var action = $.ajax({
type: "POST",
url: "/TeamMember/AddUserToUndertaking",
data: resource,
cache: false,
async: isAsync
});
action.done(function () {
resource.AllocatedToUndertaking(true);
//Do other funky stuff
});
};
Create your MVC action so that it accepts a forms collection as so:
public ActionResult AddUserToUndertaking(FormCollection postedResource)
{
if (Request.IsAjaxRequest() == false)
{
const string msg = "Non ajax request received";
Logger.ErrorFormat(msg);
throw new SecurityException(msg);
}
if (postedResource == null)
{
Logger.Debug("Null resource posted - terminating.");
return new HttpStatusCodeResult(500);
}
var resource = new AllocatedResourceAjaxViewModel(postedResource);
//Do something Funky
return new HttpStatusCodeResult(200);
}
and then you create your MVC viewmodel from the forms collection (i tend to do this by passing in the forms collection as a constructor method to the viewmodel).
public class AllocatedResourceAjaxViewModel
{
public int UserId { get; set; }
public string Name { get; set; }
public string RoleName { get; set; }
public int RoleId { get; set; }
public AllocatedResourceAjaxViewModel()
{}
public AllocatedResourceAjaxViewModel(NameValueCollection formData)
{
UserId = JsFormDataConverter.Int(formData["UserId"]);
Name = Convert.ToString(formData["FullName"]);
RoleName = Convert.ToString(formData["RoleName"]);
RoleId = JsFormDataConverter.Int(formData["RoleId"]);
}
}
As a null int in your javascript VM will lead to a string of 'undefined' being passed you need a converter method to safely extract non strings.
public static class JsFormDataConverter
{
public static bool Boolean(string formValue, bool defaultValue = false)
{
if (formValue.ToLower() == "true") return true;
if (formValue.ToLower() == "false") return false;
return defaultValue;
}
public static int Int(string formValue, int defaultValue = 0)
{
int result;
return int.TryParse(formValue, out result)
? result
: defaultValue;
}
}
and there you go. I am sure you can improve on the above but that will get you going.
The way that I have always worked is that you have your Models e.g. Order & OrderLines which are where you store all your data and get hydrated either directly from the database by SQL or (more usually these days ) by an ORM such as NHibernate or Entity Framework.
You then have ViewModels - these are used to transport the data from your application to the views - either directly ie a strongly typed view bound to say an OrderViewModel or via an action returning a JsonResult.
A OrderViewModel is not a duplication of Order as it is designed to only hold the data that is needed to be presented on the screen (If you have many different views displaying an Order in different ways it could be perfectly acceptable to have many different ViewModels -one for each view containing only the fields needed for each view). ViewModels should also not contain any complex types except other ViewModels. this helps keep accidental data access out of the views (think security and performance).
So Given
public class Order
{
public int Id { get; set; }
public DateTime OrderDate { get; set; }
public User User { get; set; }
public string Description { get; set; }
public List<OrderLine> OrderLines { get; set; }
}
public class OrderLine
{
public int Id { get; set; }
public Order Order { get; set; }
public String Description { get; set; }
public int Quantity { get; set; }
public int Weight { get; set; }
public int Price { get; set; }
}
public class User
{
public int Id { get; set; }
public string Name { get; set; }
}
You could have the two ViewModels
public class OrderViewModel
{
public int ID { get; set; }
public List<OrderLineViewModel> OrderLines { get; set; }
public DateTime OrderDate { get; set; }
public int UserId { get; set; }
public string Description { get; set; }
}
public class OrderLineViewModel
{
public int Id { get; set; }
public int OrderId { get; set; }
public String Description { get; set; }
public int Quantity { get; set; }
public int Weight { get; set; }
public int Price { get; set; }
}
The view models could then be serialized into JSON as needed or marked up with validation attributes etc.
Related
What is the benefit of creating a ModelBinder when using lib.web.mvc. ?
This example from 2011 does not use a ModelBinder
http://tpeczek.com/2011/03/jqgrid-and-aspnet-mvc-strongly-typed.html
public class ProductViewModel
{
#region Properties
public int Id { get; set; }
public string Name { get; set; }
[JqGridColumnSortingName("SupplierId")]
public string Supplier { get; set; }
[JqGridColumnSortingName("CategoryId")]
public string Category { get; set; }
[DisplayName("Quantity Per Unit")]
[JqGridColumnAlign(JqGridColumnAligns.Center)]
public string QuantityPerUnit { get; set; }
[DisplayName("Unit Price")]
[JqGridColumnAlign(JqGridColumnAligns.Center)]
public decimal? UnitPrice { get; set; }
[DisplayName("Units In Stock")]
[JqGridColumnAlign(JqGridColumnAligns.Center)]
public short? UnitsInStock { get; set; }
#endregion
#region Constructor
public ProductViewModel()
{ }
public ProductViewModel(Product product)
{
this.Id = product.Id;
this.Name = product.Name;
this.Supplier = product.Supplier.Name;
this.Category = product.Category.Name;
this.QuantityPerUnit = product.QuantityPerUnit;
this.UnitPrice = product.UnitPrice;
this.UnitsInStock = product.UnitsInStock;
}
#endregion
}
But the latest examples are using them
http://tpeczek.codeplex.com/SourceControl/latest#trunk/ASP.NET%20MVC%20Examples/jqGrid%20Examples/jqGrid/Models/ProductViewModel.cs
namespace jqGrid.Models
{
[ModelBinder(typeof(ProductViewModelBinder))]
public class ProductViewModel
{
#region Properties
public int? ProductID { get; set; }
public string ProductName { get; set; }
public int SupplierID { get; set; }
public int CategoryID { get; set; }
public string QuantityPerUnit { get; set; }
public decimal UnitPrice { get; set; }
public short UnitsInStock { get; set; }
#endregion
}
public class ProductViewModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
ProductViewModel model = (ProductViewModel)base.BindModel(controllerContext, bindingContext);
if (controllerContext.HttpContext.Request.Params["id"] != "_empty")
model.ProductID = Convert.ToInt32(controllerContext.HttpContext.Request.Params["id"]);
model.SupplierID = Convert.ToInt32(controllerContext.HttpContext.Request.Params["Supplier"]);
model.CategoryID = Convert.ToInt32(controllerContext.HttpContext.Request.Params["Category"]);
model.UnitPrice = Convert.ToDecimal(controllerContext.HttpContext.Request.Params["UnitPrice"].Replace(".", CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator));
return model;
}
}
Model Binders provide one of the most convenient functions that MVC has to offer. Model Binders' main job is to convert HTML Query Strings to strong-types. Out of the box, MVC model binders do an excellent job, but you may have a strong-type that doesn't work with the default binders. In that case you can create your own. Or you can customize them for example, maybe the postback only contains a single string value that you want to return an entire class or even a viewmodel packed with stuff.
Couple of things to keep in mind with the default behavior are: 1) MVC news up instances on it's own of Action Methods that take a first parameter of a model or view model class! 2) It will then attempt to populate that new instance with data returned from the Web Form (using Name/Value pairs) of the Query string. 3) Validation of the object (fields) happens before the first line in the controller is executed. 4) MVC model binding will NOT throw an error if there is missing field data (make sure your posted form fields have everything you need.
Finally with the functionality described above you can go a long way without writing custom binders. However they are there to handle the edge cases or any "tricks" you want to implement to make your application lean and mean. For me, I almost always use strongly-typed views and view-models as MVC does a great job of supporting full view binding to view models.
Is there a way in MVC to pass information from one controller to another? I have a character model that looks like this:
public class Character
{
[Key]
public string CharacterID { get; set; }
public string UserID { get; set; }
public string Name { get; set; }
public int Str { get; set; }
public int Con { get; set; }
public int Dex { get; set; }
public int Int { get; set; }
public int Wis { get; set; }
public int Cha { get; set; }
public int BaseAttack { get; set; }
}
And a separate weapon model like this:
public class Weapons
{
[Key]
public string WeaponID { get; set; }
public string UserID { get; set; }
public string CharacterID { get; set; }
public string Name { get; set; }
public string Type { get; set; }
public string Range { get; set; }
public int Damage { get; set; }
public int Crit { get; set; }
public int CritMultiplier { get; set; }
public string Hands { get; set; }
public string Distance { get; set; }
}
To create a weapon, you first need to create a character which assigned an ID, and I want to be able to pass that ID into the create method of my weapon controller. Is there a way to do this? Thanks
You can use TempData for this purpose. TempData stores data only between two requests. When you set the TempData the next request that is initiated can retrieve value from the TempData and it will be erased for any consequent requests.
[HttPost]
public ActionResult CreateCharacter()
{
// creates charaeters here and sets the tempdata
TempData['CharacterId'] = 50;
return RedirectToAction('CreateWeapon');
}
[HttpGet]
public ActionResult CreateWeapon()
{
var weaponModel = new WeaponModel() { CharacterId = (int)TempData['CharacterId'] };
return View(weaponModel);
}
and in your view simply have a hidden for the CharacterId, so it will be persisted if you your post fails validation or if you need to re-display the view.
#Html.HiddenFor(e => e.CharacterId);
Again this is just one approach, only if you you don't want to pass the CharacterId in the url.
You can also achive this just by passing it in the url:
[HttPost]
public ActionResult CreateCharacter()
{
// creates charaeters here and sets the tempdata
return RedirectToAction('CreateWeapon', new { characterId = 50 });
}
[HttpGet]
public ActionResult CreateWeapon(int characterId)
{
var weaponModel = new WeaponModel() { CharacterId = characterId };
return View(weaponModel);
}
I would be inclined to pass the character id to the create weapon action via routing, either as a route token that forms part of the path or via the query string. Be sure to check that the weapon can logically be associated with the character to whom the id corresponds.
You could also pass the id using TempData or Session, but considering both by default will take up memory on the web server, the simple option is to use the routing. In addition, unless you call TempData.Keep("key") after accessing TempData, the value will be removed from TempData after the first access, potentially causing issues if the user refreshes the browser window.
You could use RedirectToAction(), though as titled this will cause browser redirection.
return RedirectToAction("CreateWeapon", "Weapon", new { id = yourid });
or
#Html.ActionLink("CreateWeapon", "Create", new { id = yourid })
Edit: Your plain object property names and your action method variables need to match, to do this.
I use Asp.Net MVC, Entity Framework. I have a form it looks like below.
Here, dropdownlist is filled from a table(types). Checkboxes is filled from another table(test). Tables are like below:
public class Types
{
public int TypesID{get;set;}
public string TestName { get; set; }
public string TestExplanation { get; set; }
public int TestTime { get; set; }
}
public class Tests
{
public int TestID{get;set;
public string Name { get; set; }
public string Code { get; set; }
}
public class Types_Tests
{
public int Types_TestsID{ get; set; }
public int TypesID { get; set; }
public int TestsID { get; set; }
public virtual Types Types { get; set; }
public virtual Tests Tests { get; set; }
}
Types_test table is relation table between Types and Tests. When I click Kaydet button, it shuld save type and checked tests. I made this operation using ViewBag, javascript and hdnvalue.I added checked checkboz values to a hdntext. I made saving process like below:
[HttpPost]
public ActionResult Index(string drpType, string hdntesttypes)
{
var TypeList = Types.GetAll();
ViewBag.TypesList = new SelectList(TypeList, "Id", "Name");
var testypeList = testTypes.GetAll();
ViewBag.TestTypesList = new SelectList(testypeList, "Id", "TestName");
GenericRepository<TestDisabledTypes> testDisabledRepository = new GenericRepository<TestDisabledTypes>(_context);
if (!string.IsNullOrEmpty(hdntesttypes))
{
string[] disabletypesArray = hdntesttypes.Split(',');
using (TransactionScope trns = new TransactionScope())
{
for (int i = 0; i < disabletypesArray.Length; i++)
{
Test_Types types = new Test_Types ();
types.TestTypesID = Convert.ToInt32(disabletypesArray[i]);
types.TypesID = Convert.ToInt32(drpType);
testDisabledRepository.Insert(types);
}
trns.Complete();
}
}
return View();
}
It wokrs. But I search better solution for this process. Can someone give me any idea?
Thanks.
If you don't need additional attributes for your entity class, you don't need create link table.
Just define the following class, and EF will generate the link table for you automatically.
public class Type
{
public int TypesID{get;set;}
public string TestName { get; set; }
public string TestExplanation { get; set; }
public int TestTime { get; set; }
public ICollection<Test> Tests { get; set; }
}
public class Test
{
public int TestID{get;set;
public string Name { get; set; }
public string Code { get; set; }
public ICollection<Type> Types {get;set;}
}
Well, in EntityFramework if you want to create a many to many relation object you need to create new object of "linking" entity. Unfortunately, it is not possible to add first object, add second object and say "Guys, you are in many to many relationships. Are you happy then?" :) You need to create relation object, set appropriate fields in it (I think these are ids of two objects itself) and add it to relation collection (entity) in your model. But before doing so you need to be sure that objects with data you are linking with are already exists in database. Otherwise you'll get an error
Also it's not necessary to create manually transaction because EF does it for you automatically each time you get/save your data
Hope someone can help - this has been bugging me for around 2 hours - its probably something simple :)
Kendo UI Grid sends a request to my controller
http://localhost:1418/user/update?UserID=1&UserName=Admin&RoleName=Admin&Email=c.j.hannon%40gmail.com&Active=true&Company%5BCompanyID%5D=1&Company%5BCompanyName%5D=asd
However, the controller class 'Company' isnt bound by the binder? Can any one help my view model and controller action signature are below:
[HttpGet]
public JsonResult Update(UserViewModel model)
{
svcUser.UpdateUser(new UpdateUserRequest() {
UserID=model.UserID,
RoleID = model.RoleName,
Email = model.Email,
Active = model.Active.GetValueOrDefault(false),
UserName = model.UserName
});
return Json("", JsonRequestBehavior.AllowGet);
}
public class UserViewModel
{
public int UserID { get; set; }
public string UserName { get; set; }
public string RoleName { get; set; }
public string Email { get; set; }
public bool? Active { get; set; }
public CompanyViewModel Company { get; set; }
}
Cheers
Craig
A few things. Your immediate problem is that Company is mapped to a complex object not a primitive type. Kendo Grid just does not do this (as of this writing). Just guessing, but you probably want to setup a foreign key binding on the Grid and just pass back the Id of the company from a listbox. This is not as bad as you think and it will immediatly fix your problem and look nice too.
Maybe personal taste but seems to be a convention. Use the suffix ViewModel for the model that is bound to your View and just the suffix Model for your business objects. So a Kendo Grid is always populated with a Model.
Ex.:
public class UserModel
{
public int UserID { get; set; }
public string UserName { get; set; }
public string RoleName { get; set; }
public string Email { get; set; }
public bool? Active { get; set; }
public int CompanyID { get; set; }
}
public class CompanyModel
{
public int ID { get; set; }
public string Name { get; set; }
}
public class UserViewModel
{
public UserModel UserModel { get; set; }
public IList<CompanyModel> Companies { get; set; }
}
public ActionResult UserEdit(string id)
{
var model = new UserViewModel();
model.UserModel = load...
model.Companies = load list...
return View(model);
}
#model UserViewModel
...
column.ForeignKey(fk => fk.CompanyId, Model.Companies, "ID", "Name")
(Razor Notation)
BUT! This is just an example, you are better off Ajax loading the Grid with the IList becuase I assume you have many Users in the Grid at once, though you could server bind off the ViewModel with a List too. But the list of Companies is probably the same every time, so map it to the View just liek this rather than Ajax load it every time you do a row edit. (not always true)
I've built my Domain model layer, my repository layer, and now I'm working on my DTO layer to be used by a webApi project. I'm in the middle of implementing an Update service method, and I'm wondering about partial updates. Here's my DTO class:
public class FullPersonDto
{
public FullPersonDto()
{
Friends = new List<Person>();
}
public FullPersonDto(Person person)
{
PersonId = person.PersonId;
DateCreated = person.DateCreated;
Details = person.Details;
Friends = new List<Person>();
foreach (Person friend in person.Friends)
{
Friends.Add(new PersonDto(friend));
}
}
[Key]
public int PersonId { get; set; }
[Required]
public virtual DateTime DateCreated { get; set; }
public virtual string Details { get; set; }
public List<Person> Friends { get; set; }
public Person ToEntity()
{
var person = new Person
{
PersonId = PersonId,
DateCreated = (DateTime) DateCreated,
Details = Details,
Friends = new List<Person>()
};
foreach (PersonDto friend in Friends)
{
person.Friends.Add(friend.ToEntity());
}
return person;
}
}
Here's my Update method in my Repository:
public Person UpdatePerson(Person person)
{
var entry = _db.Entry(person);
if (entry.State == EntityState.Detached)
{
var dbSet = _db.Set<Person>();
Person attachedPerson = dbSet.Find(person.PersonId);
if (attachedPerson != null)
{
var attachedEntry = _db.Entry(attachedPerson);
attachedEntry.CurrentValues.SetValues(person); // what if values are null, like ID, or DateCreated?
}
else
{
entry.State = EntityState.Modified;
}
}
SaveChanges();
return person;
}
My question is: What if I only need to update the Details of a person via my webAPI? Is the convention to construct an entire PersonDto and Update the entire object using SetValues, or is there any way I can specify that I only want a single field updated so that I don't have to send a ton of data over the wire (that I don't really need)?
If it is possible to do partial updates, when is it ever good to update the entire entity? Even if I have to update 5/7 properties, it requires that I send old data for 2/7 to re-write so that SetValues doesn't write nulls into my fields from my DTO.
Any help here would be awesome... totally new to this stuff and trying to learn everything right. Thank you.
I've taken similar approach to do optimization, and I've faced same issues with null values when attaching (not just null, you'll have issue with boolean as well). This is what I've come up with:
public static void Update<T>(this DbContext context, IDTO dto)
where T : class, IEntity
{
T TEntity = context.Set<T>().Local.FirstOrDefault(x => x.Id == dto.Id);
if (TEntity == null)
{
TEntity = context.Set<T>().Create();
TEntity.Id = dto.Id;
context.Set<T>().Attach(TEntity);
}
context.Entry(TEntity).CurrentValues.SetValues(dto);
var attribute = dto.GetAttribute<EnsureUpdatedAttribute>();
if (attribute != null)
{
foreach (var property in attribute.Properties)
context.Entry(TEntity).Property(property).IsModified = true;
}
}
That is extension method for DbContext. Here are the interfaces IDTO and IEntity:
public interface IDTO
{
int Id { get; set; }
}
public interface IEntity
{
int Id { get; set; }
Nullable<DateTime> Modified { get; set; }
Nullable<DateTime> Created { get; set; }
}
I'm using my custom EnsureUpdatedAttribute to annotate what properties should always be updated (to deal with nulls / default values not being tracked):
public class EnsureUpdatedAttribute : Attribute
{
public IEnumerable<string> Properties { get; private set; }
public EnsureUpdatedAttribute(params string[] properties)
{
Properties = properties.AsEnumerable();
}
}
And this is a sample of usage:
public class Sample : IEntity
{
public int Id { get; set; }
public string Name { get; set; }
public bool Active { get; set; }
public Nullable<DateTime> Modified { get; set; }
public Nullable<DateTime> Created { get; set; }
}
[EnsureUpdated("Active")] /// requirement for entity framework change tracking, read about stub entities
public class SampleDTO : IDTO
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
[JsonIgnore] /// How to exclude property from going on the wire / ignored for serialization
public bool Active { get; set; }
}
[HttpPost]
public HttpResponseMessage SaveSample(SampleDTO dto)
{
dto.Active = true;
_ctx.AddModel<Sample>(dto);
_ctx.SaveChanges();
return NoContent();
}
return NoContent() is just extension for returning 204 (NoContent).
Hope this helps.
Theres a few options you have, you can create a stored procedure to update the required parts (I wouldnt do this), or you can manually select the fileds to update on the model before saving the context changes with EF.
Heres an example how to update a specific field:
public void UpdatePerson(int personId, string details)
{
var person = new Person() { Id = personId, Details = details };
db.Persons.Attach(personId);
db.Entry(person).Property(x => x.Details).IsModified = true;
db.SaveChanges();
}
It will depend on your scenario what you want to do, but generally speaking its fine to send your whole entity to be updated, and this is how i would approach your situation potentially changing in the future if needed.