EF Core 二 、 入门 EF Core (简单操作)
入门EF Core
我们将开始真正的EF之旅了,这里使用SqlServer数据,然后DbFirst;
为嘛使用SqlServer,目前公司的整体业务全部在SqlSever,所以很多产品业务都是依托于这个,当然也在考虑做数据库切换,切换EF Core就是开始,为后续做好准备,目前SqlServer的linux集群部署太麻烦了,至少我是这样认为的,而且很多客户也都人格上排斥 .... 说多了都是泪 ....
然后就是DbFirst,公司是业务型公司,注重业务需求的设计,所以在需求开发之前,表结构的设计基本上都已经确定,基于现在的业务以及背景,可能DbFirst更加适合,当然Code First也不会丢掉的
一、安装 EF Core
新建类库,用来引用 Microsoft.EntityFrameworkCore.SqlServer
如果在项目中有类似的第三方程序集引用,建议放入统一的程序集,这样不用到处维护引用信息;而且有利于后期解耦,其他业务相关的都依赖统一接口就可以,这样具体的实现引用只是一个插件而已;
二、生成数据表结构
为了做测试,这里生成两张表,TestTable,以及TestTableDetail,用来模拟主从表的场景;一步步来啊
CREATE TABLE [dbo].[TestTable](
[Id] [INT] NOT NULL,
[Name] [NVARCHAR](200) NOT NULL,
PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
CREATE TABLE [dbo].[TestTableDetail](
[Id] [INT] NOT NULL,
[PID] [INT] NOT NULL,
[Name] [NVARCHAR](200) NOT NULL,
PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
三、建立实体对象
建立实体对象,用来与数据库的对象进行匹配
[Table("TestTable")]
public class TestTable
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
}
[Table("TestTableDetail")]
public class TestTableDetail
{
[Key]
public int Id { get; set; }
public int PID { get; set; }
public string Name { get; set; }
}
四、创建DbContext上下文
/// <summary>
/// 自定义 数据上下文
/// </summary>
public class MyDbContext : DbContext
{
public DbSet<TestTable> TestTables { get; set; }
public DbSet<TestTableDetail> TestTableDetails { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
//写入连接字符串
optionsBuilder.UseSqlServer("Data Source=.\\SQLSERVER2014;Initial Catalog=EfCore.Test;User ID=sa;Pwd=1");
}
}
DbContext是EF操作数据库的窗口,我们将为每个表来创建一个DbSet<>泛型类属性,用来操作具体的表对象;DbSet支持Linq操作;这里两个知识点:
1.如何配置连接字符串
如果是Asp.net Core程序,通常配置在Startup.cs中,需要导入 Microsoft.Extensions.Configuration 名命空间方可使用,按如下方式进行注册
public void ConfigureServices(IServiceCollection services) { services.AddDbContext<BloggingContext>(options => options.UseSqlServer(Configuration.GetConnectionString("BloggingDatabase")));
}
看下源码
EFCore.SqlServer
public static DbContextOptionsBuilder UseSqlServer(
[NotNull] this DbContextOptionsBuilder optionsBuilder,
[NotNull] string connectionString,
[CanBeNull] Action<SqlServerDbContextOptionsBuilder> sqlServerOptionsAction =
null)
{
Check.NotNull(optionsBuilder, nameof(optionsBuilder));
Check.NotEmpty(connectionString, nameof(connectionString));
var extension =
(SqlServerOptionsExtension)GetOrCreateExtension(optionsBuilder).WithConnectionString(connectionString);
((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension);
ConfigureWarnings(optionsBuilder);
sqlServerOptionsAction?.Invoke(new
SqlServerDbContextOptionsBuilder(optionsBuilder));
return optionsBuilder;
}
对DbContextOptionsBuilder进行扩展,提供了UseSqlServer方法,连接信息的提供都是通过 DbContextOptionsBuilder 来实现
针对WinForms以及WPF应用呢?我测试验证的就是控制台应用
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
//写入连接字符串
optionsBuilder.UseSqlServer("Data Source=.\\SQLSERVER2014;Initial Catalog=EfCore.Test;User ID=sa;Pwd=1");
}
这里我是固定了连接字符串,可以根据配置文件来写入了;可以看到也是对 DbContextOptionsBuilder 的 UseSqlSerer方法的调用;
查看源码:
DbContext
//定义虚方法OnConfiguring的位置
protected internal virtual void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
}
在InternalServiceProvider中进行了初始化调用,调用了OnConfiguring,从而进行了连接字符串的赋值;
2.是否需要为每个类都定义DbSet属性
如果业务系统过大,我们真的会定义一个DbContext,然后将所有Entity定义成DbSet<>?应该不会,这时我们可以通过反射来实现;
1.通过实现一个反射读取类,来动态读取实体类,也就是读取类具备 “Table”属性的目标类,或者自己集成一个父类用来识别实体类;
2.将读取到的实体类,动态加入到DbContext的实体模型中;
通过 重写 OnModelCreating 方法;微软官方文档地址:https://docs.microsoft.com/zh-cn/ef/core/modeling/
通过调用ModelBuilder.Entity方法直接贴代码:
/// <summary>
/// 自定义 数据上下文
/// </summary>
public class DynamicDbContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
//写入连接字符串
optionsBuilder.UseSqlServer("Data Source=.\\SQLSERVER2014;Initial Catalog=EfCore.Test;User ID=sa;Pwd=1");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
var assembly = Assembly.GetExecutingAssembly();
foreach (Type type in assembly.ExportedTypes)
{
if (type.IsClass && type != typeof(EntityBase) && typeof(EntityBase).IsAssignableFrom((Type) type))
{
var method = modelBuilder.GetType().GetMethods().FirstOrDefault(x => x.Name == "Entity");
if (method != null)
{
method = method.MakeGenericMethod(type);
method.Invoke(modelBuilder, null);
}
}
}
base.OnModelCreating(modelBuilder);
}
}
整体思路还是两步走,先找到实体的实现类,然后通过调用DbContenxt的Entity方法;我们来看下DbContext源码;
最后通过Metadata.AddEntityType加入到实体模型,Metadata用来存储实体元数据;
还有两外一种实现方式,其实也就是衍生的方式了,因为查看源码得知最后实体被加入到了Medel中,那何不直接加入呢?
调用代码:
var myDbContext = new DynamicDbContext();
var list = myDbContext.Set<TestTable>().ToList();
Console.WriteLine($"TestTable Count: {list.Count}");
if (!list.Any()) return;
Console.WriteLine($"TestTable Detail ---------------- ");
foreach (var item in list)
{
Console.WriteLine($"ID : {item.Id} , Name : {item.Name}");
}
Console.WriteLine($"------------------------");
来看下执行效果吧....
这里的实现让我想到了ABP数据仓储的实现,ABP为每个实体创建一个仓储对象,不需要手动一个个创建,可以查看我的ABP系列 => ABP 数据访问 - IRepository 仓储 ,可以参考ABP仓储管理的思想;大家也可以去看下
五、数据访问
好了,回到最初的实现思路上,前面的准备工作都做的差不多了,该正式跑一把数据了....
public static void Query_查询数据_全量查询()
{
var myDbContext = new MyDbContext();
var list = myDbContext.TestTables.ToList();
Console.WriteLine($"TestTable Count: {list.Count}");
if (!list.Any()) return;
Console.WriteLine($"TestTable Detail ---------------- ");
foreach (var item in list)
{
Console.WriteLine($"ID : {item.Id} , Name : {item.Name}");
}
Console.WriteLine($"------------------------");
}
通过控制台应用,执行上述方法,即可查询到TestTable中的数据
到此我们基本就开始使用EF Core,实现了数据访问;后续将开始对EF的其他使用持续进行分析,以及一些高阶应用;
示例代码地址:https://gitee.com/wuxingquema/knowledge-points
文章后面附上EFCore的源码地址,一起看源码,一起学习 https://github.com/dotnet/efcore
帮助博客园推广下:https://www.cnblogs.com/cmt/p/14003277.html