SSRS IDE
~楓花雪岳~
星期三, 4月 09, 2025
[SSRS] Image - 刪除內嵌圖檔
SSRS Image 控件內嵌模式會把圖檔放進報表檔案內,報表檔案大小會增加,因此當內嵌圖檔有更新時都會注意把舊圖檔刪除,但每次都忘記去哪進行刪除,都直覺把報表檔案打開刪除舊圖檔相關內容
SSRS IDE
SSRS IDE
星期日, 3月 30, 2025
[EFCore] Cache
同事遇上 EF cache 造成結果不如預期, 寫份簡易範例來記錄並介紹
使用 Single 進行存取,目前該 entity 沒有 cache,所以會先放進 cache 內,再把 entity 往 AP 丟
Query2:理解 Cache 行為
更新 Query1 產生的 entity 資料,同時也更新資料庫內資料,藉此觀察使用 Single() 存取相同 entity 行為
Query3:AsNoTracking()
使用 AsNoTracking() 就可以跳過 cache,直接存取資料庫並回傳
Query4:Find()
當 cache 內有該 entity 時,使用 Find() 會直接回傳 cache 內 entity,不會有資料庫存取行為,沒有 TSQL 輸出,但 cache 內沒有該 entity 的話,才會去資料庫存取
C# 完整 Code
根據該筆記 [EFCore] LogTo 輸出 Linq 產生的 TSQL 語法,藉此來觀察是否發生資料庫存取,而輸出結果會稍微整理,以方便閱讀為主
using EFCoreCache.Models;
using Microsoft.EntityFrameworkCore;
namespace EFCoreCache
{
internal class Program
{
static void Main(string[] args)
{
AdventureWorks2022DbContext dbContext = new();
Console.WriteLine("----- Query1:第一次存取 Entity");
Console.WriteLine($"存取前 Person Entity Cache Count:{dbContext.Person.Local.Count}");
var query1 = dbContext.Person.SingleOrDefault(p => p.BusinessEntityID == 1);
Console.WriteLine($"存取後 Person Entity Cache Count:{dbContext.Person.Local.Count}");
Print(query1);
Console.WriteLine("----- Query2:理解 Cache 行為");
// 修正 Cache 內資料
query1.LastName = "LastNameFromCache";
// 直接更新資料庫內資料
dbContext.Person
.Where(w => w.BusinessEntityID == 1)
.ExecuteUpdate(e =>
e.SetProperty(sod => sod.LastName, "LastNameFromDB"));
var query2 = dbContext.Person.SingleOrDefault(p => p.BusinessEntityID == 1);
Print(query2);
Console.WriteLine("----- Query3:AsNoTracking()");
var query3 = dbContext.Person.AsNoTracking().SingleOrDefault(p => p.BusinessEntityID == 1);
Print(query3);
Console.WriteLine("----- Query4:Find()");
var query4 = dbContext.Person.Find(1);
Print(query4);
}
private static void Print(Person p)
{
Console.WriteLine($"內容輸出:{p.LastName}");
}
}
}
後續會分別解釋每個 Query
Query1:第一次存取 Entity
Console.WriteLine($"存取前 Person Entity Cache Count:{dbContext.Person.Local.Count}");
var query1 = dbContext.Person.SingleOrDefault(p => p.BusinessEntityID == 1);
Console.WriteLine($"存取後 Person Entity Cache Count:{dbContext.Person.Local.Count}");
Print(query1);
存取前 Person Entity Cache Count:0
SELECT TOP(2) * FROM [Person].[Person] AS [p] WHERE [p].[BusinessEntityID] = 1
存取後 Person Entity Cache Count:1
內容輸出:Sanchez
Query2:理解 Cache 行為
更新 Query1 產生的 entity 資料,同時也更新資料庫內資料,藉此觀察使用 Single() 存取相同 entity 行為
// 修正 Cache 內資料
query1.LastName = "LastNameFromCache";
// 直接更新資料庫內資料
dbContext.Person
.Where(w => w.BusinessEntityID == 1)
.ExecuteUpdate(e =>
e.SetProperty(p => p.LastName, "LastNameFromDB"));
var query2 = dbContext.Person.SingleOrDefault(p => p.BusinessEntityID == 1);
Print(query2);
使用 Single 存取相同的 entity,EFCore 還是會對資料庫進存取,但該資料目前已經在 cache 內,所以會把 cache 內 entity 往 AP 丟,內容輸出為 LastNameFromCache,在官方文章內有相關說明Queries are always executed against the database even if the entities returned in the result already exist in the context.
How Queries Work - The life of a query
3. For each item in the result set
a. If the query is a tracking query, EF checks if the data represents an entity already in the change tracker for the context instance
- If so, the existing entity is returned
- If not, a new entity is created, change tracking is set up, and the new entity is returned
b.If the query is a no-tracking query, then a new entity is always created and returned
另外 ExecuteUpdate 是不會有 cache 的,詳見 [EFCore] ExecuteUpdate 和 ExecuteDelete
UPDATE Person.Person SET LastName = 'LastNameFromDB' WHERE BusinessEntityID = 1
SELECT TOP(2) * FROM [Person].[Person] AS [p] WHERE [p].[BusinessEntityID] = 1
內容輸出:LastNameFromCache
使用 AsNoTracking() 就可以跳過 cache,直接存取資料庫並回傳
var query3 = dbContext.Person.AsNoTracking().SingleOrDefault(p => p.BusinessEntityID == 1);
Print(query3);
SELECT TOP(2) * FROM [Person].[Person] AS [p] WHERE [p].[BusinessEntityID] = 1
內容輸出:LastNameFromDB
Query4:Find()
當 cache 內有該 entity 時,使用 Find() 會直接回傳 cache 內 entity,不會有資料庫存取行為,沒有 TSQL 輸出,但 cache 內沒有該 entity 的話,才會去資料庫存取
var query4 = dbContext.Person.Find(1);
Print(query4);
內容輸出:LastNameFromCache以上範例雖然是以 EFCore 9 為記錄內容,但在 EF 6.5.1 也是相同行為
星期三, 3月 19, 2025
[EFCore] 陰影屬性
通常 EF Entity Property 會對應 Table Column,但透過陰影屬性 (Shadow Property) 可以讓該 EF Entity 沒有該 Property,但卻還是可以對應 Table Column。 適用場景在於 Table 常設的 CreateTime、ModifiedDate 欄位或是資料軟刪除欄位
C# Code
輸出結果觀察
設定 [EFCore] LogTo 來觀察 Shadow Property 所產生的 TSQL,以下有把輸出資訊簡化,方便閱讀
Shadow Property 設定
在 DbContext 內的 OnModelCreating 中透過 Fluent API 來進行設定
public partial class AdventureWorks2022DbContext : DbContext
{
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Person>()
.Property<DateTime>("ModifiedDate");
}
}
C# Code
namespace EFCoreShadowProperty
{
internal class Program
{
static void Main(string[] args)
{
string shadowPropertyName = "ModifiedDate";
var dbContext = new AdventureWorks2022DbContext();
Console.WriteLine("---------- 在 OrderBy 中使用 EF.Property 來抓取 Shadow Property");
var person = dbContext
.Person
.OrderBy(p => EF.Property<DateTime>(p, shadowPropertyName))
.FirstOrDefault();
// 取值並顯示
var modifyDate = dbContext.Entry(person).Property(shadowPropertyName).CurrentValue;
Console.WriteLine($"---------- 修改時間最早的資料:{person.LastName}-{person.FirstName}-{modifyDate}");
Console.WriteLine($"---------- 設定 Shadow Property 值並儲存");
// 設定值並儲存
DateTime now = DateTime.Now;
dbContext.Entry(person).Property(shadowPropertyName).CurrentValue = now;
dbContext.SaveChanges();
Console.WriteLine($"---------- 在 Where 中使用 EF.Property 來抓取 Shadow Property");
var updatePerson = dbContext
.Person
.Where(p => EF.Property<DateTime>(p, shadowPropertyName) == now)
.ToList();
}
}
}
輸出結果觀察
設定 [EFCore] LogTo 來觀察 Shadow Property 所產生的 TSQL,以下有把輸出資訊簡化,方便閱讀
---------- 在 OrderBy 中使用 EF.Property 來抓取 Shadow Property
SELECT TOP(1) 欄位名稱
FROM [Person].[Person] AS [p]
ORDER BY [p].[ModifiedDate]
---------- 修改時間最早的資料:Tamburello-Roberto-2007/11/4 上午 12:00:00
---------- 設定 Shadow Property 值並儲存
SET IMPLICIT_TRANSACTIONS OFF;
SET NOCOUNT ON;
UPDATE [Person].[Person] SET [ModifiedDate] = @p0
WHERE [BusinessEntityID] = @p1;
SELECT @@ROWCOUNT;
---------- 在 Where 中使用 EF.Property 來抓取 Shadow Property
SELECT 欄位名稱
FROM [Person].[Person] AS [p]
WHERE [p].[ModifiedDate] = @__now_0
星期六, 3月 15, 2025
[Azure] Notifications - Pull Request
在 Azure DevOps 上設定 PR 通知,當 PR 建立時可以發信通知該 Project 相關成員
Project Setting => Notifications => New subscription
Code (Git) => A pull request is created or updated
Project Setting => Notifications => New subscription
Code (Git) => A pull request is created or updated
subscription 相關設定,分別為
- Description:修改為方便辨識名稱
- Deliver to:選擇 Member of Project Team,預設是根據 Role
- Filter:A specific team project:選擇特定專案
Filter criteria 為發信條件,紀錄兩種觸發方式
- Event Type = Pull request created
- Status changes to Completed
兩者差異在於發出訊息內容,Pull request created 內會包含 Reviewer、檔案變更、Commits 資訊,Status changes to Completed 只有 Reviewer
下圖為官方文章 - Create an email subscription 發信條件
星期五, 3月 14, 2025
訂閱:
文章 (Atom)