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
星期四, 3月 13, 2025
[Outlook] 轉寄
在 Outlook 內設定,要把收到信件轉寄到指定信箱去
設定 => 郵件 => 轉寄 => 啟用轉寄並輸入信箱,這樣寄到 Outlook 信箱的信件就會轉寄到指定信箱,另外也可以透過設定規則,符合規則才進行轉寄
星期二, 3月 11, 2025
[EFCore] LogTo
根據官方文章 Simple Logging 內容,要在 Console 上顯示 Linq to Entity 產生的 TSQL 語法。
只要在 DbContext.OnConfiguring() 內進行 LogTo 設定,語法如下
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.UseSqlServer(連線字串)
.LogTo(Console.WriteLine , LogLevel.Information);
// 或
.LogTo(message => Console.WriteLine(message) , LogLevel.Information);
萬一沒有指定 LogLevel.Infomation 會輸出過多訊息,變得不容易找到 TSQL 語法
info: 2025/3/11 08:41:31.472 RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command) Executed DbCommand (12ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] SELECT [p].[BusinessEntityID], [p].[AdditionalContactInfo], [p].[Demographics], [p].[EmailPromotion], [p].[FirstName], [p].[LastName], [p].[MiddleName], [p].[ModifiedDate], [p].[NameStyle], [p].[PersonType], [p].[Suffix], [p].[Title], [p].[rowguid] FROM [Person].[Person] AS [p]文章內還其他輸出模式、顯示機密資料和篩選資訊相關可以參考。
星期一, 3月 10, 2025
[C#] String.Insert()
閱讀 C# 字串相關函數時,突然發現有 Insert() 可以對字串進行文字插入,根據 官方文章 用 LinqPad 筆記一下
void Main()
{
string animal_fox = "fox";
string animal_dog = "dog";
string target = $"The {animal_fox} jumps over the {animal_dog}";
string strInsert = target.Insert(target.IndexOf(animal_fox), "bold ");
target.Dump("Insert() 不會更改原本字串內容");
strInsert.Dump("Insert() 會返回一個新字串");
strInsert = strInsert.Insert(strInsert.IndexOf(animal_dog), "lazy ");
strInsert.Dump("對原字串進行 insert");
strInsert = strInsert.Insert(strInsert.Length, ".");
strInsert.Dump("當 StartIndex 參數等同於字串長度,會直接把內容附加字串最後面");
}
星期日, 3月 09, 2025
[EF] StartsWith 轉 TSQL 語法
看見 EFCore 範例使用 C# StartsWith 轉出的 TSQL 是 LIKE '前綴文字%',但在 EF 時代印象中轉出的 TSQL 會使用 substring() 才是,驗證看看
從 EF 時代就誤會到 EFCore
using System.Linq;
namespace EFLike
{
class Program
{
static void Main(string[] args)
{
var dbContext = new AdventureWorks2022DbContext();
var data = dbContext.Person
.Where(p => p.LastName.StartsWith("A"))
.ToList();
}
}
}
SQL Profile 側錄結果從 EF 時代就誤會到 EFCore
星期一, 3月 03, 2025
[VS] 格式化 - 忽略宣告陳述式中的空格
在 Line 群組看人家討論時,常常會看見宣告變數時,會使用空白排版,沒想到現在 VS2022 內有這選項,預設是沒有啟用,開啟路徑為工具 => 選項 => 文字編輯器 => C# => 程式碼樣式 => 格式化 => 間距 => 忽略宣告陳述式中的空格
星期六, 2月 15, 2025
[RV] 執行操作時發生錯誤
同事有設計一款非同步進度條,當該功能需要長時間操作時,可以讓使用者意識到系統正在跑,而不是系統當掉,ReportViewer Render 報表時常常需要點時間,所以把 ReportViewer 當成 callback 塞進去時,效果都如預期,但是當按下 ReportViewer 列印按鈕時,會拋出該錯誤訊息,在該 stackoverflow 討論內也有人遇上相同情況,最後只好先不要進度條功能,維持現況囉
正常點選列印按鈕會出現印表機選項視窗
進度條效果非同步 Code
public async Task PrograssBarShow(Action action, int second = 60)
{
await Task.Factory.StartNew(() =>
{
_prograssBar.Show(this, second);
action();
_prograssBar.Close();
});
}
星期五, 2月 14, 2025
[SSRS] DataSource 重覆,造成無法開啟資料集功能
開發 SSRS 報表時,要新增資料集時,VS 突然拋出錯誤訊息,訊息如下
詳細錯誤訊息
簡單測試後發現新開 Project 或是現有其他 Project 去新增報表或修改現有報表都可以正常開啟資料集 GUI 畫面,只有該 Project 上會拋出該錯誤訊息,同事在版控上發現某個 commit 開始就有該現象,還剛好是我 commit 的,從該 commit change 沒有發現問題,完全沒有頭緒。
如需叫用 Just-In-Time (JIT) 偵錯的詳細資料, 請參閱本訊息結尾處 (而非這個對話方塊) 的資訊。************** 例外狀況文字 **************System.NullReferenceException: 並未將物件參考設定為物件的執行個體。於 Microsoft.ReportDesigner.Design.GenericObjectDataSource.InitializeVB(ISchemaType schemaType) 於 Microsoft.ReportDesigner.Design.DesignUtil.AddTablesFromSchema(ISchemaType schemaType, Type parentType, String propertyName, IDictionary objectDataSources, IServiceProvider serviceProvider) 於 Microsoft.ReportDesigner.Design.DesignUtil.GetVBObjectDataSources(XmlSchema[] schemaDataSources, IServiceProvider serviceProvider) 於 Microsoft.ReportDesigner.Data.Local.DataSetSynchronizer.<GetProjectGenericObjectDataSources>d__11.MoveNext() 於 Microsoft.ReportDesigner.Data.Local.DataSetSynchronizer.<GetSynchableDataSources>d__7.MoveNext() 於 Microsoft.ReportDesigner.Data.Local.Dialogs.Pages.DataSetGeneralPage.LoadDataSources() 於 Microsoft.ReportDesigner.Data.Local.Dialogs.Pages.DataSetGeneralPage.LoadData() 於 Microsoft.ReportDesigner.Data.Local.Dialogs.DataSetDialog.OnLoad(EventArgs e) 於 System.Windows.Forms.Form.OnCreateControl() 於 System.Windows.Forms.Control.CreateControl(Boolean fIgnoreVisible) 於 System.Windows.Forms.Control.CreateControl() 於 System.Windows.Forms.Control.WmShowWindow(Message& m) 於 System.Windows.Forms.Control.WndProc(Message& m) 於 System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
簡單測試後發現新開 Project 或是現有其他 Project 去新增報表或修改現有報表都可以正常開啟資料集 GUI 畫面,只有該 Project 上會拋出該錯誤訊息,同事在版控上發現某個 commit 開始就有該現象,還剛好是我 commit 的,從該 commit change 沒有發現問題,完全沒有頭緒。
洗澡時突然想到,不會是報表更名造成的吧,查看資料夾內實際檔案發現,該報表使用的 DataSource 有兩個,實際檔案名稱雖然有差異,但各個 DataSource 內容只有檔案名稱大小寫差異,把更名前的 DataSource 刪除後,資料集 GUI 就完全恢復正常。
重覆測試後確定報表名稱更名,並不會直接影響 DataSource,也確認在報表內刪除 DataSource 並不會直接刪除實體 DataSource 檔案,直接刪除 Project 內的 DataSource 才會刪除實體 DataSource 檔案,猜測是該 DataSource 可能因為版控關係,該檔案不在 Project 內,又新增同名 DataSource 造成。
該 commit 是一個月前,期間也經歷三張報表開發,沒想到就突然異常。
星期二, 2月 11, 2025
[CCNA] IPv6 - 靜態路由
參考該篇 [CCNA] 靜態路由 內容,但 IP 設定改以 IPv6 為主
網路拓樸圖
設定前必須先執行下列語法,參考 Cisco ipv6 unicast-routing 說明
顯示 ipv6 網路卡設定
Router1 依上述步驟進行設定,一定要開啟 ipv6 unicast-routing
設定靜態路由
Client 端設設定 IPv6
Ping 測試
ipv6 unicast-routing
To enable the forwarding of IPv6 unicast datagrams, use the ipv6 unicast-routing command in global configuration mode. To disable the forwarding of IPv6 unicast datagrams, use the no form of this command.
顯示 ipv6 網路卡設定
Router1 依上述步驟進行設定,一定要開啟 ipv6 unicast-routing
設定靜態路由
Client 端設設定 IPv6
Ping 測試
星期一, 1月 13, 2025
GodexRT 730X - 特定區域空白
使用者回報告知,標籤輸出內容都不完整,而且是特定區域無法正常顯示而已,標籤如下
打開標籤機發現感熱部分竟然有一層膠,看起來是碳帶黏在熱感上,該位置剛好跟標籤紙無法顯示位置相同,用指甲把膠刮下來後,列印就會恢復正常
打開標籤機發現感熱部分竟然有一層膠,看起來是碳帶黏在熱感上,該位置剛好跟標籤紙無法顯示位置相同,用指甲把膠刮下來後,列印就會恢復正常
星期五, 1月 03, 2025
[CCNA] Port Security
使用 mac address 來確定該電腦是否能夠存取 switch port,該筆記內容是以只允許一個 mac address 連線來記錄
網路拓樸圖
設定 switch IP 方便進行測試
設定 fa0/1 為 Port Security 時,要先切換至 access 模式,要不然會拋出下圖錯誤訊息
Port Security 設定步驟
使用 show port security interface 來查看 Port Security 相關設定,以下圖來說,上方為設定完成後,還沒有任何連線狀態,下方為已記錄一個 mac address
show port-security 語法的 Last Source Address:Vlan 資料分別為紀錄的 mac address 和 vlan 號碼,從 PC0 截圖可以看到 mac address 是一致的
Port Security 測試
因為 switch 已經紀錄 PC0 mac address,當 Laptop0 ping siwtch 時,超過設定只允許記錄一個 mac address,該 Port 會立即 shutdown,如要在 CLI 內重啟該 Port,要先下 shutdown 後 no shutdown,直接下 no shutdown 是沒有作用的
show port-security 顯示已開啟 Port Security 的相關資訊
確認 Port Security 已經關閉
Port Security 設定
設定 fa0/1 為 Port Security 時,要先切換至 access 模式,要不然會拋出下圖錯誤訊息
- 設定 access 模式
- 允許記錄一個 mac address
- 使用 sticky 模式紀錄 mac address
- violation 模式為 shutdown 為預設值,以該筆記來說即為紀錄第二個 mac address 時會把 Port 關閉
show port-security 語法的 Last Source Address:Vlan 資料分別為紀錄的 mac address 和 vlan 號碼,從 PC0 截圖可以看到 mac address 是一致的
因為 switch 已經紀錄 PC0 mac address,當 Laptop0 ping siwtch 時,超過設定只允許記錄一個 mac address,該 Port 會立即 shutdown,如要在 CLI 內重啟該 Port,要先下 shutdown 後 no shutdown,直接下 no shutdown 是沒有作用的
show port-security 顯示已開啟 Port Security 的相關資訊
關閉 Port Security
使用 no switchport port-security 來關閉,關閉後必須重啟該 Port
關閉後兩台設備就同時可以通啦
- 參考資料
- 實作Switch的Port Security
- 網路規劃與管理實務 21-3、21-4、21-5 章節
- Port Security 端口安全
- [筆記]Cisco基本指令-Port Security