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; }
}
Related
There are loads of resources for this on Google but I can't fully understand what I need to do in my scenario:
I have this class:
public class CompanyLanguage : EntityBase
{
public int CompanyId { get; set; }
public int LanguageId { get; set; }
public bool IsDefault { get; set; }
public virtual Company Company { get; set; }
public virtual Language Language { get; set; }
}
Language is defined as:
public class Language:EntityBase
{
[Required]
[DisplayName("Language Code")]
public string LanguageCode { get; set; }
[Required]
[MaxLength(2, ErrorMessage ="2 characters maximum")]
[DisplayName("2 Char Language Code")]
public string LanguageCode2Char { get; set; }
[Required]
[DisplayName("Language Name")]
public string LanguageName { get; set; }
public virtual List<LabelLanguage> LabelLanguages { get; set; }
}
Running a Fortify Scan returns the issue below as a high priority:
(ASP.NET MVC Bad Practices: Optional Submodel With Required Property)
We can't run the fortify scan - it's being run by someone else, so I need to get the changes right so it doesn't come straight back.
All the resources I've looked at suggest that underposting attacks could be made - i.e. a null Language, even though Language has some required properties.
For me, this is a valid scenario - the required properties of Language are only required if Language isn't null.
So what am I supposed to do to resolve this? Do I make public int LanguageId { get; set; } required, or public virtual Language Language { get; set; } or both?
Or am I completely wrong an I have to do something else? As I say I can't test these as the software has to be sent away for the test or I'd be trying all sorts out.
To summarize our discussion from comments.
Create a view model which models only the information that is needed to satisfy the corresponding view.
populate view models in your controller action from your domain ef models
either project directly into view models using linq queries or Automapper.
Example view model for your question
public class CompanyLanguageEditViewModel
{
[DisplayName("Company")]
[Required]
public int CompanyId { get; set; }
[DisplayName("Language")]
[Required]
public int LanguageId { get; set; }
public bool IsDefault { get; set; }
public IEnumerable<SelectListItem> Companies{ get; set; }
public IEnumerable<SelectListItem> Languages { get; set; }
}
And in your view you can then use
#Html.DropDownListFor(x => x.CompanyId, Model.Companies);
and your label will be Country and you are only going to POST back what you need
I am working on an ASP.NET application split in to 4 layers.
Business Layer
Data Access Layer
WCF Services Client
ASP.NET MVC5 application
I am using AutoMapper to map from domain class to business class and other way around. I am using Data Annotation:
[StringLength(250)]
[Required(ErrorMessage = "Required Title")]
[Display(Name = "Function Title")]
Which is translated in view ASP.NET Razor page, i.e. creating record by passing strongly typed and validate against that. My question is how I can achieve this where I am passing business class to view as strongly typed and I don't want data dependency on business layer nor ASP.Net MVC application?
DAL Entity
[Table("Navigation_Functions")]
public class Navigation_FunctionsEntity
{
public Navigation_FunctionsEntity()
{
}
[Key]
public int Function_ID { get; set; }
[StringLength(250)]
[Required(ErrorMessage = "Required Title")]
[Display(Name = "Function Title")]
public string FunctionName { get; set; }
[Required(ErrorMessage = "Required Hierarchy Level")]
[Display(Name = "Hierarchy Level")]
public int Hierarchy_Level { get; set; }
public ICollection<Navigation_FunctionHierarchy> Navigation_FunctionHierarchy { get; set; }
public ICollection<Navigation_FunctionInAction> Navigation_FunctionInAction { get; set; }
public ICollection<Navigation_FunctionInController> Navigation_FunctionInController { get; set; }
public ICollection<Navigation_FunctionController> Navigation_FunctionController { get; set; }
}
Business Entity
public class Navigation_Functions
{
public int Function_ID { get; set; }
public string FunctionName { get; set; }
public int Hierarchy_Level { get; set; }
public ICollection<Navigation_FunctionHierarchy> Navigation_FunctionHierarchy { get; set; }
public ICollection<Navigation_FunctionInAction> Navigation_FunctionInAction { get; set; }
public ICollection<Navigation_FunctionInController> Navigation_FunctionInController { get; set; }
public ICollection<Navigation_FunctionController> Navigation_FunctionController { get; set; }
}
My advice is don't organize your code into layers like this. It's a lot of hoops but there's no real advantage. In fact, I found over time that a layered architecture like this actually hinders refactoring and evolution of your codebase. Layers are for cake, not software.
https://vimeo.com/131633177
I also question the value of WCF in your picture too, I don't know what it adds to the solution here except adding latency and making your requests slower.
Not sure what happened but I am getting the following error while attempting to pull up any view in my web app. The code is auto generated by visual studio and I am not getting any errors before building. Using ASP.Net MVC 6, EF7.
An exception of type 'System.InvalidOperationException' occurred in EntityFramework.Core.dll but was not handled in user code
Additional information: The entity type 'Microsoft.AspNet.Mvc.Rendering.SelectListGroup' requires a key to be defined.
Here is the line the code is erroring out on.
public IActionResult Index()
{
var schoolContext = _context.Schools
.Include(s => s.District)
.Include(s => s.Location)
.Include(s => s.Tier);
return View(schoolContext.ToList());
}
After some searching I can't figure out exactly what I need to fix. This was working at one point. Not sure what changed.
The view does have a defenition
#model IEnumerable<School>
As requested here is the School model
public class School
{
//Original Fields
public int SchoolId { get; set; }
[Display(Name = "Name")]
public string SchoolName { get; set; }
[Display(Name = "Date Added")]
public DateTime SchoolDateAdded { get; set; }
[Display(Name = "Last Update")]
public DateTime SchoolLastUpdate { get; set; }
[Display(Name="Updated By")]
public string SchoolUpdatedBy { get; set; }
//Referance Fields
public int DistrictId { get; set; }
public IEnumerable<SelectListItem> DistrictList { get; set; }
public int LocationId { get; set; }
public IEnumerable<SelectListItem> LocationList { get; set; }
public int TierId { get; set; }
public IEnumerable<SelectListItem> TierList { get; set; }
//Navigation Property
public District District { get; set; }
public Location Location { get; set; }
public Tier Tier { get; set; }
}
These IEnumerable<SelectListItem>s should not be part of your EF model. Remember the single responsibility principle. Keep any UI framework away from your DAL implementation. Use a view model representing a School.
As for the error, from EF's point of view, School has a 1-n association with SelectListItem, so it tries to make it part of its mapping schema. But each mapped type needs a primary key, which of course isn't mapped, and EF can't infer any.
A quick, but dirty, fix would be to exclude the properties from being mapped by the [NotMapped] attribute, but a better segregation of your code is the real remedy.
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 busy creating my first EF code first model and I've come across a slightly confusing problem.
I have a number of model classes that each inherit from a base model class that has three common properties I want used in all model classes. These properties are Id, LastUpdated and LastUpdatedBy.
Id is the primary key of each model class.
LastUpdated is a foreign key to my 'User' model class.
LastUpdatedBy is a datetime field that indicates the last time the record was modified.
So what I'd like to setup is the 1 to 1 foreign key relationship from my base class to my 'User' model class but I'm receiving the exception:
Multiplicity is not valid in Role 'Profile_LastUpdatedByUser_Source'
in relationship 'Profile_LastUpdatedByUser'. Because the Dependent
Role properties are not the key properties, the upper bound of the
multiplicity of the Dependent Role must be '*'
This is my ModelBase class:
public class ModelBase
{
public int Id { get; set; }
public DateTime LastUpdated { get; set; }
[ForeignKey("LastUpdatedByUser")]
[Required]
public int LastUpdatedByUserId { get; set; }
public virtual User LastUpdatedByUser { get; set; }
}
This is one of my model classes:
public class Profile : ModelBase
{
[StringLength(25, MinimumLength=1)]
[Required(ErrorMessage="First Name is Required")]
public string FirstName { get; set; }
[StringLength(25, MinimumLength = 1)]
[Required(ErrorMessage = "Last Name is Required")]
public string LastName { get; set; }
[StringLength(25, MinimumLength = 1)]
[Required(ErrorMessage = "Email Address is Required")]
public string Email { get; set; }
public string Mobile { get; set; }
public string HomePhone { get; set; }
public string WorkPhone { get; set; }
public string ImageSource { get; set; }
public Squable.Model.Enums.MembershipType.MembershipTypeEnum MembershipType { get; set; }
}
This is my user class (Please ignore the Password property, I'll fix that later ;) ):
public class User : ModelBase
{
public string UserName { get; set; }
public string Password { get; set; }
public int ProfileId { get; set; }
public virtual Profile Profile { get; set; }
}
I don't know if what I am doing is best practise but I could do with some advice as to how to either fix the problem or maybe just some pointers in the right direction.
Move
public int Id { get; set; }
to User class and to Profile you can also change the names to UserId and to ProfileId and move
public virtual User LastUpdatedByUser { get; set; }
to Profile class.
I have a bad experience with sharing Id in base entity If you are planning to use Repository and UnitOfWork pattern you will get a lot of problems later. Check your current database structure and tables with SQL Server Management Studio.
More Info TPH