Conditionally validate collection/list item properties based on parent model value - asp.net

I have the following view models
public class Step4ViewModel {
[Required(ErrorMessage="Yes/No Required")]
public bool? HaveVehicles { get; set; }
public List<Vehicle> Vehicles { get; set; }
public Step4ViewModel() {
this.Vehicles = new List<Vehicle>();
}
}
public class Vehicle {
public string Index { get; set; }
[DisplayName("Registration/Vin")]
[Required(ErrorMessage="Registration Required")]
[ValidVehicleRegistrationNumber(ErrorMessage = "Invalid Id Number")]
public string Registration { get; set; }
[Required(ErrorMessage="Make Required")]
public string Make { get; set; }
[Required(ErrorMessage="Model Required")]
public string Model { get; set; }
[Required(ErrorMessage="Year")]
public string Year { get; set; }
public Vehicle()
{
this.Index = Guid.NewGuid().ToString();
}
}
The validation works just fine. The problem is that it always works. I only want the validation to fire on vehicles in the collection when the user has indicated that they have vehicles. If HaveVehicles equals false I do not want the validation to fire.
Normally for this sort of validation I would build a custom validator in which I would only return validation errors if HaveVehicles is true. I am unable to do this here because HaveVehicles resides in a parent model which is inaccesible from the within the vehicle object.
One approach I've though of that may work is using a partial view to add/remove the collection from the DOM as null collections aren't validated. If the user selects yes ajax loads a partial view which contains a for loop that iterates over each vehicle and calls another partial view to display it.
Is there a better approach to do this or is something similar to the above my only option?

Related

ASP.NET MVC Auto generate integer number

Good day, a really newbie developer here.
I Have a form and it have a entity of "QueueNumber" Can someone show me how to code so that when ever i save my form it generates automatically QueueNumber + the Prefix, btw my prefix entity is in another class
public class Queue
{
public int QueueId { get; set; }
[Required]
public string Name { get; set; }
public string QueueNumber
public int ServiceId { get; set; }
public Service Service { get; set; }
}
-
public class Service
{
public int ServiceId { get; set; }
[Display(Name = "Service Name")]
public string ServiceName { get; set; }
[Display(Name = "Service Letter")]
public string ServiceLetter { get; set; }
[Display(Name = "Status")]
public bool? Status { get; set; }
[Display(Name = "Assigned Location")]
public int? LocationId { get; set; }
public virtual Location Location { get; set; }
public virtual ICollection<Customer> Customer { get; set; }
}
Outcome in database :
1. A001
2. A002
3. A003
i just want to be able to generate a queue number automatically and when i save in data base its like A= Service Letter and 001=QueueNumber. Thankyou
If the QueueNumber needs to be persisted to the table, then I would set it up as a calculated column so that the database can manage computing it and updating it if the underlying fields change.
If it is just something that you want to represent in the UI then I would recommend having the view model calculate this.
The entity can calculate something like this with a [NotMapped] attribute. For example:
public class Queue
{
public int QueueId { get; set; }
[Required]
public string Name { get; set; }
[NotMapped]
public string QueueNumber
{
get { return string.Format("{0}{1:000}", Service?.ServiceLetter ?? "?", QueueId);
}
[ForeignKey("Service")]
public int ServiceId { get; set; }
public Service Service { get; set; }
}
The problem with this approach is that to be able to rely on your Queue to reveal a QueueNumber, the Queue must eager load the Service, or you enable lazy loading and risk that performance hit vs. having Service == #null and getting an exception or invalid QueueNumber result. In the above example, if the Service isn't eager loaded you will get back something like "?001".
I prefer to use ViewModels for a number of reasons including performance, security, and handling conditions like this more cleanly.
For example, given a QueueViewModel as such:
[Serializable]
public sealed class QueueViewModel
{
public int QueueId{ get; set; }
public string Name { get; set; }
public string ServiceName { get; set; }
public string ServiceLetter { get; set; }
public string QueueNumber
{
return string.Format("{0}{1:000}", ServiceLetter, QueueId);
}
}
Then when reading the data, we don't pass Entities to the view, we pass our view model...
var viewModel = context.Queues
.Where(x => x.QueueId == queueId)
.Select(x => new QueueViewModel
{
QueueId = x.QueueId,
Name = x.Name,
ServiceName = x.Service.Name,
ServiceLetter = x.Service.ServiceLetter
}).Single();
return viewModel;
The benefits of this approach:
We don't have to worry about eager/lazy loading. The query fetches everything needed, and our view model can compute anything needed from the data loaded. (Queries can compute values as well if you like, but be wary of limitations in that the query has to be able to go to SQL, so no user functions, etc.)
Performance is improved since the query only returns the data needed rather than entire entity graphs, and no rish of lazy load hits.
Security is improved, we expose no more data to the client than is expected/needed, and we don't open the door for "lazy" updates where entities are attached to a context and saved without proper validation.

ASP.NET MVC 5 get 3 models data in 1 view

I am trying to do small application in mvc 5. In this I have 3 models say Organisation model, Product model and Package model.
Organisation model:
namespace Own.Models
{
public class OrganisationViewModel
{
[Key]
public int OrganisationID { get; set; }
[Required(ErrorMessage = "Please enter Organisation name")]
public string OrganisationName { get; set; }
[Required(ErrorMessage = "Please enter organisation address")]
public string OrganisationAddress { get; set; }
Package Model:
namespace Own.Models
{
public class PackageViewModel
{
[Key]
public int PackageID { get; set; }
[Required(ErrorMessage = "Please enter packagename")]
public string Packagename { get; set; }
[Required(ErrorMessage = "Please enter packagedescription")]
public string PackageDescription { get; set; }
Product Model:
namespace Own.Models
{
public class ProductViewModel
{
[Key]
public int ProductID { get; set; }
[Required(ErrorMessage = "Please enter Product Name")]
public string ProductName { get; set; }
[Required(ErrorMessage = "Please enter Product Description")]
public string ProductDescription { get; set; }
Now, I need to create another model named Submission model. Here in this Submission view I want the three dropdown lists for Organisation, Product and Package. If I select Organisation dropdown by selecting item in that dropdown it should display Organisation ID, Organisation Name and Organisation Address (just it should display details, read only).
Similarly for Package and Product also it should display all details and after displaying full details in one view at bottom there should be button to save the details. Here I am not using any database. Only some static data. How to display all 3 models data in that final model view? and how to get that read only data when selecting the drop down.
You can create anonamous type to concat fields
obj =db.Org
.Where(...)
.ToList()
.Select(s => new
{ ID=s.ID,
Description = string.Format("{0}-- £{1}", s.Name,s.Description)
});
Viewbag.OrgDdl=new SelectList(obj,"ID","Description");
In you view, you can use it like this
#Html.DropDownListFor(m1 => m1.ID, ViewBag.OrgDdl as IEnumerable<SelectListItem>)
Hope this helps
The best approach to this issue is creating a View Model consisting three mentioned objects as properties. Something like:
public class MyNewViewModel
{
public OrganisationViewModel MyOrganisation { get; set; }
public PackageViewModel MyPackage { get; set; }
public ProductViewModel MyProduct { get; set; }
}
Then you could populate your properties inside MyNewViewModel constructor at first or you can populte them seperatly during any action or ajax callback. So you will have your whole related data on view.

ASP MVC Manipulate model data based on database rows for a form in the view

I'm quite stuck with ASP MVC Model's, Now I understand how to create a simple model so I could set some values like this:
namespace build_01.Models
{
public class NewBooking
{
public int bookingID { get; set; }
public string bookingName { get; set; }
}
}
Now what I'm trying to do is have a Model that has all the bookingNames for example such as:
namespace build_01.Models
{
public class BookingNames
{
public string administrator { get; set; }
public string normal { get; set; }
public string user { get; set; }
}
}
However, what I would like to do is get this data from a database, for example I could have adminsistrator, normal, user, superuser, banned or whatever I like but the idea is that this information in the database can be changed. The bookingNames can be added, edited or deleted.
So let's say I just added superuser and banned to the database my model would now look like this for when I send it to the view and submit a form from the view to send to the HTTPPOST controller:
namespace build_01.Models
{
public class BookingNames
{
public string administrator { get; set; }
public string normal { get; set; }
public string user { get; set; }
public string superuser { get; set; }
public string banned { get; set; }
}
}
In my view I'm basically making checkboxes based on this information however I need to be able to set and get the values in a model so I can access them in my HTTPOST controller.
Now i've had a look at the Entity Framework but that doesn't seem to be what I want.
Is it possible to be able to create a model with the actual rows or even a column from a row in order to send it to the view to create a form for those columns so that I can return the values to the HTTPPOST in order to input data into a row in a table?
If so how can I approach this?

asp.net MVC ViewModel construction

I'm tying myself in knots - and thought it best to take a big step back, and back to basics.
I understand I should be using a ViewModel, so that is what I'm trying to contruct.
My demo app will have 4 sections (4 different parts of a form to complete):
Get Date/Number of days from user
Use that data to query the database, and return a list of qualifying records - each of these will have a unique ID of TypeID - and for each of these, they should also have 2 dynamic DropDownLists associated with them (so that whatever is selected in ListBox3 for each of the lists, corresponds to the TypeID3 (and whatever ID that has)
user will then be able to select Extras, again from a drop down list populated dynamically
users Name/Add/Tel will be collected
My "View" of what the ViewModel needs to look like/hold is:
I beleive my viewModel should look something like this:
public class SearchViewModel
{
public DateTime Date{ get; set; }
public int Days { get; set; }
public IQueryable<TypeID> TypeIDs { get; set; }
public IQueryable<LB1Item> LB1Items { get; set; }
public IQueryable<LB2Item> LB2Items { get; set; }
public IQueryable<Extras> Extras { get; set; }
public string Name { get; set; }
public string Add { get; set; }
public string Tel { get; set; }
public string email { get; set; }
}
First of all - is this how you would construct a ViewModel for what I've described above? I'm not certain of the DropDown Boxes, as for each form, there could be 1, 2, 3....10, 11, 12 for each TypeID retrieved - based on the Date selected.
Each of the drop down boxes for LB1Item and LB2Item - need to have their selected values stored against the TypeID for each line also.
This is what I think the class should look like for 1 drop down:
public class LB1Item
{
public String TypeName { get; set; }
public long TypeID { get; set; }
public int NumSelected { get; set; }
public int TypeCount { get; set; }
public IEnumerable<SelectListItem> CarsAvail
{
get
{
return new SelectList(
Enumerable.Range(0, TypeCount+1)
.OrderBy(typecount => typecount)
.Select(typecount => new SelectListItem
{
Value = typecount.ToString(),
Text = typecount.ToString()
}
), "Value", "Text");
}
}
}
Does that look ok also? Or am I overcomplicating what I'm trying to achieve?
I'd also like, after POSTing back the data after each stage (1, 2, 3, 4) to be actively populating the ViewModel with the selected values - and passing it back down to the view, so that I can retrieve it for the next Step.
What I want to end up with is something like this:
Date: 01/09/2012
Days: 4
{ List:
TypeID: 3059 ListBox1: 2 ListBox2: 8748,
TypeID: 2167 ListBox1: 7 ListBox2: 2378,
TypeID: 4983 ListBox1: 4 ListBox2: 5873
}
{List:
ExtraID: 4324,
ExtraID: 3878,
ExtraID: 4872,
ExtraID: 7698,
ExtraID: 2873
}
Name: Mark
Add: My town
Tel: 0912378
Email: me#me.com
Thanks for any help/pointers/samples...
Mark
For this type of solution I would separate out each section you want to render as individual views and use Ajax calls using Jquery ajax method. I would also use KnockoutJS to handle the views in your client. So you will essentially have two ViewModels, one in JavaScript in the client and one in MVC for returning the pieces you need as JSON for the Ajax calls from the client. Section 1 of your view is entered by the user into the client so you do not need it on the Controller side. Section 1 is basically the data used to query for Section 2. No need to make your collections IQueryable either since you will not being querying the lists that are returned. Your ViewModel on the Controller side might look something like this:
public class Section2
{
public List<TypeID> TypeIDs { get; set; }
public List<LB1Item> LB1Items { get; set; }
public List<LB2Item> LB2Items { get; set; }
}
public class Section3
{
public List<Extras> Extras { get; set; }
}
public class Section4
{
public string Name { get; set; }
public string Add { get; set; }
public string Tel { get; set; }
public string email { get; set; }
}
So the steps that would be taken are that when an event is thrown that the date and days have been entered by the user an Ajax call is made back to a controller with entered data and days to query for the information that will populate Section 2. The controller returns the Section2 ViewModel as JSON and it is rendered in the HTML using Knockout. Then when the user selects from the lists in Section 2 an event is thrown to query the controller again to return the information needed to populate Section 3, and the cycle is repeated.
There is an excellent example on using Knockout to do exactly what you are trying to do here.

Validating uniqueness with data annotations in asp.net mvc

I have various questions about validation using data annotations. I am using the following setup
asp.net mvc 2
entity framework 4
data annotations
Basically, I'm trying to get unique validation working and i'm a little confused. My models are as follows:
public class Buyer
{
public int Id { get; set; }
[Required(ErrorMessage = "The email is required")]
public string Email { get; set; }
[Required(ErrorMessage= "The name is required")]
public string Name { get; set; }
}
public class Seller
{
public int Id { get; set; }
[Required(ErrorMessage = "The email is required")]
public string Email { get; set; }
[Required(ErrorMessage= "The name is required")]
public string Name { get; set; }
}
I have set up a unique field attribute as follows
public class UniqueFieldAttribute : ValidationAttribute
{
public IUniqueValidator Validator { get; set; }
public int Id { get; set; }
public override bool IsValid(object value)
{
if (value == null)
{
return true;
}
return Validator.IsValid(Convert.ToString(value), Id);
}
}
I then created a validator that implements the IUniqueValidator interface:
public class BuyerUniqueEmailValidator : IUniqueValidator
{
public bool IsValid(string value, int id)
{
TheDb db = new TheDb();
var existing = from Buyer b in db.Buyers
where b.Email.ToLower() == value.ToLower()
select b;
foreach (Buyer b in existing)
{
if (b.Id != id)
{
return false;
}
}
return true;
}
}
The idea is there! However, on execution I am having problems. When I add this
[UniqueField(Validator=new BuyerUniqueEmailValidator(), Id=this.Id ErrorMessage= "This email is in use")]
the project won't compile.
Basically, what I want to know is if it is possible to pass a class to the validationAttribute to perform the validation? Also, how can i pass an id.
Additionally, is there anyway to create a generic unique field generator that would work for all my models that have an email field, or do I have to have a BuyerEmailValidator, a SellerEmailValidator etc, etc. I can't seem to get T working correctly.
Only worried about serverside at the moment.
Thanks
Your code implementation of this can't see any records not visible to its own transaction, and, hence, can't work reliably in a multi-user environment. Why not use a DB constraint?

Resources