asp.net MVC 4 simple sorting - c#

I am using ASP.net MVC4 and am trying to accomplish a simple sort , so far I have found how to make the whole database sortable - by using ActionLink buttons in my View (failed to make it work btw...), but what I am trying to accomplish is a permanently sorted database.
My view and controller are both scaffolded at the moment, no changes made to this part. I am trying to make the record with the least TimeRemaining to always show up on top of the list.
Thanks!
My Database:
public class EquipmentDataBase {
public int ID { get; set; }
[DisplayName("Name")]
public string equipment { get; set; }
[DisplayName("Inv.Nr.")]
public string InventoryNumber { get; set; }
[DisplayName("Location")]
public string Location { get; set; }
[Required(ErrorMessage ="Please specify date")]
[DataType(DataType.Date)]
[DisplayName("Next Inspection")]
public DateTime NextInspectionDate { get; set; }
[Required(ErrorMessage ="PleaseSpecifyRegistrationDate")]
[DataType(DataType.Date)]
public DateTime RegistrationDate { get; set; }
public string Responsible { get; set; }
public TimeSpan TimeRemaining
{
get
{
return (NextInspectionDate - DateTime.Today);
}
}
My Controller:
namespace PeriodicInspections.Controllers {
public class EquipmentDBController : Controller
{
private EquipmentDbContext db = new EquipmentDbContext();
// GET: EquipmentDB
public ActionResult Index()
{
return View(db.equipment.ToList());
}

If you want to have it sorted by the same criteria, the best approach is to sort them at database level. You can achieve this by changing the code in the controller as follows:
// GET: EquipmentDB
public ActionResult Index()
{
return View(db.equipment.OrderBy(x => x.NextInspectionDate).ToList());
}
The OrderBy sorts the data by NextInspectionDate. This property is present at database level in contrast to the helper property TimeRemaining that is only available in the .NET code. As regards the sort order, this will make no difference.

Use Linq. instead of
return View(db.equipment.ToList());
use
return View(db.equipment.ToList().OrderBy(e=>e.TimeRemaining );

Related

Include only one property, not entire database row

Model:
public class Word
{
public int ID { get; set; }
public string Title { get; set; }
public DateTime? WhenCreated { get; set; }
public ApplicationUser Author { get; set; }
[NotMapped]
public string AuthorName
{
get
{
if (Author != null)
{
return Author.UserName;
}
else {
return "";
}
}
}
public List<Definition> Definitions { get; set; }
}
Controller:
[HttpGet]
public IEnumerable<Word> Get()
{
return _db.Words.Include(x=>x.Author).ToList();
}
My Controller now returns entire ApplicationUser class which is one of properties of Word. I want to send only one property of ApplicationUser: UserName. How can I do that?
I've added AuthorName, which would return only data that I want from ApplicationUser. Unfortunately I still have to .Include(x=>x.Author) to make this property work. Can I somehow omit including Author in process of data serialization (to hide it when sending data to user)?
I know I can use .Select() method, but it requires me to type all properties I will need. If I modify my Model in the future, I will need to update all those .Select() which will would be inconvenient and waste of time.
How would you solve that?
You need to create a Dto object and assign the values to it and return the Dto instead.
Dto
public class WordDto
{
public string Title { get; set; }
public DateTime? WhenCreated { get; set; }
public string AuthorName { get; set; }
}
Then in your action
[HttpGet]
public async Task<IEnumerable<WordDto>> Get()
{
return _db.Words
.Include(x=>x.Author)
.Select(x =>
new WordDto
{
Title = x.Title,
DateTime = x.WhenCreated,
AuthorName = x.Author?.UserName ?? string.Empty
}
)
.ToListAsync();
}
You can try it as shown below.
Note : You don't need to use Include here.
[HttpGet]
public async Task<IEnumerable<Word>> Get()
{
return _db.Words.Select(x => new
{
Word = x,
AuthorName = x.Author.UserName
}
).ToList();
}
Create a View model and use AutoMapper to populate. Look at using AutoMapper and ProjectTo extension https://github.com/AutoMapper/AutoMapper/wiki/Queryable-Extensions
That way if you add properties to View model they will be automatically mapped if they exist on your EF model
So create a VM with required properties named appropriately (see AutoMapper docs on naming conventions):
public class WordVM
{
public string Title { get; set; }
public DateTime? WhenCreated { get; set; }
public string AuthorUserName { get; set; }
}
Then use AutoMapper to project (it will do any required includes so if you changed the VM later then it would handle that)
_db.Words.ProjectTo<WordVM>().ToList();
You don't need the NotMapped property AutoMapper would map the navigation property Author and the Author Property UserName to AuthorUserName
My workaround was to get all the related entities with .include(), then loop over them and omit the property values I did not want to return. It would require some maintenance in case your model changed, but surprisingly, it did not impact the response time dramatically.

MVC ViewModel, using a VM with some properies marked required, depending if its a GET or POST

I've often found myself fixing validations for the modelstate manually, due to the inconsistency of some fields that are required in a view model during post and get.
Supposing I've got this View Model:
public class RestaurantMenuName_ViewModel
{
public Int64 RestaurantMenuNameId { get; set; }
public Int64 OwnerId{ get; set; }
public string MenuNameCategory { get; set; }
public string CategoryDescription { get; set; }
public bool IsFormSaved { get; set; }
}
During a GET request the controller/Action requires the validation of just the fields, RestaurantMenuNameId and OwnerId. When calling the Action RestaurantMenuName, the query string values are RestaurantMenuNameId and OwnerId. Modelstate validation will be done on:
RestaurantMenuNameId
OwnerId
During a POST request the controller/Action will require the modelstate validation of the fields:
RestaurantMenuNameId
OwnerId
MenuNameCategory
CategoryDescription
This is the inconsistency issue I'm talking about, a solution could be using a ViewModel for Get requests and one for Post, but this could be real a time waster and error prone. Using ViewBag is out of discussion.
Question:
Is there a way to tell MVC that we want some fields [required] for GET and other for POST?
The following is a Pseudo-code of what I'm talking about:
public class RestaurantMenuName_ViewModel
{
[Required: in GET, POST] //<--Pseudo code
public Int64 RestaurantMenuNameId { get; set; }
[Required: in GET, POST] //<--Pseudo code
public Int64 OwnerId { get; set; }
[Required: in POST] //<--Pseudo code
public string MenuNameCategory { get; set; }
[Required: in POST] //<--Pseudo code
public string CategoryDescription { get; set; }
public bool IsFormSaved { get; set; }
}
It's not a very good practice (and confusing in your case) to pass complex objects when you need only few properties. It will be better to pass only the required ids as primitives.
If the case is special and you really need the complex objects, it will be better to create two different view models for every request and decorate the required properties accordingly.
However, you can create your own require validation attribute which will validate properties dependening on the current request.
public class MyRequiredAttribute : ValidationAttribute
{
private string httpVerb;
public MyRequiredAttribute(string httpVerb)
{
this.httpVerb = httpVerb;
}
public override bool IsValid(object value)
{
if(HttpContext.Current.Request.HttpMethod == this.httpVerb)
{
return value != null;
}
return true;
}
}
// Usage
public class MyViewModel
{
[MyRequired("GET")]
public string A { get; set; }
[MyRequired("POST")]
public string B { get; set; }
}
Note: you can use an enumeration to avoid some difficulties (ex. upper case, lower case, misspelling etc.) and also you can override the FormatErrorMessage method to change the default error message and format it properly.

Is there any way to pass a whole model via html.actionlink in ASP.NET MVC 3 razor

#Html.ActionLink("Reply", "BlogReplyCommentAdd", "Blog",
new { blogPostId = blogPostId, replyblogPostmodel = Model,
captchaValid = Model.AddNewComment.DisplayCaptcha },null)
My Controller :
public ActionResult BlogReplyCommentAdd(int blogPostId, BlogPostModel model, bool captchaValid)
{}
In my Controller i am passing whole model.But values of properties are null till reach to Action
BlogPostModel:
[Validator(typeof(BlogPostValidator))]
public partial class BlogPostModel : BaseNopEntityModel
{
public BlogPostModel()
{
Tags = new List<string>();
Comments = new List<BlogCommentModel>();
AddNewComment = new AddBlogCommentModel();
}
public string SeName { get; set; }
public string Title { get; set; }
public string Body { get; set; }
public bool AllowComments { get; set; }
public int NumberOfComments { get; set; }
public DateTime CreatedOn { get; set; }
public IList<string> Tags { get; set; }
public IList<BlogCommentModel> Comments { get; set; }
public AddBlogCommentModel AddNewComment { get; set; }
}
Any how i need whole model.
Thanks in advance
You're not going about this correct way at all. The only time whole model data gets passed in the query string like this is when an HTML form is submitted via a GET operation; and even then that's not ideal except in cases where HTTP caching isn't a problem.
In this case, you're already passing the ID of the blog post in the query string to the controller method - so in your controller method you go and retrieve the blog post model and then pass it to the view.
Edit Upon adding this answer - #levelnis' comment then showed up - what (s)he's/they've said is exactly the same thing.
Update
And think about it - if you make it so that's how your site works - then anybody could 'publish' content on your blog's site by seeding the query string with all sorts of horrible stuff, not to mention making your site a playground for SEO spammers and such.

ASP.NET MC3 - single interface to create a record and the supporting records

I have the beginnings of an ASP.NET MVC3 application that is supposed to implement scheduling. I have the following in my model:
public class Schedule
{
public int ScheduleID { get; set; }
public bool isDisabled { get; set; }
[DisplayName("For Delivery")]
public bool isDeliver { get; set; }
public int Count { get; set; }
public virtual ICollection<TimeofDay> Times { get; set; }
public virtual Location Location { get; set; }
public int Week { get; set; }
public int weekday { get; set; }
}
public class TimeofDay
{
[Key]
public int TimeID {get;set;}
public DateTime Time { get; set; }
}
The model is supposed to accept 0 or more Time of Day entities that I pass by using JavaScript to create a new input field:
function createtimefield() {
var TimeDiv = document.getElementById('timefields');
var newDivInput = document.createElement("input");
newDivInput.name = "Times";
idText="Time" + GLOBAL_timeDivIdCount++;
newDivInput.id = idText;
newDivInput.value = "12:00 am";
TimeDiv.appendChild(newDivInput);
}
My Controller will work file for accepting the data passed up until I add data to the time fields. This is supposed to create new entities in the TimeofDay table that gets generated by the model, and link back to the the ScheduleID. I don't want two interfaces to input this simple data, but can't seem to find the way to create both entities with MVC3 in a single action. Anyone have any ideas?
Andrew
(Thank you for reading)
As requested the controller was:
public ActionResult Create(Schedule schedule, string[] schedTimes)
{
if (ModelState.IsValid)
{
Schedule newschedule = db.Schedule.Add(schedule);
db.SaveChanges();
return RedirectToAction("Index");
}
......
}
I now realize I need to create a view model that will encompass both my schedule class and an array of strings. I will create the schedule and then iterate through the array of strings and create TimeofDay objects
for inputs to bind with times collection ur form fields need to have indexed names like
Times[0].TimeID // this would probably be hidden field
Times[0].Time
Times[1].TimeID
Times[1].Time
.
.
Times[n].TimeID
Times[n].Time
when you do this there might be other issues when deleting rows from the middle. there are lot of blog posts on this out there. Phil haack's post is my favorite as it explains in very simple way how can you have non sequential indices for list binding. For more links please look at my answer to this question
I attempted to create a view model to support this by extending my model as follows:
public class ScheduleCreate
{
public Schedule schedule {get;set;}
public string[] schedTimes {get;set}
}
I then modified my view by changing:
#model scheduler.Models.Schedule
to:
#model scheduler.Models.ScheduleCreate
I additionally changed all of the model.(parameter) to model.schedule.(parameter)
Then I changed my controller to:
public ActionResult Create(ScheduleCreate mod)
{
if (ModelState.IsValid)
{
Schedule newschedule = db.Schedule.Add(mod.schedule);
if (mod.schedTime != null)
{
foreach (string instanceTime in mod.schedTimes)
{
newschedule.Times.Add(new {Time = DateTime.Parse(instanceTime) }); // unteseted
}
}
db.SaveChanges();
return RedirectToAction("Index");
}
PopulateDropDown();
return View(mod.schedule);
}

Can't serialize an object

I defined a model like this
public class Planilla
{
[Key]
public int IDPlanilla { get; set; }
[Required(ErrorMessage = "*")]
[Display(Name = "Dirección de Negocio")]
public int IDDireccionDeNegocio { get; set; }
[Required (ErrorMessage = "*")]
public string Nombre { get; set; }
[Display(Name = "Descripción")]
public string Descripcion { get; set; }
public bool Activo { get; set; }
[ScriptIgnore]
public virtual DireccionDeNegocio DireccionDeNegocio { get; set; }
}
And I have a method in my controller that returns the first element of this model
[HttpPost]
public ActionResult GetElements(string IDCampana)
{
Planilla query = db.Planillas.First();
return Json(query);
}
My problem is when I invoke this method from client side throws an error that say's
circular reference is detected trying to serialize
System.Data.Entity.DynamicProxies.Planilla_7F7D4D6D9AD7AEDCC59865F32D5D02B4023989FC7178D7698895D2CA59F26FEE
Debugging my code I realized that the object returned by the execution
of the methodFirstit's a
{System.Data.Entity.DynamicProxies.Planilla_7F7D4D6D9AD7AEDCC59865F32D5D02B4023989FC7178D7698895D2CA59F26FEE}
instead a Model of my namespace like
Example.Models.DireccionDeNegocio`.
Why am I doing wrong?? Because I tried with other models and work's well
Use view models, that's the only advice I can give you. Never pass domain models to your views. It's as simple as that. And if you respect this simple rule and fundamental rule in ASP.NET MVC applications you will never have problems. So for example if you need only the id and the description in your view:
[HttpPost]
public ActionResult GetElements(string IDCampana)
{
Planilla query = db.Planillas.First();
return Json(new
{
Id = query.IDPlanilla,
Description = query.Description
});
}
Notice that in this case the anonymous object serves as view model. But if you really wanted to do things properly you would write your view model:
public class PlanillaViewModel
{
public int Id { get; set; }
public string Description { get; set; }
}
and then:
[HttpPost]
public ActionResult GetElements(string IDCampana)
{
Planilla query = db.Planillas.First();
return Json(new PlanillaViewModel
{
Id = query.IDPlanilla,
Description = query.Description
});
}
By the way Ayende wrote a nice series of blog posts about this.
System.Data.Entity.DynamicProxies.* is the Entity Framework proxy namespace. Your DbContext creates your entities as such to support lazy loading and change tracking. This isn't your problem. The problem likely lies in a circular association.

Categories

Resources