热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

EntityFramework(EF)CodeFirst多对多关系的实体增,删,改,查操作全程详细示例

本文我们来学习一下在EntityFramework中使用Context删除多对多关系的实体是如何来实现的。我们将以一个具体的控制台小实例来了解和学习整个实现EntityFramework多对多关

本文我们来学习一下在Entity Framework中使用Context删除多对多关系的实体是如何来实现的。我们将以一个具体的控制台小实例来了解和学习整个实现Entity Framework 多对多关系的实体删除的操作过程。

你将学习到

  • 怎样创建一个引用Entity Framework的项目;

  • 怎样配置Entity Framework的数据库连接;

  • 怎样去掉Entity Framework Code First 生成的表名的复数;

  • 怎样通过EntityTypeConfiguartion配置实体的Fluent API ;

  • 怎样配置Entity Framework的实体多对多的关系映射;

  • Entity Framework数据初始化;

  • 怎样使用包管理工具控制台来生成和更新数据库;

  • 怎么删除Entity Framework中的多对多关系的数据。

本示例开发环境

  • 操作系统:Windows 10

  • 开发工具及版本:Visual Studio 2015 Update 1

  • .NET Framework版本:.NET Framework 4.6

  • 程序输出方式:控制台应用程序

第一步、创建项目并引用程序包

1.1 创建项目

首先,我们创建一个控制台应用程序,取名为:EFRemoveManyToManyDemo,如下图:

1.2 引用程序包

接着打开程序包管理工具,安装必须的EntityFramework引用包,如下:

第二步、创建实体类并配置数据库连接

2.1 创建实体类

安装好Entity Framework包之后 ,我们先创建本示例需要的两个实体对应的类:User和Role(都放在Model的文件夹下),如下:

User.cs

using System;
using System.Collections.Generic;

namespace EFRemoveManyToManyDemo.Model
{
public class User
{
public User()
{
Roles = new HashSet();
}
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime? CreatedOn { get; set; }
public virtual ICollection Roles { get; set; }
}
}

Role.cs

using System.Collections.Generic;

namespace EFRemoveManyToManyDemo.Model
{
public class Role
{
public Role()
{
this.Users = new HashSet();
}
public int Id { get; set; }
public string Name { get; set; }

public virtual ICollection Users { get; set; }
}
}

2.2 配置Fluent API

为了配置Fluent API,新建一个Mapping文件夹,再分别创建User的配置文件UserConfigurationMapping和Role的配置文件RoleConfigurationMapping,如下:

UserConfiguration.cs

using EFRemoveManyToManyDemo.Model;
using System.Data.Entity.ModelConfiguration;

namespace EFRemoveManyToManyDemo.Mapping
{
public class UserConfigurationMapping : EntityTypeConfiguration
{
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
{
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 创建Context类

接下来,我们再创建一个名为:ManyToManyRemoveContext的类,该类继承至DbContext类,用于管理数据库的连接上下文和数据库初始化等的一些配置和操作,如下:

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 配置连接字符串

再在App.config配置文件中添加本地的数据库连接字符串,大致如下(具体的请根据你的实际数据连接参数来):

xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>

<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=你的数据库服务器地址;database=ManyToManyRemoveDemo;uid=你的数据库登录名;pwd=密码" 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 重写Context

为了将我们刚才写的Fluent API应用到对应的实体上,所以我们需要重写(override)DbContext的OnModelCreating方法,如下:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Remove();

modelBuilder.Configurations.Add(new UserConfigurationMapping());
modelBuilder.Configurations.Add(new RoleConfigurationMapping());
}

其中

modelBuilder.Conventions.Remove();

是将Entity Framework Code First在实体类生成对应表时去掉表名的复数用的。简单地说就是,默认情况下,Entity Framework Code First在由实体类生成对应表时的表名是复数形式的,比如本例的User和Role类,如果没有这句配置,在生成表名的时候将会是Users和Roles这两个表名,反之,则是User和Role这两个表名。

好了,下面贴出完整的ManyToManyRemoveContext.cs文件的代码:

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();

modelBuilder.Configurations.Add(new UserConfigurationMapping());
modelBuilder.Configurations.Add(new RoleConfigurationMapping());
}

public DbSet Users { get; set; }
public DbSet Roles { get; set; }
}
}

本文写到这里,关于Entity Framework的引用,实体类的声明和Fluent API配置以及与数据库连接等操作都已完成了。接下来我们要做的是利用Entity Framework所实体生成到配置好的数据库中。

第三步、应用Migration生成数据库

在接下来的过程中,我们会用到包管理控制台(Package Manager Console)和三个命令:

3.1 Enable-Migrations

命令使用方式如下图:

运行以上命令后,Entity Framework会自动在我们的项目中创建一个名为Migrations的文件夹,同时生成一个Configuartion.cs的配置文件。这时的项目结构大致是这样的:

生成好Configuration.cs的文件我们再作数据的初始化,如下:

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 {
new Role{ Id=1,Name="超级管理员" },
new Role{ Id=2,Name="管理员" },
new Role{ Id=3,Name="一般用户" }
};

var users = new List {
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() }
};
}
}
}

完成第一个命令和数据初始化配置后,我们进行第二个命令。

3.2 Add-Migration Init -Verbose

执行此命令后,会在Migrations的文件夹中自动生成一个形如:时间戳_Init.cs的数据迁移文件,如本例生成的是201512040507219_Init.cs这样一个文件名,其中Init是我们指定的本次数据迁移的版本名称,文件中的内容如下:

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");
}
}
}

我们可以通过这个文件中的内容看到,有Up()和Down()这两个方法,Up()方法要执行的其实就是本次数据迁移要对数据进行的操作,而Down()方法则是在以后我们如果要退回到此版本应该执行的操作。

经过以上两个命令,如你迫不及待地要去数据库管理工具中查看有一个名叫:ManyToManyRemoveDemo的数据库是否已生成,那么很遗憾地告诉你,还没有。这时,我们还得执行最后一个命令来生成数据库和实体对应的表。

3.3 Update-Database -Verbose

执行以上命令,我们这时再打开数据库管理工具。没错ManyToManyRemoveDemo就在那里。再查看表是否成功生成呢,再检查一下表中是否有我们初始化的数据呢,没错,这些都是可以有的。怎么样,惊喜吧,欢呼吧,我们做到了!!!

但还没完,请先回复平静,这还只是一个开始。Entity Framework还可以做得更多,我们需要学习的也还有很多,编程的道路从来就不是一步到位的,得有个过程。一步一步往下看吧。

第四步、增、删、改、查操作

4.1 查询数据示例

打开我们项目的Program.cs文件。首先,我们来查询(Query)一下数据库中的数据,如下:

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)));
});
}
}

运行结果如图:

4.2 更新数据示例

再来更新一条数据库中的数据怎么样,如下:

 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();
}
}

运行结果如我们所料,如图:

4.3 删除数据示例

Id为3的User的FirstName已经从数据库更新了。同样的,我们要完成删除操作也比较简,如下:

 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 新增数据示例

就不再贴图了。最后是添加操作,向User表添加一个用户并分配一个Id为1的角色,代码如下:

static void Add()
{
List 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 删除多对多数据的示例

好了,以上是对User(用户实体)进行简单的增、删、改、查的操作,那么我们要实现多对多的删除操作呢?也就是删除用户的同时删除其对应的角色,实现的代码如下:

static void RemoveManyToMany()
{
using (var cxt = new ManyToManyRemoveContext())
{
var user = cxt.Users.FirstOrDefault(x => x.Id == 1);
var roles = new List();
roles.AddRange(user.Roles.Select(x => x));
foreach (var role in roles)
{
user.Roles.Remove(role);
}
cxt.Users.Remove(user);
cxt.SaveChanges();
}
}

运行结果如图:

完整示例代码及下载地址

好了,最后把Program.cs这个测试文件贴上来,供参考:

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 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();
roles.AddRange(user.Roles.Select(x => x));
foreach (var role in roles)
{
user.Roles.Remove(role);
}
cxt.Users.Remove(user);
cxt.SaveChanges();
}
}
}
}

如果需要完整的示例源码,请点击这里下载


推荐阅读
  • 《数据结构》学习笔记3——串匹配算法性能评估
    本文主要讨论串匹配算法的性能评估,包括模式匹配、字符种类数量、算法复杂度等内容。通过借助C++中的头文件和库,可以实现对串的匹配操作。其中蛮力算法的复杂度为O(m*n),通过随机取出长度为m的子串作为模式P,在文本T中进行匹配,统计平均复杂度。对于成功和失败的匹配分别进行测试,分析其平均复杂度。详情请参考相关学习资源。 ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • Imtryingtofigureoutawaytogeneratetorrentfilesfromabucket,usingtheAWSSDKforGo.我正 ... [详细]
  • GetWindowLong函数
    今天在看一个代码里头写了GetWindowLong(hwnd,0),我当时就有点费解,靠,上网搜索函数原型说明,死活找不到第 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文介绍了C#中数据集DataSet对象的使用及相关方法详解,包括DataSet对象的概述、与数据关系对象的互联、Rows集合和Columns集合的组成,以及DataSet对象常用的方法之一——Merge方法的使用。通过本文的阅读,读者可以了解到DataSet对象在C#中的重要性和使用方法。 ... [详细]
  • 本文介绍了在Linux下安装Perl的步骤,并提供了一个简单的Perl程序示例。同时,还展示了运行该程序的结果。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • 不同优化算法的比较分析及实验验证
    本文介绍了神经网络优化中常用的优化方法,包括学习率调整和梯度估计修正,并通过实验验证了不同优化算法的效果。实验结果表明,Adam算法在综合考虑学习率调整和梯度估计修正方面表现较好。该研究对于优化神经网络的训练过程具有指导意义。 ... [详细]
  • 在重复造轮子的情况下用ProxyServlet反向代理来减少工作量
    像不少公司内部不同团队都会自己研发自己工具产品,当各个产品逐渐成熟,到达了一定的发展瓶颈,同时每个产品都有着自己的入口,用户 ... [详细]
  • 本文介绍了一个在线急等问题解决方法,即如何统计数据库中某个字段下的所有数据,并将结果显示在文本框里。作者提到了自己是一个菜鸟,希望能够得到帮助。作者使用的是ACCESS数据库,并且给出了一个例子,希望得到的结果是560。作者还提到自己已经尝试了使用"select sum(字段2) from 表名"的语句,得到的结果是650,但不知道如何得到560。希望能够得到解决方案。 ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • HDFS2.x新特性
    一、集群间数据拷贝scp实现两个远程主机之间的文件复制scp-rhello.txtroothadoop103:useratguiguhello.txt推pushscp-rr ... [详细]
  • 利用Visual Basic开发SAP接口程序初探的方法与原理
    本文介绍了利用Visual Basic开发SAP接口程序的方法与原理,以及SAP R/3系统的特点和二次开发平台ABAP的使用。通过程序接口自动读取SAP R/3的数据表或视图,在外部进行处理和利用水晶报表等工具生成符合中国人习惯的报表样式。具体介绍了RFC调用的原理和模型,并强调本文主要不讨论SAP R/3函数的开发,而是针对使用SAP的公司的非ABAP开发人员提供了初步的接口程序开发指导。 ... [详细]
  • 深入理解Kafka服务端请求队列中请求的处理
    本文深入分析了Kafka服务端请求队列中请求的处理过程,详细介绍了请求的封装和放入请求队列的过程,以及处理请求的线程池的创建和容量设置。通过场景分析、图示说明和源码分析,帮助读者更好地理解Kafka服务端的工作原理。 ... [详细]
author-avatar
lf8762
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有