In this article, we will learn how to use Context to delete many-to-many relational entities in the Entity Framework. We will use a small console instance to understand and learn the whole process of entity deletion in Entity Framework.
You will learn
-
How to create a project that references Entity Framework?
-
How to configure the database connection of Entity Framework?
-
How to remove the plural of table names generated by Entity Framework Code First?
-
How to configure the entity's Fluent API through EntityType Configuartion <T>;
-
How to configure Entity Framework entity multi-to-many mapping;
-
Entity Framework data initialization;
-
How to use the package management tool console to generate and update the database?
-
How to delete the data of many-to-many relationship in Entity Framework?
This example development environment
-
Operating System: Windows 10
-
Development tools and versions: Visual Studio 2015 Update 1
-
NET Framework Version:.NET Framework 4.6
-
Program output mode: console application program
Step 1: Create the project and reference the package
1.1 Create Projects
First, we create a console application named EFRemoveManyToManyDemo, as follows:
1.2 Reference Package
Then open the package management tool and install the necessary EntityFramework reference package as follows:
Step 2: Create entity classes and configure database connections
2.1 Creating Entity Classes
After installing the Entity Framework package, we first create the corresponding classes of the two entities required for this example: User and Role (both under the folder of Model), as follows:
User.cs
using System;
using System.Collections.Generic;
namespace EFRemoveManyToManyDemo.Model
{
public class User
{
public User()
{
Roles = new HashSet<Role>();
}
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime? CreatedOn { get; set; }
public virtual ICollection<Role> Roles { get; set; }
}
}
Role.cs
using System.Collections.Generic;
namespace EFRemoveManyToManyDemo.Model
{
public class Role
{
public Role()
{
this.Users = new HashSet<User>();
}
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<User> Users { get; set; }
}
}
2.2 Configure Fluent API
To configure the Fluent API, create a new Mapping folder, and then create User Configuration Mapping and Role Configuration Mapping, respectively, as follows:
UserConfiguration.cs
using EFRemoveManyToManyDemo.Model;
using System.Data.Entity.ModelConfiguration;
namespace EFRemoveManyToManyDemo.Mapping
{
public class UserConfigurationMapping : EntityTypeConfiguration<User>
{
public UserConfigurationMapping()
{
Property(x => x.FirstName).HasMaxLength(50).IsRequired();
Property(x => x.LastName).HasMaxLength(50).IsRequired();
}
}
}
RoleConfigurationMapping.cs
using EFRemoveManyToManyDemo.Model;
using System.Data.Entity.ModelConfiguration;
namespace EFRemoveManyToManyDemo.Mapping
{
public class RoleConfigurationMapping : EntityTypeConfiguration<Role>
{
public RoleConfigurationMapping()
{
HasKey(x => x.Id);
Property(x => x.Name).HasMaxLength(50).IsRequired();
HasMany(x => x.Users)
.WithMany(x => x.Roles)
.Map(m =>
{
m.MapLeftKey("RoleId");
m.MapRightKey("UserId");
m.ToTable("LNK_User_Role");
});
}
}
}
2.3 Create Context Class
Next, we create a class named ManyToManyRemoveContext, which inherits from the DbContext class to manage some configuration and operation of database connection context and database initialization, as follows:
using EFRemoveManyToManyDemo.Mapping;
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration.Conventions;
namespace EFRemoveManyToManyDemo
{
public class ManyToManyRemoveContext : DbContext
{
public ManyToManyRemoveContext() : base("ManyToManyRemoveContext")
{
}
}
}
2.4 Configuration connection string
Then add the local database connection string in the App.config configuration file, which is roughly as follows (specific according to your actual data connection parameters):
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
</configSections>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6" />
</startup>
<connectionStrings>
<add name="ManyToManyRemoveContext" connectionString="server=Your database server address;database=ManyToManyRemoveDemo;uid=Your database login name;pwd=Password" providerName="System.Data.SqlClient"/>
</connectionStrings>
<entityFramework>
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
<parameters>
<parameter value="mssqllocaldb" />
</parameters>
</defaultConnectionFactory>
<providers>
<provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
</providers>
</entityFramework>
</configuration>
2.5 Rewrite Context
In order to apply the Fluent API we just wrote to the corresponding entity, we need to override the OnModel Creating method of DbContext, as follows:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Configurations.Add(new UserConfigurationMapping());
modelBuilder.Configurations.Add(new RoleConfigurationMapping());
}
among
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
Entity Framework Code First is used to remove the plural of the table name when the entity class generates the corresponding table. Simply put, by default, the table names of Entity Framework Code First are plural when generating corresponding tables from entity classes, such as the User and Role classes in this example. Without this configuration, the table names of Users and Roles would be used when generating table names, and vice versa, the two table names of User and Role.
Well, here's the code for the complete ManyToManyRemoveContext.cs file:
using EFRemoveManyToManyDemo.Mapping;
using EFRemoveManyToManyDemo.Model;
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration.Conventions;
namespace EFRemoveManyToManyDemo
{
public class ManyToManyRemoveContext : DbContext
{
public ManyToManyRemoveContext() : base("ManyToManyRemoveContext")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Configurations.Add(new UserConfigurationMapping());
modelBuilder.Configurations.Add(new RoleConfigurationMapping());
}
public DbSet<User> Users { get; set; }
public DbSet<Role> Roles { get; set; }
}
}
At this point, the references to Entity Framework, the declaration of entity classes, the configuration of Fluent API and the connection to the database are all completed. The next thing we need to do is to use Entity Framework entities to generate into the configured database.
The third step is to apply Migration to generate database
In the next step, we will use the Package Manager Console and three commands:
3.1 Enable-Migrations
The command is used in the following way:
After running the above command, the Entity Framework automatically creates a folder named Migrations in our project and a Configuartion.cs configuration file. At this time, the project structure is roughly as follows:
After generating the Configuration.cs file, we initialize the data as follows:
namespace EFRemoveManyToManyDemo.Migrations
{
using Model;
using System;
using System.Collections.Generic;
using System.Data.Entity.Migrations;
using System.Linq;
internal sealed class Configuration : DbMigrationsConfiguration<ManyToManyRemoveContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
}
protected override void Seed(ManyToManyRemoveContext context)
{
var roles = new List<Role> {
new Role{ Id=1,Name="Super Administrator" },
new Role{ Id=2,Name="Administrators" },
new Role{ Id=3,Name="General user" }
};
var users = new List<User> {
new User {Id=1,FirstName="Kobe",LastName="Bryant",CreatedOn=DateTime.Now,Roles=roles },
new User {Id=2,FirstName="Chris",LastName="Paul",CreatedOn=DateTime.Now,Roles=roles.Where(x=>x.Id==2).ToList() },
new User {Id=3,FirstName="Jerimy",LastName="Lin",CreatedOn=DateTime.Now,Roles=roles.Take(2).ToList() }
};
}
}
}
After completing the first command and data initialization configuration, we proceed to the second command.
3.2 Add-Migration Init -Verbose
After executing this command, a data migration file like timestamp_Init.cs will be automatically generated in the folder of Migrations, such as the file name 201512040507219_Init.cs generated in this example, where Init is the version name of the data migration we specified, and the contents of the file are as follows:
namespace EFRemoveManyToManyDemo.Migrations
{
using System;
using System.Data.Entity.Migrations;
public partial class Init : DbMigration
{
public override void Up()
{
CreateTable(
"dbo.Role",
c => new
{
Id = c.Int(nullable: false, identity: true),
Name = c.String(nullable: false, maxLength: 50),
})
.PrimaryKey(t => t.Id);
CreateTable(
"dbo.User",
c => new
{
Id = c.Int(nullable: false, identity: true),
FirstName = c.String(nullable: false, maxLength: 50),
LastName = c.String(nullable: false, maxLength: 50),
CreatedOn = c.DateTime(),
})
.PrimaryKey(t => t.Id);
CreateTable(
"dbo.LNK_User_Role",
c => new
{
RoleId = c.Int(nullable: false),
UserId = c.Int(nullable: false),
})
.PrimaryKey(t => new { t.RoleId, t.UserId })
.ForeignKey("dbo.Role", t => t.RoleId, cascadeDelete: true)
.ForeignKey("dbo.User", t => t.UserId, cascadeDelete: true)
.Index(t => t.RoleId)
.Index(t => t.UserId);
}
public override void Down()
{
DropForeignKey("dbo.LNK_User_Role", "UserId", "dbo.User");
DropForeignKey("dbo.LNK_User_Role", "RoleId", "dbo.Role");
DropIndex("dbo.LNK_User_Role", new[] { "UserId" });
DropIndex("dbo.LNK_User_Role", new[] { "RoleId" });
DropTable("dbo.LNK_User_Role");
DropTable("dbo.User");
DropTable("dbo.Role");
}
}
}
We can see from the contents of this file that there are two methods, Up() and Down(), which are actually the data migration operation, and Down() method is the operation we should perform if we want to return to this version later.
After the above two commands, if you can't wait to go to the database management tool to see if a database named ManyToManyRemoveDemo has been generated, it's a pity to tell you that it hasn't. At this point, we have to execute the last command to generate tables corresponding to the database and entities.
3.3 Update-Database -Verbose
Execute the above command, and then we will open the database management tool. Yes, ManyToManyRemoveDemo is right there. Then check whether the table is successfully generated, and check whether there is any initialized data in the table. Yes, these are all possible. Well, surprise, cheer, we have done it!!!
But it's not over yet. Please calm down first. It's just the beginning. Entity Framework can do more, we need to learn a lot, programming path is never in place, there must be a process. Look down step by step.
Step 4: Add, delete, modify and check operations
4.1 Examples of Query Data
Open the Program.cs file for our project. First, let's Query the data in the database, as follows:
static void Main(string[] args)
{
Query();
ReadKey();
}
static void Query()
{
using (var cxt = new ManyToManyRemoveContext())
{
var users = cxt.Users.ToList();
users.ForEach(x =>
{
WriteLine("User First Name:{0},Last Name:{1},Create On:{2}\n |__Roles:{3}", x.FirstName, x.LastName, x.CreatedOn, string.Join(",", x.Roles.Select(r => r.Name)));
});
}
}
The results of operation are shown as follows:
4.2 Update Data Example
How about updating the data in another database, as follows:
static void Main(string[] args)
{
Update();
Query();
ReadKey();
}
static void Query()
{
using (var cxt = new ManyToManyRemoveContext())
{
var users = cxt.Users.ToList();
users.ForEach(x =>
{
WriteLine("User First Name:{0},Last Name:{1},Create On:{2}\n |__Roles:{3}", x.FirstName, x.LastName, x.CreatedOn, string.Join(",", x.Roles.Select(r => r.Name)));
});
}
}
static void Update()
{
using (var cxt = new ManyToManyRemoveContext())
{
var user = cxt.Users.FirstOrDefault(x=>x.Id==3);
user.FirstName = "ShuHao";
cxt.SaveChanges();
}
}
The results of the operation are as we expected, as shown in the figure:
4.3 Delete Data Example
The First Name of User Id 3 has been updated from the database. Similarly, we need to complete the deletion operation is relatively simple, as follows:
static void Remove()
{
using (var cxt = new ManyToManyRemoveContext())
{
var user = cxt.Users.FirstOrDefault(x=>x.Id==2);
cxt.Users.Remove(user);
cxt.SaveChanges();
}
}
4.4 New Data Samples
No more mapping. Finally, add an operation, add a user to the User table and assign a role with Id 1. The code is as follows:
static void Add()
{
List<Role> roles;
using (var cxt = new ManyToManyRemoveContext())
{
roles = cxt.Roles.ToList();
cxt.Users.Add(new User
{
Id = 4,
FirstName = "Console",
LastName = "App",
CreatedOn = DateTime.Now,
Roles = roles.Where(x => x.Id == 1).ToList()
});
}
}
4.5 Examples of deleting multiple pairs of data
Well, the above is a simple operation of adding, deleting, modifying and checking User (user entity). So we need to implement many-to-many deletion operation? That is to delete the user while deleting its corresponding role, the code is as follows:
static void RemoveManyToMany()
{
using (var cxt = new ManyToManyRemoveContext())
{
var user = cxt.Users.FirstOrDefault(x => x.Id == 1);
var roles = new List<Role>();
roles.AddRange(user.Roles.Select(x => x));
foreach (var role in roles)
{
user.Roles.Remove(role);
}
cxt.Users.Remove(user);
cxt.SaveChanges();
}
}
The results of operation are shown as follows:
Complete sample code and download address
All right. Finally, paste up the test file of Program.cs for your reference.
using EFRemoveManyToManyDemo.Model;
using System;
using System.Collections.Generic;
using System.Linq;
using static System.Console;
namespace EFRemoveManyToManyDemo
{
public class Program
{
static void Main(string[] args)
{
//Update();
WriteLine("Before many to many removed");
Query();
RemoveManyToMany();
WriteLine("After many to many removed");
Query();
ReadKey();
}
static void Query()
{
using (var cxt = new ManyToManyRemoveContext())
{
var users = cxt.Users.ToList();
users.ForEach(x =>
{
WriteLine("User First Name:{0},Last Name:{1},Create On:{2}\n |__Roles:{3}", x.FirstName, x.LastName, x.CreatedOn, string.Join(",", x.Roles.Select(r => r.Name)));
});
}
}
static void Add()
{
List<Role> roles;
using (var cxt = new ManyToManyRemoveContext())
{
roles = cxt.Roles.ToList();
cxt.Users.Add(new User
{
Id = 4,
FirstName = "Console",
LastName = "App",
CreatedOn = DateTime.Now,
Roles = roles.Where(x => x.Id == 1).ToList()
});
}
}
static void Update()
{
using (var cxt = new ManyToManyRemoveContext())
{
var user = cxt.Users.FirstOrDefault(x => x.Id == 3);
user.FirstName = "ShuHao";
cxt.SaveChanges();
}
}
static void Remove()
{
using (var cxt = new ManyToManyRemoveContext())
{
var user = cxt.Users.FirstOrDefault(x => x.Id == 2);
cxt.Users.Remove(user);
cxt.SaveChanges();
}
}
static void RemoveManyToMany()
{
using (var cxt = new ManyToManyRemoveContext())
{
var user = cxt.Users.FirstOrDefault(x => x.Id == 1);
var roles = new List<Role>();
roles.AddRange(user.Roles.Select(x => x));
foreach (var role in roles)
{
user.Roles.Remove(role);
}
cxt.Users.Remove(user);
cxt.SaveChanges();
}
}
}
}
If you need a complete sample source code, Please click here to download