In this article, we will discuss how to customize the data model by using attributes that specify formatting, validation, and database mapping rules.
In the Author
entity, we have BirthDate
property, and all of the web pages currently display the time along with the date.
In this field, we only need the date instead of date and time. We can use data annotation attributes and make one code change that will fix the display format in every view that shows the birth date.
In Models\Author.cs, add a using
statement for the System.ComponentModel.DataAnnotations
namespace and add DataType
and DisplayFormat
attributes to the BirthDate
property, as shown below.
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace MvcWithEFCoreDemo.Models
{
public class Author
{
public int AuthorId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime BirthDate { get; set; }
public virtual ICollection<Book> Books { get; set; }
}
}
The DataType
attribute is used to specify a data type that is more specific than the database intrinsic type.
DataType.Date
does not specify the format of the date that is displayed.CultureInfo
.Let's run your application and go to the author Index
page again and you will see that times are no longer displayed for the birth dates.
The same will be true for any view that uses the Author
model.
You can also specify data validation rules and validation error messages using attributes.
StringLength
attribute sets the maximum length in the database and provides the client-side and server-side validation for ASP.NET MVC.To add this limitation, add StringLength
attributes to the LastName
and FirstName
properties as shown below.
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace MvcWithEFCoreDemo.Models
{
public class Author
{
public int AuthorId { get; set; }
[StringLength(20)]
public string FirstName { get; set; }
[StringLength(20)]
public string LastName { get; set; }
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime BirthDate { get; set; }
public virtual ICollection<Book> Books { get; set; }
}
}
We can also specify the ErrorMessage
which will be shown on the webpage when the user enters more than 20 characters in the name field.
[StringLength(20, ErrorMessage = "Name cannot be longer than 20 characters.")]
The database model has changed in a way that requires a change in the database schema. In the Package Manager Console
, enter the following commands in Package Manager Console
.
.
Add-Migration MaxLengthOnNames
Update-Database
The add-migration
command creates a file named <timeStamp>_MaxLengthOnNames.cs
.
using Microsoft.EntityFrameworkCore.Migrations;
namespace MvcWithEFCoreDemo.Migrations
{
public partial class MaxLengthOnNames : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<string>(
name: "LastName",
table: "Authors",
maxLength: 20,
nullable: true,
oldClrType: typeof(string),
oldNullable: true);
migrationBuilder.AlterColumn<string>(
name: "FirstName",
table: "Authors",
maxLength: 20,
nullable: true,
oldClrType: typeof(string),
oldNullable: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<string>(
name: "LastName",
table: "Authors",
nullable: true,
oldClrType: typeof(string),
oldMaxLength: 20,
oldNullable: true);
migrationBuilder.AlterColumn<string>(
name: "FirstName",
table: "Authors",
nullable: true,
oldClrType: typeof(string),
oldMaxLength: 20,
oldNullable: true);
}
}
}
It contains the code in the Up
method that will update the database to match the current data model. The update-database
command ran that code.
The NotMapped
attribute is used to specify an entity or property that is not to be mapped to a table or column in the database.
NotMapped
attribute overrides this default convention, and the entity or property will not be mapped to a table or column in the database.In the following example, the FullName
property will not be mapped to a column in the Authors
table in the database.
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace MvcWithEFCoreDemo.Models
{
public class Author
{
public int AuthorId { get; set;
[StringLength(20, ErrorMessage = "Name cannot be longer than 20 characters.")]
public string FirstName { get; set; }
[StringLength(20, ErrorMessage = "Name cannot be longer than 20 characters.")]
public string LastName { get; set; }
[NotMapped]
public string FullName
{
get
{
return FirstName + " " + LastName;
}
}
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime BirthDate { get; set; }
public virtual ICollection<Book> Books { get; set; }
}
}
The Display
attribute specifies that the caption for the text boxes on views. For example, to show First Name, Last Name, Full Name, and Birth Date instead of the property name on the views, you can use the Display
attribute.
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace MvcWithEFCoreDemo.Models
{
public class Author
{
public int AuthorId { get; set; }
[StringLength(20, ErrorMessage = "Name cannot be longer than 20 characters.")]
public string FirstName { get; set; }
[StringLength(20, ErrorMessage = "Name cannot be longer than 20 characters.")]
public string LastName { get; set; }
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
[NotMapped]
[Display(Name = "Full Name")]
public string FullName
{
get
{
return FirstName + " " + LastName;
}
}
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime BirthDate { get; set; }
public virtual ICollection<Book> Books { get; set; }
}
}
FullName
is a calculated property that returns a value by concatenating two other properties. Therefore, it has only a get accessor, and no FullName column will be generated in the database.