Static in asp.net mvc - asp.net-mvc

I am learning Asp.net MVC.
I am building an application in which I have following components:
1.Student class:
public class Students
{
[Required(ErrorMessage="Student Id is required")]
[Display(Name="Student Id")]
public int Sid { get; set; }
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
[Required]
public string Address { get; set; }
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:yyyy-MM-dd}")]
[DataType(DataType.Date)]
public DateTime DOB { get; set; }
[Required]
public string Email { get; set; }
}
2.StudentManager class
public List<Students> sList = new List<Students>()
{
new Students{Sid=1001,FirstName="A",LastName="T",DOB=Convert.ToDateTime("14-10-1987"),Email="a#b.com",Address="I"},
new Students{Sid=1002,FirstName="B",LastName="U",DOB=Convert.ToDateTime("14-10-1987"),Email="a#b.com",Address="I"},
new Students{Sid=1003,FirstName="C",LastName="V",DOB=Convert.ToDateTime("14-10-1987"),Email="a#b.com",Address="I"},
new Students{Sid=1004,FirstName="D",LastName="W",DOB=Convert.ToDateTime("14-10-1987"),Email="a#b.com",Address="I"},
new Students{Sid=1005,FirstName="E",LastName="X",DOB=Convert.ToDateTime("14-10-1987"),Email="a#b.com",Address="I"},
new Students{Sid=1006,FirstName="F",LastName="Y",DOB=Convert.ToDateTime("14-10-1987"),Email="a#b.com",Address="I"},
new Students{Sid=1007,FirstName="G",LastName="Z",DOB=Convert.ToDateTime("14-10-1987"),Email="a#b.com",Address="I"},
};
public void Edit(Students s)
{
Students stud = sList.Where(st => st.Sid == s.Sid).First();
stud.FirstName = s.FirstName;
stud.LastName = s.LastName;
stud.Email = s.Email;
stud.DOB = s.DOB;
}
3. And a student controller
public static StudentManager sm = new StudentManager();
// GET: Student
public ActionResult Index()
{
return View(sm.sList);
}
public ActionResult Edit(int? id)
{
Students student = sm.sList.Where(s => s.Sid == id).First();
return View(student);
}
[HttpPost]
public ActionResult Edit(Students s)
{
sm.Edit(s);
return RedirectToAction("Index");
}
My queries are:
1.I am able to edit a student's details.But if I do not use the keyword static in Controller then it is not updating.
2.How Model Binding automatically passes Student object to HttpPost Edit()?
Can someone please explain?

Every time you create StudentManager class you are creating new collection of students.
When StudentManager is static it is created only once, so you ll use the same object for every call and modify collection in it.
When StudentManager is not static for every call you ll create new object with new collection, so your edit will modify collection within object that ll be thrown away with a next call (like view).
It's not a good practice to have references to dependencies as static properties. I would recommend to make you collection property static and initialize it only once for life cycle of application and later migrate to use db or file or a call, depending of what your goal is.
Model Binding uses one of the registered Model Binders, some are provided by default so for main types of data, like JSON, you don't have to to anything.
I would definitely recommend to read a good book about mvc, to get the mechanics behind it. As the good and the bad part about it is it works like "magic" =)

Related

MCV Core Dropdownlist Stored Procedure

I have a model that looks as follows:
public partial class RoutePartner
{
public Guid Id { get; set; }
[Required]
public Guid PartnerId { get; set; }
[Required]
public string RouteCompany { get; set; }
}
The partnerId comes from another TABLE that has an Id and Name. I'm trying to build my CRUD forms to load the list of partners and make them available to the form. That is the best way to do this in MVC Core? I see many .NET examples but none for CORE.
One last point is that partner drop down should be populated by a stored procedure.
Thanks
Dan
Your class doesn't have to be the same as your database. Why not have a class called Partner that has the ParnterID and PartnerName? In your RoutePartner class have a Partner property, instead of just the ID.
Public class SomeViewModel //Used by your view
{
public RouteParnter {get; set;}
public ParnterList List<SelectListItem> {get; set;}
public static List<SelectListItem> PopulateDropdown()
{
List<SelectListItem> ls = new List<SelectListItem>();
DataTable someTable = Database.LoadParnters(); // Database is your data layer
foreach (var row in someTable.Rows)
{
ls.Add(new SelectListItem() { Text = row["PartnerName"].ToString();, Value = row["PartnerID"].ToString(); });
}
return ls;
}
}
public partial class RoutePartner
{
public Guid Id { get; set; }
[Required]
public Partner SomePartner { get; set; }
[Required]
public string RouteCompany { get; set; }
public RouteParnter(Guid id)
{
DataRow returnedRow = Database.LoadRouteParnter(id);
Id = id;
// Assuming your DataRow returned has all the info you need.
RouteCompany = row["RouteCompany"];
SomePartner = new Partner();
SomePartner.PartnerId = row["PartnerID"]; // cast to guid, just example
SomePartner.PartnerName = row["PartnerName"].ToString();
}
}
public class Partner
{
public Guid PartnerId { get; set; }
public string PartnerName { get; set; }
}
I would loosely couple the data layer. You could use ADO.NET or any other way to communicate with the database. I usually do this by creating a parameterized constructor (say taking in an ID to load), to call the data layer. The data layer returns a DataTable, DataRow, DataSet, or a custom object. In the constructor, you populate the values of the RouteParnter object. Likewise you could have a List in the ViewModel.
I wanted to provide a quick update to how I solved this issue. I ended up creating a PartnerContext for EF. Once created, in controllers that need access to the list, I placed PartnerContext.ToList() into ViewBag and then used ASP helpers to load the list.

How to create a viewModel object for many-to-many relationships

I'm new to ASP.NET and currently working on a simple app to show a list of people and their hobbies. I have the following classes:
public class Hobby
{
public int HobbyID {get;set;}
public string Name {get;set;}
public string Type {get;set;}
public ICollection<PersonHobby> PersonHobbies {get;set;}
}
public class Person
{
public int PersonID {get;set;}
public int Age {get;set;}
public string Name {get;set;}
public ICollection<PersonHobby> PersonHobbies {get;set;}
}
public class PersonHobby
{
public int PersonHobbyID {get;set;}
public int PersonID {get;set;}
public int HobbyID {get;set;}
}
When viewing a person's Details page, I also need to display their hobbies. I did some research and found that ViewModels are a good way to accomplish this. So I created one, but I'm not sure if I did it correctly:
public class PersonHobbiesViewModel
{
public Person Person {get;set;}
public IEnumerable<PersonHobby> PersonHobbies {get;set;}
public IEnumerable<Hobby> Hobbies {get;set;}
}
And at this point I know that I need to create a viewmodel object in my controller's Details method and populate it with data, but I don't know how to navigate through the different tables. I have this:
public ActionResult Details(int? id)
{
var viewModel = new PersonHobbiesViewModel();
viewModel.Person = db.Person.find(id);
viewModel.Hobbies = ???
return View(viewModel);
}
On the other hand, if I'm going in the completely wrong direction, let me know! Thanks in advance.
Firstly what you might like to do, is change your entity models ever so slightly and let EF6 deal with the many to many complexity for you.
Your new model might look like this:
public class Hobby
{
public int HobbyID { get; set; }
public string Name { get; set; }
public string Type { get; set; }
public Person Person { get; set; }
}
public class Person
{
public int PersonID { get; set; }
public int Age { get; set; }
public string Name { get; set; }
public ICollection<Hobby> Hobbies { get; set; }
}
Your context might be like:
public class MyContext : DbContext
{
public DbSet<Hobby> Hobbies { get; set; }
public DbSet<Person> People { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Hobby>()
.HasRequired<Person>(s => s.Person)
.WithMany(s => s.Hobbies)
.HasForeignKey(s => s.PersonId);
}
}
Then when you are creating your model you can simply query it like:
var person = db.People.Include(c => c.Hobbies).SingleOrDefault(x => x.PersonID == id);
var viewModel = new PersonHobbiesViewModel();
viewModel.Person = person;
viewModel.Hobbies = person.Hobbies;
return View(viewModel);
Have a read through the following tutorials about complex entity models and reading from them, they cover the same content.
Ps. I have not tested any of this.
#shenku's answer has correct way to map entities. But it is unnecessary if you don't consider about naming conventions.
Additionally Entity Framework has much of c#'s object oriented programming basics. That means if you pass the person object from your controller to view, you could access to your entities on view like below.
//Controller
var person = db.People.Include(c => c.Hobbies).SingleOrDefault(x => x.PersonID == id);
return View(person);
#*View Page*#
#using Project.Models
#* prints Person Name and Age *#
#Model.Name #Model.age
#* prints Hobby Names of Person
#foreach (var item in Model)
{
#item.name
}
You've answered your own problem correct as "circular reference" on your comment.
So you don't need PersonHobby class, Entity framework will automatically creates this table for you. This magic happens because you've defined hobby and person as a collection on their own classes.
Also you don't need a viewModel for your situation. Just pass the person or hobby object. Do not pass the Icollection because it's already loaded when you wrote db.People.Include(c => c.Hobbies). Also you can define classes as 'virtual' so it will load entities without include method.(Lazy-loading)

Benefit of ModelBinder for lib.web.mvc

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.

MVC strongly-typed view, and server side setting properties before sending to lower layers?

I have a layered application that send commands to the business layer (actually, the application is based on ncqrs framework, but I don't think it's important here).
A command looks like this :
public class RegisterUserCommand : CommandBase
{
public string UserName { get; set; }
public string Email{ get; set; }
public DateTime RegistrationDate { get; set; }
public string ApiKey {get; set;} // edit
}
There is no logic in this class, only data.
I want to have the users type their user name, email and I want the system to use the current date to build the command.
What is best between :
create a strongly typed view based on the RegisterUserCommand, then inject the date and the APi Key just before sending it to the business layer ?
create a RegisterUserViewModel class, create the view with this class and create the command object based on the view input ?
I wrote the following code (for the solution n°2) :
public class RegisterController : Controller
{
//
// GET: /Register/
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(RegisterUserViewModel registrationData)
{
var service = NcqrsEnvironment.Get<ICommandService>();
service.Execute(
new RegisterUserCommand
{
RegistrationDate = DateTime.UtcNow,
Email= registrationData.Email,
UserName= registrationData.Name,
ApiKey = "KeyFromConfigSpecificToCaller" // edit
}
);
return View();
}
public class RegisterUserViewModel
{
[Required]
[StringLength(16)]
public string Name { get; set; }
[Required]
[StringLength(64)]
public string Email{ get; set; }
}
}
This code is working... but I wonder if I choose the correct way...
thanks for advises
[Edit] As the Datetime seems to cause misunderstanding, I added another property "ApiKey", that should also be set server side, from the web layer (not from the command layer)
[Edit 2] try the Erik suggestion and implement the 1st solution I imagined :
[HttpPost]
public ActionResult Index(RegisterUserCommand registrationCommand)
{
var service = NcqrsEnvironment.Get<ICommandService>();
registrationCommand.RegistrationDate = DateTime.UtcNow;
registrationCommand.ApiKey = "KeyFromConfigSpecificToCaller";
service.Execute(
registrationCommand
);
return View();
}
... Is it acceptable ?
I think you would be better off with option #2, where you would have a separate ViewModel and a Command. While it may seem redundant (to an extent), your commands are really messages from your web server to your command handler. Those messages may not be formatted the same as your ViewModel, nor should they be. And if you're using NCQRS as is, you would then have to map your commands to your AR methods and constructors.
While it may save you a little bit of time, I think you pigeon-hole yourself in to modeling your domain after your ViewModels, and that should not be the case. Your ViewModels should be a reflection of what your user experiences and sees; your domain should be a reflection of your business rules and knowledge, and are not always reflected in your view.
It may seem like a bit more work now, but do yourself a favor and keep your commands separate from your view models. You'll thank yourself later.
I hope this helps. Good luck!
I would recommend putting this into the constructor of the RegisterUserCommand class. That way the default behavior is always to set it to DateTime.UtcNow, and if you need to set it to something explicitly you can just add it to the object initializer. This will also help in scenarios where you're using this class in other parts of your project, and you forget to set the RegistrationDate explicitly.
public class RegisterUserCommand : CommandBase
{
public string UserName { get; set; }
public string Email{ get; set; }
public DateTime RegistrationDate { get; set; }
public RegisterUserCommand()
{
RegistrationDate = DateTime.UtcNow;
}
}
And the Controller
public class RegisterController : Controller
{
//
// GET: /Register/
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(RegisterUserViewModel registrationData)
{
var service = NcqrsEnvironment.Get<ICommandService>();
service.Execute(
new RegisterUserCommand
{
Email= registrationData.Email,
OpenIdIdentifier = registrationData.OpenIdIdentifier
}
);
return View();
}
public class RegisterUserViewModel
{
[Required]
[StringLength(16)]
public string Name { get; set; }
[Required]
[StringLength(64)]
public string Email{ get; set; }
}
}
I would use number 1 and use the system.componentmodel.dataannotations.metadatatype for validation.
I created an example (answer) for another SO question Here.
This allows you to keep your model in another library, validate the fields and show the fields like you would internal/private classes with DataAnnotations. I'm not a big fan of creating a completely separate class for a view that has no additional value while having to ORM the data back to another class. (If you had additional values like dropdown list values, or default values then I think it would make sense).
Instead of
[HttpPost]
public ActionResult Index(RegisterUserViewModel registrationData)
{
var service = NcqrsEnvironment.Get<ICommandService>();
service.Execute(
new RegisterUserCommand
{
RegistrationDate = DateTime.UtcNow,
Email= registrationData.Email,
UserName= registrationData.Name,
ApiKey = "KeyFromConfigSpecificToCaller" // edit
}
);
return View();
}
You can have
[HttpPost]
public ActionResult Index(RegisterUserCommand registrationData)
{
var service = NcqrsEnvironment.Get<ICommandService>();
registrationData.ApiKey = "KeyFromConfigSpecificToCaller";
service.Execute(registrationData);
return View();
}

Passing complex object from view to controller/view in ASP.NET MVC

In my MVC application I have a problem with passing data from view to controller. I have fairly complex domain classes:
public class TaskBase : PersistableObject
{
public virtual TaskCategory Category { get; set; }
public virtual IList<TaskNote> Notes { get; set; }
public virtual string TaskTitle { get; set; }
public virtual string TaskBody { get; set; }
public virtual DateTime? CreationTime { get; set; }
public virtual User CreatedBy { get; set; }
public virtual int CompletionRatio { get; set; }
}
public class MainTask : TaskBase
{
public virtual IList<TaskBase> ChildTasks { get; set; }
public virtual User AssignedTo { get; set; }
public virtual IList<TaskHistory> History { get; set; }
}
public class TaskFormModel : ViewDomainBase
{
public MainTask Task { get; set; }
public LoginForm LoginInfo { get; set; }
}
And in my view I want to pass an instance of TaskFormModel to the controller.
<%= Html.ActionLink<TaskController>("Edit Task", (x) => x.Edit(new TaskFormModel() { Task = item, LoginInfo = Model.LoginInfo }))%>
And here is the controller action:
public ActionResult Edit (TaskFormModel taskInfo)
{
return View(ViewPageName.TaskDetailsForm, task.Task);
}
In this action method taskInfo comes null even if I pass non-null instance from view. I think I have a binding problem here. I think, writing custom model binder requires every property to be converted and also when new fields added then binder class should also be changed, so I don't want custom model binder to do this. Is there any other way to pass data to controller in this scenario? Or could custom model binder can be coded so that less code written and also when new properies are added binder class will not need to be changed?
Edit After Comments: What I am trying to achieve is basically to pass an instance from one view to another view, without querying repository/db in my controller's action.
First version of answer:
Your GET edit method should be like:
public ActionResult Edit (int id)
{
var model = taskRepository.GetTaskEditModel(id);
return View(ViewPageName.TaskDetailsForm, model);
}
and ActionLink:
<%= Html.ActionLink("Edit Task", "Edit", "Task", new { model.Task.id })%>
If you want to pass complex objects to controller, you should wrap them up in html form and pass to POST action.
In my opinion you are doing something wrong.
As I understand: you are trying to instantiate a new object, pass it to browser and get it back.
well you cant.
If object you want to edit exists already in your storage, then you should alter your ActionLink to reference it by id, and instantiate it inside your Edit action.
Take a look at default strongly typed index views created by tooling.

Resources