I have picked LINQ to SQL as ORM framework for ASP .NET MVC3 project. Everything was good before I was faced with need to put additional field 'Confirm Password' to registration form. As it was mentioned in one question on SO (unfortunately I can't find it at the moment), it's better to use interface to extend generated LINQ to SQL classes with validation attributes, instead of having another class for storing validation attributes. So here we go:
public interface IRegTry
{
[Required]
[Email]
string EMail { get; set; }
[Required]
[StringLength(100, ErrorMessage = "Should not exceed 100 symbols")]
string FirstName { get; set; }
[Required]
string Password { get; set; }
}
[MetadataType(typeof(IRegTry))]
public partial class RegTry : IRegTry { }
RegTry class is generated class by LINQ to SQL based on database entity.
On the View we have confirm password field, which should make sure that two typed password equals to each other.
So here we adding it:
public class RegTryViewModel : RegTry
{
[Required]
[EqualTo("Password", ErrorMessage = "You should type two identical passwords to continue")]
public string ConfirmPassword { get; set; }
}
View is strongly typed view with RegTryViewModel model.
I just ask here to make sure I'm doing everything right. The thing that makes me feel uncomfortable is that I spread validation logic between IRegTry interface and the RegTryViewModel class. But I can't add ConfirmPassword property to IRegTry interface because base SQL to LINQ class doesn't has it at all.
Thanks in advance guys!
I know that you already accepted an answer for this but I think it may be better to set this up using partial classes. As long as you set up the partial class in the same namespace with the same name, everything will get set up automatically. Here is an example of how I set this up in one of my projects:
namespace OperationsMetrics
{
[MetadataType(typeof(ClientStatMD))]
public partial class client_wkly_stat : IValidatableObject
{
public class ClientStatMD
{
[Required(ErrorMessage = "Client selection is required")]
public virtual int client_id { get; set; }
[Required(ErrorMessage = "SLAs met is required")]
public virtual int wkly_sla_met { get; set; }
[Required(ErrorMessage = "Total SLAs possible is required")]
public virtual int wkly_sla_req { get; set; }
[Required(ErrorMessage = "Number of input files is received")]
public virtual int num_inp_files_rec { get; set; }
[Required]
public string client_name { get; set; }
}
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (wkly_sla_met > wkly_sla_req)
{
yield return new ValidationResult("SLAs met cannot be greater that SLAs possible");
}
}
public string client_name { get; set; } //this isn't a part of the actual db object but can still be accessed in the Validate method
}
}
You can set up the Partial Class as an IValidatableObject which implements its own Validate method. You can have a check for Confirm==Password in your Validate method.
You can get some more information in this Pluralsight Video
If you are using View Model classes, you don't need validation logic connected to your DAL Model classes, so you shouldn't need that validation interface linked to the DAL Model class.
Related
I have a model that contains basic information. However, my View requires more information just for display so I think that a ViewModel is needed here to display that extra information. However, should I add the Validation attributes in the model so that when I perform Code-First migration, it automatically creates the database for me with the correct datatype of each columns or should I add the Validation attributes to the ViewModel since the form should validate the filled information?
public class Module
{
[Key]
public int id { get; set; }
[Required]
[StringLength(100)]
[Column(TypeName = "varchar")]
[Display(Name="Module Name")]
public string ModuleName { get; set; }
}
public class ModuleViewModel
{
[Key]
public int id { get; set; }
[Required]
[StringLength(30)]
[Column(TypeName="varchar")]
[Display(Name="Module ID")]
public string ModuleID { get; set; }
[Required]
[StringLength(100)]
[Column(TypeName = "varchar")]
[Display(Name="Module Name")]
public string ModuleName { get; set; }
//To populate dropdownlist
public List<SelectListItem> ModuleLevelList { get; set; }
}
Do I really need a ViewModel in this case?
Data Annotation attributes for user input validation go on the ViewModel. Data Annotations for Entity Framework Code First go on the Model.
They are conceptually two different things, validation of input and database generation using EF Code First.
For example, Required and StringLength for Entity Framework Code First creates a database column of type varchar(length) NOT NULL. Required and StringLength on the ViewModel are used in Validation of user input. Do not conflate the two, there is nothing wrong with using StringLength(length) twice. Put the length value in a static Constant if you want the length expressed one place only.
I highly recommend you use a view model. You may think it is redundant right now but I guarantee you that it is very useful and down the road you will thank me. I've been burned many times in the past trying to just use a model object everywhere and relying heavily on data annotations like yourself. Plus, you don't have to litter your model layer with view layer garbage such as [Display(Name="Module Name")]
In your case, I suggest this:
public class Module
{
[Key]
public int id { get; set; }
[Required]
[StringLength(100)]
[Column(TypeName = "varchar")]
public string ModuleName { get; set; }
}
public class ModuleViewModel
{
public int id { get; set; }
[Required]
[StringLength(30)]
[Display(Name="Module ID")]
public string ModuleID { get; set; }
[Required]
[StringLength(100)]
[Display(Name="Module Name")]
public string ModuleName { get; set; }
//To populate dropdownlist
public List<SelectListItem> ModuleLevelList { get; set; }
}
Is there any way possible to generate the database object in model.edmx with DataAnnotations(like Required, StringLength)?
When you use database first approach with EF all your classes are generated as partial classes. Which means you can spilt the definition over two or more source files. If you make changes to the generate class it will be overwritten.
Let's say you have a partial class of User generated by EF as
public partial class User
{
public string Username { get; set; }
public string Telphone { get; set; }
}
Now to use DataAnnotations on the properties of User you would create a new source file with the same name in the same namespace and make use of MetadataType attribute as:
[MetadataType(typeof(UserMetaData))]
public partial class User
{
public string Username { get; set; }
public string Telphone { get; set; }
}
public class UserMetaData
{
[Required]
[Display(Name = "User name")]
public string Username { get; set; }
[DataType(DataType.PhoneNumber)]
[Display(Name = "Telephone number")]
public string Telephone { get; set; }
}
As you can see I've used MetadataType attribute in the second example which specifies the metadata class to associate with a data model class.
I have refactored an application which uses EF5 Database First. The application uses metadata classes to add data annotations to the entity properties. Before the refactor, these worked. They are mostly just display names and data formats.
Example:
[MetadataType(typeof(QueryDetailsResultMetadata))]
public partial class QueryDetailsResult : IPortfolio
{
public string Source { get { return "Local"; } }
}
public class QueryDetailsResultMetadata
{
//Fields from QueryDetailsResult requiring annotations
[Display(Name = "Company Name")]
public string SiteName { get; set; }
[Display(Name = "Contact Telephone Number")]
public string ContactTelNo { get; set; }
}
Before the refactor, the partial class did not inherit from an interface and it did not have the non mapped property. These changes are however required. Neither of these two should be causing a problem as both are well documented as valid solutions.
The interface looks like this
public interface IPortfolio
{
int Id { get; set; }
string SiteName { get; set; }
string YearOfManufacture { get; set; }
string Contact { get; set; }
string ContactTelNo { get; set; }
string Source { get;}
}
The display uses the properties like this
#Html.DisplayNameFor(model => model.Portfolio.ContactTelNo)
On the View at runtime, the property names are shown rather than the display names. Any ideas why? I can't see any reason for the annotations to be broken
//edit
I tried moving the annotations on to the new non-mapped fields in the partial and removed them from the metadata class. To seee if it had any effect. None. Also double checked the edmx is in the same Namespace as the partial class and metadata file which it is.
Any thoughts on what to check or try? Not having much success this end, most google results are just saying to use a metadata class which is already in place.
//2nd Edit
Moving annotations out of metadata class and on to the interface did the trick.
It is a little bit confusing how the DataAnnotation attributes are wroking with interfaces because:
at one hand classes don't inherit attributes from their interfaces: Is it possible to use DataAnnotations with Interfaces?
the the other hand you can/need put the attributes on the interfaces: ASP.NET MVC DisplayAttribute and interfaces
But how it works only depends on the type of your "container" in the view so lets consider the following types:
public class QueryDetailsResult : IPortfolio
{
public string SiteName { get; set; }
}
public interface IPortfolio
{
string SiteName { get; set; }
}
So if you have #Html.DisplayNameFor(model => model.Portfolio.SiteName) in your view
and your model class looks like
public class Model {
public QueryDetailsResult Portfolio { get; set; }
}
then you need to put the DisplayAttribute on the SiteName property of your QueryDetailsResult class because MVC looks for the "container" type in the model.Portfolio.SiteName expression which is QueryDetailsResult
but if you have your model class defined as
public class Model {
public IPortfolio Portfolio { get; set; }
}
then you need to put it on the SiteName property of the IPortfolio interface because your "container" type is the IPortfolio interface.
I'm trying to cast an IEnumerable of an inherited type to IEnumerable of base class.
Have tried following:
var test = resultFromDb.Cast<BookedResource>();
return test.ToList();
But getting error:
You cannot convert these types. Linq to Entities only supports conversion primitive EDM-types.
The classes involved look like this:
public partial class HistoryBookedResource : BookedResource
{
}
public partial class HistoryBookedResource
{
public int ResourceId { get; set; }
public string DateFrom { get; set; }
public string TimeFrom { get; set; }
public string TimeTo { get; set; }
}
public partial class BookedResource
{
public int ResourceId { get; set; }
public string DateFrom { get; set; }
public string TimeFrom { get; set; }
public string TimeTo { get; set; }
}
[MetadataType(typeof(BookedResourceMetaData))]
public partial class BookedResource
{
}
public class BookedResourceMetaData
{
[Required(ErrorMessage = "Resource id is Required")]
[Range(0, int.MaxValue, ErrorMessage = "Resource id is must be an number")]
public object ResourceId { get; set; }
[Required(ErrorMessage = "Date is Required")]
public object DateFrom { get; set; }
[Required(ErrorMessage = "Time From is Required")]
public object TimeFrom { get; set; }
[Required(ErrorMessage = "Time to is Required")]
public object TimeTo { get; set; }
}
The problem I'm trying to solve is to get records from table HistoryBookedResource and have the result in an IEnumerable<BookedResource> using Entity Framework and LINQ.
UPDATE:
When using the following the cast seams to work but when trying to loop with a foreach the data is lost.
resultFromDb.ToList() as IEnumerable<BookedResource>;
UPDATE 2:
Im using entity frameworks generated model, model (edmx) is created from database, edmx include classes that reprecent the database tables.
In database i have a history table for old BookedResource and it can happen that the user want to look at these and to get the old data from the database entity framework uses classes with the same name as the tables to receive data from db. So i receive the data from table HistoryBookedResource in HistoryBookedResource class.
Because entity framework generate the partial classes with the properties i dont know if i can make them virtual and override.
Any suggestions will be greatly appreciated.
Typically you use AsEnumerable<T> in such cases:
var test = resultFromDb.AsEnumerable().Cast<BookedResource>();
This has the advantage of not enumerating the whole result at once (you don't loose the laziness).
try with:
resultFromDb.AsEnumerable().Cast<BookedResource>();
I'm developing a complex application with about 90 different forms (yeah, awesome). How do I go about doing complex field validation based on a few requirements:
1) field requirements are based on which user is logged in (role)
2) field requirements change if other data fields are answered differently (dynamic)
how is this accomplished in MVC4 using EF5 POCO's?
I currently have created data annotations for required fields like so:
My EF5 POCO Model:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
[MetadataType(typeof(User_Validation))]
public partial class User
{
public int UserID { get; set; }
public string UserName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Password { get; set; }
public string Email { get; set; }
}
ValidationModels.cs file located with my EF5 POCO's:
public class User_Validation
{
public int UserID { get; set; }
[Required(ErrorMessage = "The UserName is required")]
public string UserName { get; set; }
[Required(ErrorMessage = "The FirstName is required")]
public string FirstName { get; set; }
[Required(ErrorMessage = "The LastName is required")]
[Display(Name="Last Name")]
public string LastName { get; set; }
[Required(ErrorMessage = "The Password is required")]
[DataType(DataType.Password)]
public string Password { get; set; }
[Required(ErrorMessage = "The Email is required")]
[DataType(DataType.EmailAddress)]
public string Email { get; set; }
}
This works fine and dandy, but how do I make my validations dynamic??
Thanks!
Do you need the admin to set the dynamic requirements for the responses or will they be fairly static from the outset?
If I was you I would create different action methods for each triggered response. This would allow you to specify the forms as partial views and render them based on the input logic.
If you need custom field validation it is recommended to write your own data validation framework. You can inherit from the ActionFilterAttribute which allows you to add custom validation before and after each action request/response cycle. Look here for some info Custom Filters in MVC
Additionally I would introduce a custom jquery validation framework for client side validation so that there is no multiple postbacks for the same form and then do you custom server side validation before generating the next form dynamically. For info an a framework look at this blog: Jquery - Custom Validation
Keep in mind that you don't want a lot of chatting between your data store and view, so I would store as much of your configuration in a well thought out cache implementation.
I hope this helps.