EF Core 二 、 入門 EF Core

五行缺碼發表於2020-11-20

入門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的其他使用持續進行分析,以及一些高階應用;

文章後面附上EFCore的原始碼地址,一起看原始碼,一起學習 https://github.com/dotnet/efcore
幫助部落格園推廣下:https://www.cnblogs.com/cmt/p/14003277.html

相關文章