1.EF基本搭建
EF也用了好幾年了,但是在日常開發的時候,有時候因為偷懶,有時候因為趕專案,很多代碼,多半就是Ctrl+C和Ctrl+V,慢慢的一些代碼怎么寫都忘記了,雖然覺得很簡單,但是就是記不起來怎么寫,逐漸退化,所以記錄一下,后續再賦值粘貼也好找一些,免得打開專案,
在此以.Net FramWork 控制臺搭建簡單的Demo使用的模式是DBFirst,個人覺得現在多半還是資料庫設計和代碼還是分開的,
1.創建資料庫和表,這里使用關聯表,一個主表一個從表,抽象出簡單的業務關系為一個員工持有哪些設備,而關系是一對多,
--創建資料庫
CREATE DATABASE EfDemo;
GO
USE EfDemo;
GO
--創建員工表
CREATE TABLE [dbo].[Employee]
([id] [INT] IDENTITY(1, 1) NOT NULL,
[Code] [NVARCHAR](20) NULL,
[Name] [NVARCHAR](20) NULL,
);
--創建設備表
CREATE TABLE [dbo].[Device]
([DeviceId] [INT] IDENTITY(1, 1) NOT NULL,
[id] [INT] NULL,
[DeviceName] [NVARCHAR](20) NULL
);
1.首先NuGet 安裝EntityFramework,至于什么版本看一下介紹,選擇對應框架的版本,我的是4.8安裝的EF版本是6.2

2.在組態檔中設定連接字串
<connectionStrings>
<add name="efConstr" connectionString="Data Source=192.168.0.106;Initial Catalog=EfDemo;User ID=sa;Password=sa@123456" providerName="System.Data.SqlClient" />
</connectionStrings>
3.創建員工物體類和設備物體類,并添加相關屬性,添加2個方法添加和洗掉Device資訊
[Table("Employee")] 表映射
[Key] 主鍵
[Required] 必填
[Column("Name")] 列名映射
[StringLength(1000)] 設定長度,如果是dbfirst記得與資料庫長度匹配
[Table("Employee")]
public class EmployeeEntity
{
public EmployeeEntity()
{
DeviceEntities = new HashSet<DeviceEntity>();
}
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
[Column(Order = 1)]
[Required]
public int id { get; set; }
//將資料庫欄位Name映射別名為"mingzi "
[Column("Name")]
public string mingzi { get; set; }
public string Code { get; set; }
public virtual ICollection<DeviceEntity> DeviceEntities { get; set; }
public void AddDevice(DeviceEntity entity)
{
this.DeviceEntities.Add(entity);
}
public void RemoveDevice(DeviceEntity entity)
{
this.DeviceEntities.Remove(entity);
}
}
[Table("Device")]
public class DeviceEntity
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public int DeviceId { get; set; }
public int id { get; set; }
public string DeviceName { get; set; }
}
4.創建UserContext背景關系,繼承自DbContext
public class UserContext: DbContext
{
public UserContext()
: base("name=efConstr")
{
}
public virtual DbSet<EmployeeEntity> EmployeeEntities { get; set; }
}
2.EF查詢
查詢員工和設備資料,為了查詢方便已經在物體模型上設定了,所以不需要連表查詢,直接根據員工查出關聯表的資料,我們想在程式中輸出ef執行的sql日志可以使用
userContext.Database.Log = sql => { Console.WriteLine(sql); };
1.使用Find即時 查詢id為1的資料,此時執行代碼是直接實時查詢資料庫,
using (UserContext userContext = new UserContext())
{
var result = userContext.EmployeeEntities.Find(1);
}
2.使用Where延時查詢 查詢id大于0的資料,在執行回圈之前此時還未提交到資料庫,得到的是一個IQueryable可以使用ToList()或者對IQueryable進行回圈就會直接提交到資料庫
但是不建議直接使用ToList
public static List<EmployeeDto> SerachEmployeeInfo()
{
List<EmployeeDto> employees = new List<EmployeeDto>();
using (UserContext userContext = new UserContext())
{
//是一個IQueryable,此時并不會提交到資料庫
var result = userContext.EmployeeEntities.Where(x => x.id > 0);
//雖然有多個但是只是拼接運算式,ToList時提交
result = result.Where(x => x.id == 2).ToList();
foreach (var item in result)
{
EmployeeDto employeeDto = new EmployeeDto();
employeeDto.Id = item.id;
employeeDto.Name = item.mingzi;
employeeDto.Code = item.Code;
employeeDto.DeviceEntities = item.DeviceEntities.ToList();
employees.Add(employeeDto);
}
}
return employees;
}
3.使用連接查詢,默認為內連,如果需要左連接就需要將2個合并插入一個新的表,然后使用DefaultIfEmpty() 定義可為空
public static List<EmployeeDto> SerachEmployeeInfoByJoin()
{
List<EmployeeDto> employees = new List<EmployeeDto>();
using (UserContext userContext = new UserContext())
{
List<int> ins = new List<int> { 2 };
//使用內連接查詢
var result = from u in userContext.EmployeeEntities
join c in userContext.DeviceEntities on u.id equals c.id
select new
{
Id = u.id,
Name = u.mingzi,
DeviceName = c.DeviceName
};
//使用左連接查詢
var result = from u in userContext.EmployeeEntities
join c in userContext.DeviceEntities on u.id equals c.id
into leftTable
from lt in leftTable.DefaultIfEmpty()
select new
{
Id = u.id,
Name = u.mingzi,
DeviceName = c.DeviceName
};
//提交到資料庫,并且遍歷投影出的新的資料
foreach (var item in result)
{}
}
return employees;
}
4.使用EF執行sql陳述句,適合比較復雜的sql陳述句,主要依靠ExecuteSqlCommand和SqlQuery,呼叫存盤程序也是同樣的道理
public static int SerachEmployeeInfoBySql ()
{
using (UserContext userContext = new UserContext())
{
DbContextTransaction trans = null;
try
{
trans = userContext.Database.BeginTransaction();
string sqlExecute = "Update Employee set Name ='EF測驗' WHERE Id=@Id";
//查詢陳述句
//string sqlQuery = "Select*from Employee WHERE Id=@Id"
SqlParameter parameter = new SqlParameter("@Id", 1);
//執行ExecuteSqlCommand增刪改
int executeResult = userContext.Database.ExecuteSqlCommand(sqlExecute, parameter);
//var result = userContext.Database.SqlQuery<EmployeeEntity>(sqlQuery, parameter).ToList<EmployeeEntity>();
trans.Commit();
return executeResult;
}
catch (Exception ex)
{
if (trans != null)
trans.Rollback();
throw ex;
}
finally
{
trans.Dispose();
}
}
}
3.EF增刪改使用
1.新增一筆資料,這里使用的是異步方法,其實同步方法一樣的道理,由于Employee和Device是主從關系,所以執行下面的代碼同時會向Device表也插入一條
- 1.如果在一個背景關系中再次將mingzi 改為"同一背景關系中再次賦值",那么就會修改前面插入的資料,因為在一個背景關系中會進行
資料跟蹤
static async Task AddEmployee()
{
using (UserContext userContext = new UserContext())
{
EmployeeEntity entity = new EmployeeEntity();
entity.mingzi = "新增資料1";
entity.Code = "A123";
//導航屬性插入值
entity.AddDevice(new DeviceEntity { DeviceName = "ipad" });
userContext.EmployeeEntities.Add(entity);
//執行SaveChangesAsync()資料才會提交
await userContext.SaveChangesAsync();
//同背景關系中再次賦值,就會修改mingzi的值
entity.mingzi = "同一背景關系中再次賦值";
await userContext.SaveChangesAsync();
}
return result;
}
2.使用EF修改資料,如果直接修改是無效的
- 2.1.在同一背景關系中修改,通常在作業中的做法是需要先把物件查詢出來,然后修改對應的值,
同一背景關系修改
static async Task UpdateEmployee()
{
using (UserContext userContext = new UserContext())
{
//先查詢在修改
var content = await userContext.EmployeeEntities.FindAsync(7);
content.mingzi = "被修改的值";
await userContext.SaveChangesAsync();
}
}
- 2.2.在不同背景關系中修改值,我們使用
Attach的目的就是把一個沒有被dbContext 跟蹤的物件附加到新的背景關系中,使其被跟蹤,必須先附加再賦值才能生效,如果先賦值那就需要在背景關系中將物件的狀態設定為EntityState.Modified
不同背景關系修改
static async Task AddEmployee()
{
EmployeeEntity entity = null;
using (UserContext userContext = new UserContext())
{
entity = new EmployeeEntity();
entity.mingzi = "新增資料1";
entity.Code = "A123";
entity.AddDevice(new DeviceEntity { DeviceName = "ipad1" });
userContext.EmployeeEntities.Add(entity);
await userContext.SaveChangesAsync();
}
using (UserContext userContext = new UserContext())
{
//先附加再修改
userContext.EmployeeEntities.Attach(entity);
entity.mingzi = "不同背景關系先附加再修改值";
await userContext.SaveChangesAsync();
//先修改再設定狀態為Modified
entity.mingzi = "不同背景關系先修改值再設定狀態";
userContext.Entry<EmployeeEntity>(entity).State = EntityState.Modified;
await userContext.SaveChangesAsync();
}
}
- 2.3.按照上面的方式,EF在執行時生成的sql會將所有的欄位都更新一遍,我們可以設定只對某個
屬性欄位更新,在執行sql時只會更新那一個欄位,前提需要對物件進行Attach,然后再設定欄位狀態,
using (UserContext userContext = new UserContext())
{
entity.mingzi = "不同背景關系修改值";
userContext.EmployeeEntities.Attach(entity);
//通知context mingzi屬性被修改
userContext.Entry<EmployeeEntity>(entity).Property<string>("mingzi").IsModified = true;
await userContext.SaveChangesAsync();
}
3.洗掉資料的方式和修改類似,通用的是查詢出來然后是使用Remove,如果不同背景關系,我們需要Attach或者將狀態更改為EntityState.Deleted
static async Task removeEmployee(int id)
{
using (UserContext userContext = new UserContext())
{
var emp = userContext.EmployeeEntities.Where(x => x.id == id).FirstOrDefault();
userContext.EmployeeEntities.Remove(emp);
await userContext.SaveChangesAsync();
}
}
- 3.1.不同背景關系洗掉使用
附加或者設定狀態
static async Task removeEmployee(int id)
{
EmployeeEntity entity = null;
using (UserContext userContext = new UserContext())
{
entity = userContext.EmployeeEntities.Where(x => x.id == id).FirstOrDefault();
}
using (UserContext userContext = new UserContext())
{
//設定物體狀態為洗掉
// userContext.Entry(entity).State = EntityState.Deleted;
//附加到當前背景關系
userContext.EmployeeEntities.Attach(entity);
//執行洗掉
userContext.EmployeeEntities.Remove(entity);
await userContext.SaveChangesAsync();
}
}
4.Context背景關系
1.下面在一個背景關系里面執行多個操作,一次SaveChanges()是保存全部的變化,假如中間有一個失敗,那所有的都將失敗,可以理解到SaveChanges是事務的結束,所以不能全域使用
static void UpdateEmployee()
{
using (UserContext userContext = new UserContext())
{
try
{
var content = userContext.EmployeeEntities.Find(22);
content.mingzi = "一個背景關系修改第一個值";
var content1 = userContext.EmployeeEntities.Find(23);
content1.mingzi = "一個背景關系修改第二個值撒啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊撒啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊";
userContext.SaveChanges();
}
catch (Exception ex) { throw ex; }
}
}
2.不同的Context不能連接查詢,除非load到記憶體之后再去操作,如果是多執行緒或者多個請求,最好多個Context

5.查詢本地快取
1.在EF中查詢是有快取的,例如下面在使用Where時每一次都會去資料庫查詢,但是使用Find時,他會現在本地記憶體中查找一次,如果前面查詢有結果,那就直接使用,那么就有可能產生臟讀,但是對性能提升有幫助,使用時根據自身需求而定,
using (UserContext userContext = new UserContext())
{
var result = userContext.EmployeeEntities.Where(x => x.id < 3).ToList();
var result1 = userContext.EmployeeEntities.Where(x => x.id ==2).ToList();
var result2 = userContext.EmployeeEntities.Find(1);
var result3 = userContext.EmployeeEntities.Where(x => x.id == 1).ToList();
}
2.如果不希望查詢出的結果在記憶體中快取,可以使用AsNoTracking不在記憶體中拷貝副本,直接回傳,就算后續使用Find也不會讀取記憶體
using (UserContext userContext = new UserContext())
{
var result = userContext.EmployeeEntities.Where(x => x.id < 3).AsNoTracking().ToList();
}
``
###### 6.導航屬性以及延遲加載
1.主從查詢丟棄子表查詢,在背景關系中使用`LazyLoadingEnabled =fasle`
```cs
userContext.Configuration.LazyLoadingEnabled = false;
2.使用Include,不延遲加載將資料一次性加載出來
using (UserContext userContext = new UserContext())
{
var result = userContext.EmployeeEntities.Include("DeviceEntities").Where(x=>x.id>0);
foreach (var item in result)
{
EmployeeDto employeeDto = new EmployeeDto();
employeeDto.Id = item.id;
employeeDto.Name = item.mingzi;
employeeDto.Code = item.Code;
employeeDto.DeviceEntities = item.DeviceEntities.ToList();
employees.Add(employeeDto);
}
}
3.主從表的級聯洗掉,前提是需要修改資料庫外鍵的洗掉規則為級聯
using (UserContext userContext = new UserContext())
{
entity = userContext.EmployeeEntities.Where(x => x.id == id).FirstOrDefault();
userContext.EmployeeEntities.Remove(entity);
userContext.SaveChanges();
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/455359.html
標籤:.NET技术
