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 方法;微軟官方文檔地址://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的其他使用持續進行分析,以及一些高階應用;
文章後面附上EFCore的源碼地址,一起看源碼,一起學習 //github.com/dotnet/efcore
幫助部落格園推廣下://www.cnblogs.com/cmt/p/14003277.html