I am using MVC database first approach and created one .edmx file.
Now my all the tables are available in this model.tt file.
I have also defined some dataannotation on those fields of table.
But What I have noticed that when ever I tried to update this model then value of dataanotation will be lapse.
Any thoughts please.
Yes it's kinda funny how much you read about annotations but you can't actually use them because they get overwritten.
This is what I found to help me
http://www.ozkary.com/2015/01/add-data-annotations-to-entity.html
also this
https://learn.microsoft.com/en-us/previous-versions/aspnet/ee256141(v=vs.98)
I don't pretend to understand it but here's a join the dots guide.
An example generated EF class looks like this:
public partial class Employee
{
public int Emp_ID { get; set; }
public string Emp_Name { get; set; }
public Nullable<System.DateTime> Commencement_Date { get; set; }
}
Definitely do not edit this.
Instead you create a seperate class file (I called mine metadata.cs and put it in the Models folder) with this in it:
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel;
namespace MyProject.Models
{
[MetadataType(typeof(EmployeeMD))] // new name for your metadata class
public partial class Employee // same name as your EF generated class
{
// Nothing in here
}
internal sealed class EmployeeMD // your metadata class
{
[Required]
[StringLength(50, MinimumLength = 2, ErrorMessage = "Name required")]
public string Emp_Name { get; set; }
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public Nullable<System.DateTime> Commencement_Date { get; set; }
}
}
Related
I have the following problem: it turns out that when I write the controller of my article table from my database, I get an error on my web page. I'm using ASP.NET Core 5 with a pre-existing database. I'm a newbie at this, I'm learning by creating projects.
In my controller class I have this:
https://codeshare.io/3AoPBB
I have this class articlesviewmodel:
https://codeshare.io/mp08vk
And this is my articlemap:
https://codeshare.io/0gQqZv
DbContextSytem: https://codeshare.io/1YD6Bj
Article table in SQL Server database:
https://codeshare.io/DZrPJk
Class Category for a table in SQL Server:
public class Category
{
public int idcategory { get; set; }
[Required]
[StringLength(50, MinimumLength = 3, ErrorMessage = "The Category must not have more than 50 characters")]
public string namecategory { get; set; }
public string descategory { get; set; }
public bool numberstatate { get; set; }
// modify table category
public ICollection<Article> articles { get; set; }
}
I really don't know what I can be doing wrong in the include
I solved it in the following way, it turns out that I got an error because the foreign key was missing between the category and articles tables, I solved it with the following line of code in the dbcontextsystem under modelBuilder.ApplyConfiguration(new ArticleMap());
modelBuilder.Entity<Article>().HasOne(a => a.Category).WithMany(c => c.articles).HasForeignKey(a => a.idcategory);
Public class UserMetdata
{
[Required]
[Display(Name="User ID")]
public int UserID { get; set; }
[Display(Name="User Name")]
public string UserName { get; set; }
}
I dont want to UserName to be shown in View. Its similar like creating not required Annotation. One solution is by deleting UserName form Class but i dont want that.
How can it be done using Data Annotation.
You could use ScaffoldColumnAttribute for that property
[ScaffoldColumn(false)]
public string UserName { get; set; }
This will work only when you let framework dynamically generate your views by calling #Html.DisplayForModel() or like, and you DO NOT have defined display template for that model at Views/Shared/DisplayTemplates or Views/ControllerName/DisplayTemplates. Otherwise, you should edit that display template and remove corresponding line from it
If model-first, we use [MetadataType(typeof(ConceptMetadataSource))] to attach a MetadataSource file which contains all the data annotations like [HiddenInput(DisplayValue = false)] or [Display(Name = "Title")].
For example:
[MetadataType(typeof(ConceptMetadataSource))]
public partial class Concept
...
Now, I am using database-first approach since there is an existing database. This time, the entity classes are automatically created by edmx model. At the beginning of each entity class, there is lines of comment below:
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated from a template.
//
// Manual changes to this file may cause unexpected behavior in your application.
// Manual changes to this file will be overwritten if the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
Since the code will be regenerated once we modify a table in the database, the data annotations will be wiped out each time the entity classes are regenerated.
Can anyone tell me what is the best method to annotate those entity classes? Thank you.
All you have to do is create another partial class and use metadatatype attribute. Here is the sample code
//This is generated by EDMX
namespace DataLayer
{
using System;
using System.Collections.Generic;
public partial class Customer
{
public Customer()
{
this.CustomerAddresses = new HashSet<CustomerAddress>();
this.CustomerOrders = new HashSet<CustomerOrder>();
}
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string EmailId { get; set; }
public Nullable<System.DateTime> DateOfBirth { get; set; }
public virtual ICollection<CustomerAddress> CustomerAddresses { get; set; }
public virtual ICollection<CustomerOrder> CustomerOrders { get; set; }
}
}
Add following code manually
namespace DataLayer
{
[MetadataType(typeof(CustomerMetaData))]
public partial class Customer
{
}
public class CustomerMetaData
{
[StringLength(10, ErrorMessage = "First name must be 25 characters or less in length.")]
[Required(ErrorMessage = "First name is required.")]
public String FirstName { get; set; }
}
}
Okay, here is the answer.
The trick is, the auto-generated classes are all partial classes. The compilation process will combine all partial classes with the same name.
If we have public partial class Concept generated by DbContext, all we need to do is to create another one started with public partial class Concept. This new partial class can be created in a different folder, but we need to its namespace should be updated into the same as the auto-generated partial class.
In this newly created partial class, we can add all kinds of data-annotations such as
[Required(ErrorMesssage="This Field is required")]
Or, we can even add new properties like
FullName {get {return string.Format("{0} {1}", FirstName, LastName);}}
If the model is updated from the database again, only the auto-generated partial classes will be updated. Those newly manually added partial classes, which contain our annotations and other manipulations will remain intact.
define a view model like
public class VMConcept
{
public Concept NewConcept {get; set;}
}
[MetadataType(typeof(ConceptMetadataSource))]
public partial class Concept{}
public class ConceptMetadataSource {
[Required(ErrorMesssage="This Field is required")]
public string PropertyName {get; set;}
}
I am applying validation using DataAnnotations to an MVC ViewModel which is a composite of several entity framework objects and some custom logic. The validation is already defined for the entity objects in interfaces, but how can I apply this validation to the ViewModel?
My initial idea was to combine the interfaces into one and apply the combined interface to the ViewModel, but this didn't work. Here's some sample code demonstrating what I mean:
// interfaces containing DataAnnotations implemented by entity framework classes
public interface IPerson
{
[Required]
[Display(Name = "First Name")]
string FirstName { get; set; }
[Required]
[Display(Name = "Last Name")]
string LastName { get; set; }
[Required]
int Age { get; set; }
}
public interface IAddress
{
[Required]
[Display(Name = "Street")]
string Street1 { get; set; }
[Display(Name = "")]
string Street2 { get; set; }
[Required]
string City { get; set; }
[Required]
string State { get; set; }
[Required]
string Country { get; set; }
}
// partial entity framework classes to specify interfaces
public partial class Person : IPerson {}
public partial class Address : IAddress {}
// combined interface
public interface IPersonViewModel : IPerson, IAddress {}
// ViewModel flattening a Person with Address for use in View
[MetadataType(typeof(IPersonViewModel))] // <--- This does not work.
public class PersonViewModel : IPersonViewModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
public string Street1 { get; set; }
public string Street2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Country { get; set; }
}
My real-world problem involves about 150 properties on the ViewModel, so it's not as trivial as the sample and retyping all the properties seems like a horrible violation of DRY.
Any ideas on how to accomplish this?
In order for this to work you will need to manually associate the interfaces as metadata for your concrete classes.
I expected to be able to add multiple MetadataType attributes but that is not permitted.
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] // Notice AllowMultiple
public sealed class MetadataTypeAttribute : Attribute
Therefore, this gives a compilation error:
[MetadataType(typeof(IPerson))]
[MetadataType(typeof(IAddress))] // <--- Duplicate 'MetadataType' attribute
public class PersonViewModel : IPersonViewModel
However, it works if you only have one interface. So my solution to this was to simply associate the interfaces using a AssociatedMetadataTypeTypeDescriptionProvider and wrap that in another attribute.
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class MetadataTypeBuddyAttribute : Attribute
{
public MetadataTypeBuddyAttribute(Type modelType, Type buddyType)
{
TypeDescriptor.AddProviderTransparent(
new AssociatedMetadataTypeTypeDescriptionProvider(
modelType,
buddyType
),
modelType);
}
}
In my situation (MVC4) the data annotation attributes on my interfaces already worked. This is because my models directly implement the interfaces instead of having multi-level inheritance. However custom validation attributes implemented at the interface level do not work.
Only when manually associating the interfaces all the custom validations work accordingly. If I understand your case correctly this is also a solution for your problem.
[MetadataTypeBuddy(typeof(PersonViewModel), typeof(IPerson))]
[MetadataTypeBuddy(typeof(PersonViewModel), typeof(IAddress))]
public class PersonViewModel : IPersonViewModel
based on answer here, I couldn't somehow make that MetadataTypeBuddy attribute works. I'm sure that we must set somewhere that MVC should be calling that attribute. I managed to get it work when I run that attribute manually in Application_Start() like this
new MetadataTypeBuddyAttribute(typeof(PersonViewModel), typeof(IPerson));
new MetadataTypeBuddyAttribute(typeof(PersonViewModel), typeof(IAddress));
The MetadataTypeBuddy attribute did not work for me.
BUT adding "new" MetadataTypeBuddyAttribute in the "Startup" did work BUT it can lead to complex code where the developer is not aware to add this in the "Startup" for any new classes.
NOTE: You only need to call the AddProviderTransparent once at the startup of the app per class.
Here is a thread safe way of adding multiple Metadata types for a class.
[AttributeUsage(AttributeTargets.Class)]
public class MetadataTypeMultiAttribute : Attribute
{
private static bool _added = false;
private static readonly object padlock = new object();
public MetadataTypeMultiAttribute(Type modelType, params Type[] metaDataTypes)
{
lock (padlock)
{
if (_added == false)
{
foreach (Type metaDataType in metaDataTypes)
{
System.ComponentModel.TypeDescriptor.AddProviderTransparent(
new AssociatedMetadataTypeTypeDescriptionProvider(
modelType,
metaDataType
),
modelType);
}
_added = true;
}
}
}
}
I have a Solution under the solution there is few Projects one of the called DomainModel,
in which i write my models and other stuff mainly infrastructure.
Now i have another project called WebUI in which i do my UI (Views, Controllers , etc...)
I want to use Remote attribute in DomainModel project which must implemented in WebUI certain view.
When i use it in DomainModel it's gives me an error, that it does not recognize the Controller and it's correct it does not recognize it because the if I add the reference of WebUI the Vs begin to swear at me because it will be a circular reference.
How to implement this?
this is my code
Controller that serves the RemoteValidation
[OutputCache(Location = OutputCacheLocation.None, NoStore = true)]
public class RemoteValidationController : Controller
{
public JsonResult CheckPassword(string SmsCode)
{
return Json(12345, JsonRequestBehavior.AllowGet);
}
}
//The real entity in DomainModel project
public class SmsCustomer
{
public int CustomerId { get; set; }
public string Cli { get; set; }
//this is what i have to validate on server
public virtual string SmsCode { get; set; }
public DateTime InsertDate { get; set; }
public int CustomerDaysChoiceId { get; set; }
public int CustomerAmountChoiceId { get; set; }
[Required(ErrorMessage = "error")]
[StringLength(128, ErrorMessage = "error")]
public string SelectedWords { get; set; }
public SmsCustomerDaysChoice CustomerDaysChoice { get; set; }
public SmsCustomerAmountChoice CustomerAmountChoice { get; set; }
}
this is my entity after i extend it with the remote attr in WebUI.Models
public class Customer : SmsCustomer
{
[Required(ErrorMessage = "Error required")]
[StringLength(9, ErrorMessage = "Error length")]
[Remote("CheckPassword", "RemoteValidation", ErrorMessage = "Error remote")]
public override string SmsCode { get; set; }
}
this is my view
#Html.TextBoxFor(c => c.SmsCode)
//error span
<span class="checkbox-form-error" data-valmsg-for="SmsCode" data-valmsg-replace="true"> </span>
The remote validation stuff is very specific to the WebUI project.
Because of this, I'd create a View model that inherits from the actual class, and then override the property that needs remote validation. Then you should be able to specify the controller/action for remote validation.
You can also put your validation in a class of its own, like ScottGu demonstrates here:
http://weblogs.asp.net/scottgu/archive/2010/01/15/asp-net-mvc-2-model-validation.aspx
(Look down the post, before the last step)
Also take a look at this: Adding DataAnnontations to Generated Partial Classes