Object-relational mapping is a programming technique for converting data between incompatible type systems in object-oriented programming languages.
It's good for abstracting the datastore
(SQL / XML / Json / flat file / whatever)
out in order to provide an interface that can be used in your code.
It tries to abstract the database access, so if you change from mssql to mysql you don't have to write your code again (rudely speaking).
ORM are not so well in addressing relational database specific features.
“With ORM, the database becomes an implementation detail, and can be ignored”
-Idiots Ever...
“The database is a bucket”
-Me being totally wrong...
Performance of SELECT mapping over 500 iterations - POCO serialization (by StackExchange team)
Method | Duration |
---|---|
Hand coded (using a SqlDataReader) | 47ms |
Dapper ExecuteMapperQuery |
49ms |
ServiceStack.OrmLite | 50ms |
NHibernate SQL | 104ms |
Linq 2 SQL | 181ms |
Entity framework 6 | 631ms |
Entity framework Core 1 | ? ms |
NHibernate
Dapper
Entity Framework is an open source orm framework from asp.net team. There are 2 versions actively developing
EF 6 is a tried and tested data access technology with many years of features and stabilization
EF Core is a lightweight, extensible, and cross-platform version of Entity Framework.
EF Core introduces many improvements and new features when compared with EF6.x but some features from EF6 are not yet implemented.
It enables you to create model from an existing database (like SQL Server, Oracle, DB2 etc.).
This approach reduces the amount of code that we need to write since it automatically generates code. But it also limits us to work with the structure of the generated code.
Very popular if you have Database designed by DBAs, developed separately or if you have existing Database.
It enables you to create model’s Entities, relationships, and inheritance hierarchies on the design surface of empty model (.edmx file) by using entity designer and then create database from it.
It is good if you like to visualize the structure of the data in the application or you don't like writing code or SQL since it will generate for you.
In this approach you have no much control over your entities (auto generated code which is hard to modify) and database. In this way it is rarely used but for small easy projects this approach will be very productive.
It enables you to describe a model by using C# classes and then create database from these classes. These classes are called POCO classes.
It provides full control over the code since there is no auto generated code which is hard to modify.
In this approach, your code defines only the database mappings and EF will handle creation of database with its relations.
public class Blog
{
//Primary Key
public int Id { get; set; }
public string Title { get; set; }
//Navigation Property
public virtual ICollection<Post> Posts { get; set; }
}
public class Post
{
//Primary Key
public int Id { get; set; }
public string Title { get; set; }
//Foreing Key
public int BlogId { get; set; }
//Navigation Property
[ForeignKey("BlogId")]
public virtual Blog Blog { get; set; }
}
When we are designing our model we have in mind some conventions.
Conventions are sets of rules that are used to automatically configure a conceptual model based on class definitions when working with Code First.
public class Blog
{
//Primary Key
public int Id { get; set; }
//....
}
//Foreing Key
public int BlogId { get; set; }
//Navigation Property
public virtual Blog Blog { get; set; }
DbContext is an important part of Entity Framework. It is a bridge between your domain or entity classes and the database. DbContext is responsible for the following activities:
public class BlogContext : DbContext
{
public BlogContext() : base ("BlogContextConStringName")
{
}
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
//To access the fluent API you override the OnModelCreating method in DbContext
protected override void OnModelCreating(DbModelBuilder dbModelBuilder)
{
}
}
Depending on your configuration file
(app.config || web.config || appsettings.json)
<connectionStrings>
<add name="BlogContextConStringName" connectionString="Server=.;Database=DemoEf6;User Id=sa;password=1234;MultipleActiveResultSets=true" providerName="System.Data.SqlClient"/>
</connectionStrings>
{
"ConnectionStrings": {
"BlogContextConStringName": "Server=.;Database=DemoEf6;User Id=sa;password=1234;MultipleActiveResultSets=true"
}
}
To begin with Migrations:
To make changes in your model:
In the package manager console run the following command.
> Enable-Migrations
> Add-Migration "MyInitialCreate"
> Update-Database
To make changes
> Add-Migration "ThisIsNewChange"
> Update-Database
Pull data from the Database
public static void PullDataFromTheDatabase()
{
var lstPosts = new List<Post>();
using (var db = new BlogContext()) {
lstPosts = db.Posts.ToList();
}
foreach (var p in lstPosts) {
Console.WriteLine($"Post with id {p.Id} has the title {p.Title}.");
}
}
Insert in the database a new object
public static void InsertDataToTheDatabase()
{
var blog = new Blog()
{
Title = "My Ef Blog",
Posts = new List<Post>()
{
new Post() { Title = "Post One" },
new Post() { Title = "Post Two" },
}
};
using (var db = new BlogContext())
{
db.Blogs.Add(blog);
db.SaveChanges();
}
}
Find Data And Update
public static void FindDataAndUpdate()
{
using (var db = new BlogContext())
{
var blog = db.Blogs.Where(b => b.Title == "Post Two").FirstOrDefault();
blog.Title = "My Awesome Ef Blog";
db.SaveChanges();
}
}
Find Data And Delete it
public static void FindDataAndDelete()
{
using (var db = new BlogContext()) {
var post = db.Posts.FirstOrDefault(p => p.Title == "Post Two");
db.Posts.Remove(post);
db.SaveChanges();
}
}
Join
public static void ASimpleJoin()
{
//don't forget using System.Data.Entity;
var lstBlogs = new List<Blog>();
using (var db = new BlogContext()) {
lstBlogs = db.Blogs.Include(b => b.Posts).ToList();
}
foreach (var b in lstBlogs) {
Console.WriteLine($"This blog {b.Title} has this posts : ");
foreach (var p in b.Posts) {
Console.WriteLine($"This is a post title : {p.Title}");
}
}
}
Other stuff Linq can perform
public static void CounAllPosts()
{
using (var db = new BlogContext()) {
var totalPost = db.Posts.Count();
Console.WriteLine($"Total posts {totalPost}");
var sumTotalLikesInPosts = db.Posts.Sum(x=>x.Likes);
Console.WriteLine($"Total posts likes {sumTotalLikesInPosts}");
//...
}
}
Note! DataAnnotations only give you subset of configuration options. Fluent API provides full set of configuration options available in Code First.
[Table("Product_Order")]
public class Order
{
[Key]
[Column("Order_ID")]
public int Id { get; set; }
public DateTime? Date { get; set; }
public OrderState State { get; set; }
public string Item { get; set; }
[Range(1,25)]
public int Quantity { get; set; }
[MinLength(3, ErrorMessage="What are you thinking??")]
[MaxLength(50, ErrorMessage="ERROR!! FAILZ!!!")]
public string Name { get; set; }
[NotMapped]
public string Note { get; set; }
[InverseProperty("Order")]
public virtual ICollection<Product> Products { get; set; }
}
The code first fluent API is most commonly accessed by overriding the OnModelCreating method on your derived DbContext.
public class BlogContext : DbContext
{
public BlogContext() : base ("BlogContextConStringName")
{
}
//To access the fluent API you override the OnModelCreating method in DbContext
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>()
.Property(u => u.DisplayName)
.HasColumnName("display_name");
//Configuring a Primary Key
modelBuilder.Entity<OfficeAssignment>()
.HasKey(t => t.InstructorID);
//Configuring the Property to be Required
modelBuilder.Entity<Department>()
.Property(d => d.Name).IsRequired();
//Specifying the Maximum Length on a Property
modelBuilder.Entity<Department>()
.Property(d => d.Name).HasMaxLength(50);
}
}
//Data Annotation
[Column("Order_ID")]
public int Id { get; set; }
//Fluent API
modelBuilder.Entity<Order>()
.Property(d => d.Id).HasColumnName("Order_ID");
yeah, Attributes are very cool...
Attribute | Desription |
---|---|
Required | The Required annotation will force EF (and MVC) to ensure that property has data in it. |
MinLength | MinLength annotation validates property whether it has minimum length of string. |
MaxLength | MaxLength annotation maximum length of property which in turn sets the maximum length of column in the database |
StringLength | Specifies the minimum and maximum length of characters that are allowed in a data field. |
Attribute | Desription |
---|---|
Table | Specify name of the DB table which will be mapped with the class |
Column | Specify column name and datatype which will be mapped with the property |
Index | Create an Index for specified column. (EF 6.1 onwards only) |
ForeignKey | Specify Foreign key property for Navigation property |
NotMapped | Create an Index for specified column. (EF 6.1 onwards only) |
Attribute | Desription |
---|---|
DatabaseGenerated | DatabaseGenerated attribute specifies that property will be mapped to Computed column of the database table. So the property will be read-only property. It can also be used to map the property to identity column |
InverseProperty | InverseProperty is useful when you have multiple relationships between two classes. |
Attribute | Desription |
---|---|
Key | Mark property as EntityKey which will be mapped to PK of related table. |
Timestamp | Mark the property as a non-nullable timestamp column in the database. |
Concurrency Check | ConcurrencyCheck annotation allows you to flag one or more properties to be used for concurrency checking in the database when a user edits or deletes an entity. |
protected override void Seed(BlogContext context)
{
context.Blogs.AddOrUpdate(
b => b.Title,
new Blog() { Title = "My Ef Blog" },
new Blog() { Title = "My .Net Blog" }
);
context.SaveChanges();
}
Microsoft SQL Server Profiler is a graphical user interface to SQL Trace for monitoring an instance of the Database Engine or Analysis Services. You can capture and save data about each event to a file or table to analyze later.
Inspecting your Entity Framework SQL