zoukankan      html  css  js  c++  java
  • [C#/.NET]Entity Framework(EF) Code First 多对多关系的实体增,删,改,查操作全程详细示例

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

    你将学习到

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

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

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

    • 怎样通过EntityTypeConfiguartion<T>配置实体的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<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 配置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<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 创建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>
        <!-- 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=你的数据库服务器地址;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<PluralizingTableNameConvention>();
    
                modelBuilder.Configurations.Add(new UserConfigurationMapping());
                modelBuilder.Configurations.Add(new RoleConfigurationMapping());
            }

    其中

    modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();

    是将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<PluralizingTableNameConvention>();
    
                modelBuilder.Configurations.Add(new UserConfigurationMapping());
                modelBuilder.Configurations.Add(new RoleConfigurationMapping());
            }
    
            public DbSet<User> Users { get; set; }
            public DbSet<Role> 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<Role> {
                    new Role{ Id=1,Name="超级管理员" },
                    new Role{ Id=2,Name="管理员" },
                    new Role{ Id=3,Name="一般用户" }
                };
    
                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() }
                };
            }
        }
    }

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

    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}
     |__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}
     |__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<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 删除多对多数据的示例

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

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

    运行结果如图:

    完整示例代码及下载地址

    好了,最后把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}
     |__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();
                }
            }
        }
    }

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

  • 相关阅读:
    wex5 实战 框架拓展之2 事件派发与data刷新
    wex5 实战 框架拓展之1 公共data组件(Data)
    wex5 实战 HeidiSQL 导入Excel数据
    wex5 实战 手指触屏插件 hammer的集成与优劣
    wex5 实战 登陆帐号更换与用户id一致性
    wex5 实战 用户点评与提交设计技巧
    wex5 实战 省市县三级联动与地址薄同步
    wex5 实战 wex5与js的组件关系与执行顺序(父子与先后)
    wex5 实战 单页模式下的多页面数据同步
    [BZOJ]4237: 稻草人
  • 原文地址:https://www.cnblogs.com/Alex80/p/7791754.html
Copyright © 2011-2022 走看看