有一段时间没有更新博客了, 赶上今天外面下雨, 而且没人约球, 打算把最近对 Entity Framework DBContext 使用的心得梳理一下, 早些时候在网上简单查过, 对于最新版本的 EF 并没有类似的知识梳理类文章, 希望对大家有所帮助.
1. 不要 Code first, 也不要 DB first
我为什么讨厌 Code first 和 DB first 呢? 首先 Code first 是先写代码, 数据库完全由代码生成, 开发阶段尚可, 一旦到了产品发布阶段, 如果需要添加字段, 我们总不能用 visual studio 去生产环境上去更新数据库吧, 听起来就很可怕. 而且另外的一个问题自动是生成的数据库脚本也不可控, 还不如自己提前设计好. DB first 也好不了哪去, 反向转过来的代码包含很多没有用的文件, 而且数据库的更新还要重新走 Model 生成过程, 简直无法理解为什么会有这样的设计. 说了这么多, 怎么解决呢?
数据库和领域模型分开设计, 按照对应关系映射字段, 使用自定义链接字串, 既不使用领域模型生成数据库, 也不用数据库生成领域模型, 示例代码如下, SQL Code 以 Destinations 和 TTable 表为例:
- CREATE TABLE [DBO].[Destinations]
- (
- [DestinationId] [int] PRIMARY KEY NOT NULL,
- [Name] [nvarchar](max) NULL,
- [Country] [nvarchar](max) NULL,
- [Description] [nvarchar](max) NULL,
- [Photo] [varbinary](max) NULL
- )
- CREATE TABLE [TTT].[TTable]
- (
- [Id] [int] PRIMARY KEY
- NOT NULL,
- [Name] [nvarchar](max) NULL
- )
- Model Class:
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- namespace Model
- {
- public class Destination
- {
- public int DestinationId { get; set; }
- public string Name { get; set; }
- public string Country { get; set; }
- public string Description { get; set; }
- public byte[] Photo { get; set; }
- public List<Lodging> Lodgings { get; set; }
- }
- public class Lodging
- {
- public int LodgingId { get; set; }
- public string Name { get; set; }
- public string Owner { get; set; }
- public bool IsResort { get; set; }
- public Destination Destination { get; set; }
- }
- public class TTable
- {
- public int Id { get; set; }
- public string Name { get; set; }
- }
- }
- Connect String:
- <connectionStrings>
- <add name="BAContext" connectionString="Data Source=(LocalDb)\MSSQLLocalDB;Initial Catalog=DataAccess.BreakAwayContext;Integrated Security=SSPI;" providerName="System.Data.SqlClient" />
- </connectionStrings>
- DB Context:
- using System.Data.Entity;
- using System.Data.Entity.ModelConfiguration;
- using Model;
- namespace DataAccess
- {
- public class TTableConfiguration : EntityTypeConfiguration<TTable>
- {
- public TTableConfiguration()
- {
- this.ToTable("TTable", "TTT");
- }
- }
- public class BreakAwayContext : DbContext
- {
- protected override void OnModelCreating(DbModelBuilder modelBuilder)
- {
- modelBuilder.Configurations.Add(new TTableConfiguration());
- }
- public BreakAwayContext(string connString) : base(connString)
- {
- }
- public DbSet<Destination> Destinations { get; set; }
- public DbSet<Lodging> Lodgings { get; set; }
- public DbSet<TTable> TTables { get; set; }
- }
- }
2. 如果数据库的表的字段和领域模型的字段不对应, 如何处理呢? 比如本文的 TTable 表是在 TTT Schema 下面的, 而其他表示设计在 DBO 下面, 最方便的方式是使用 fluent API, 具体代码如请参见 TTableConfiguration Class 和 OnModelCreating() 方法, 可配置的粒度非常细, 比如可以配置领域模型和数据库的哪个 Schema 的哪张表的哪一列对应, 本文是将 TTable 类的数据库表配置为了 TTT Schema 下的 TTable 表,
- public class TTableConfiguration : EntityTypeConfiguration<TTable>
- {
- public TTableConfiguration()
- {
- this.ToTable("TTable", "TTT");
- }
- }
3. 增删该查自带事物支持, 具体代码如下,
- public static int Insert()
- {
- var destination = new Destination
- {
- Country = "Chs",
- Description = "Chs is the language package",
- Name = "xsss"
- };
- using (var context = new BreakAwayContext(ConfigurationManager.ConnectionStrings["BAContext"].ConnectionString))
- {
- var rt = context.Destinations.Add(destination);
- context.SaveChanges();
- return rt.DestinationId;
- }
- }
- public static void Update(Destination destIn)
- {
- using (var context = new BreakAwayContext(ConfigurationManager.ConnectionStrings["BAContext"].ConnectionString))
- {
- var dest = context.Destinations.Where(a => a.DestinationId == destIn.DestinationId).Single();
- dest.Name = destIn.Name;
- context.SaveChanges();
- }
- }
- public static void Delete(int destId)
- {
- using (var context = new BreakAwayContext(ConfigurationManager.ConnectionStrings["BAContext"].ConnectionString))
- {
- var destination = new Destination() { DestinationId = destId };
- context.Destinations.Attach(destination);
- context.Destinations.Remove(destination);
- context.SaveChanges();
- }
- }
- public static Destination Query(int destId)
- {
- using (var context = new BreakAwayContext(ConfigurationManager.ConnectionStrings["BAContext"].ConnectionString))
- {
- IQueryable<Destination> dest = context.Destinations.Where(a => a.DestinationId == destId);
- return dest.Single();
- }
- }
4. 如果需要多个操作同时成功或者失败, 需要手动开启事务, 具体代码如下,
- public static void TransactionOps()
- {
- using (var context = new BreakAwayContext(ConfigurationManager.ConnectionStrings["BAContext"].ConnectionString))
- {
- using (var dbContextTransaction = context.Database.BeginTransaction())
- {
- try
- {
- var destination = new Destination
- {
- Country = "Chs",
- Description = "Chs is the language package",
- Name = "xs2s"
- };
- var destId = context.Destinations.Add(destination);
- context.SaveChanges();
- context.Destinations.Attach(destId);
- context.Destinations.Remove(destId);
- context.SaveChanges();
- dbContextTransaction.Commit();
- }
- catch (System.Exception ex)
- {
- dbContextTransaction.Rollback();
- System.Console.WriteLine(ex.ToString());
- }
- }
- }
- }
5. 分页查询是网站设计的常用功能, 一个简单的真分页查询方法如下如下所示,
- public static List<Destination> QueryPaging<TKey>(int pageIndex, int pageSize, Expression<Func<Destination, bool>> whereLambda, Expression<Func<Destination, TKey>> orderBy)
- {
- using (var context = new BreakAwayContext(ConfigurationManager.ConnectionStrings["BAContext"].ConnectionString))
- {
- return context.Destinations.Where(whereLambda).OrderBy(orderBy).Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();
- }
- }
总结
本文对最新版本的 Entity Framework 进行增删改查操作给出了详尽的解释, 并且给出了数据库和领域模型代码分开设计的完整解决方案, 同时介绍了手动数据库表和领域模型映射, 数据库事务实现, 分页查询等常用功能, 希望对大家有所帮助.
来源: https://www.cnblogs.com/pugang/p/8909414.html