有開 DoubleBuffered
沒有開 DoubleBuffered,每次卷軸往下就可以看出畫面閃爍
不知道是文件太舊還是對於內容有所誤會,開啟 DoubleBuffered 效果還蠻顯著
The DataGridView control does not support double buffering. If DoubleBuffered is set to true in a derived DataGridView control, users will not receive visual feedback when resizing rows, columns, or headers or when reordering columns.所在環境也是有自訂 DataGridView 並開啟 DoubleBuffered 來使用,一直以來沒有出現大問題,測試理解何謂 visual feedback 效果
using System.Windows.Forms;
namespace AvoidDoubleBuffer
{
public class UCDataGridView : DataGridView
{
public UCDataGridView()
{
DoubleBuffered = true;
}
}
}
string TSQL10 = @"
SELECT
P.ProductID ,
P.[Name] ,
POD.OrderQty ,
POD.UnitPrice ,
POD.LineTotal
FROM [Purchasing].[PurchaseOrderHeader] AS POH
JOIN [Purchasing].[PurchaseOrderDetail] AS POD ON POH.PurchaseOrderID = POD.PurchaseOrderID
JOIN Production.[Product] AS P ON POD.ProductID = P.ProductID
";
var result10 = dbContext.Database
.SqlQueryRaw<PurchaseOrderDetailViewModel>(TSQL10);
var result = await result10.ToListAsync();
Console.WriteLine(result.Count().ToString());
// TSQL 語法正常輸出
// SELECT
// P.ProductID ,
// P.[Name] ,
// POD.OrderQty ,
// POD.UnitPrice ,
// POD.LineTotal
// FROM[Purchasing].[PurchaseOrderHeader] AS POH
// JOIN[Purchasing].[PurchaseOrderDetail] AS POD ON POH.PurchaseOrderID = POD.PurchaseOrderID
// JOIN Production.[Product] AS P ON POD.ProductID = P.ProductID
執行後會拋出該錯誤,原來 SqlQueryRaw 底層是 FromSql 去執行的,所以每個 Property 都要有值才行,在 TSQL pass 但卡在 EFCore 上。Unhandled exception. System.InvalidOperationException: The required column 'ProductName' was not present in the results of a 'FromSql' operation.
string TSQL11 = @"
SELECT
P.ProductID ,
P.[Name] ,
POD.OrderQty ,
POD.UnitPrice ,
POD.LineTotal
FROM [Purchasing].[PurchaseOrderHeader] AS POH
JOIN [Purchasing].[PurchaseOrderDetail] AS POD ON POH.PurchaseOrderID = POD.PurchaseOrderID
JOIN Production.[Product] AS P ON POD.ProductID = P.ProductID
";
var result11 = dbContext.Database
.SqlQueryRaw<PurchaseOrderDetailViewModel>(TSQL11)
.Where(vw => vw.ProductID == productID);
var result = await result11.ToListAsync();
Console.WriteLine(result.Count().ToString());
產生 TSQL
exec sp_executesql N'
SELECT
[e].[LineTotal],
[e].[OrderQty],
[e].[ProductID],
[e].[ProductName], -- 外層為 ProductName
[e].[UnitPrice]
FROM
(
SELECT
P.ProductID,
P.[Name], -- 內層為 Name
POD.OrderQty,
POD.UnitPrice,
POD.LineTotal
FROM[Purchasing].[PurchaseOrderHeader] AS POH
JOIN[Purchasing].[PurchaseOrderDetail] AS POD ON POH.PurchaseOrderID = POD.PurchaseOrderID
JOIN Production.[Product] AS P ON POD.ProductID = P.ProductID
) AS[e]
WHERE[e].[ProductID] = @__productID_1',
N'@__productID_1 int',@__productID_1=1
執行後會拋出該錯誤,從 TSQL 語法可以觀察到,LINQ WHERE 條件,會根據 PurchaseOrderDetailViewModel Property 轉成對應 TSQL 欄位,因為 TSQL 內層沒有把 Name 轉成 ProductName,導致外層抓不到 ProductName。Unhandled exception. Microsoft.Data.SqlClient.SqlException (0x80131904): 無效的資料行名稱 'ProductName'。
namespace EFCoreQuery.Models
{
public class PurchaseOrderDetailViewModel
{
public int ProductID { get; set; }
/// <summary>
/// 把 Name 轉為 ProductName
/// </summary>
[Column("Name")]
public string ProductName { get; set; }
public Int16 OrderQty { get; set; }
public decimal UnitPrice { get; set; }
public decimal LineTotal { get; set; }
}
}
CASE 1:沒有任何 where 條件
string TSQL13 = @"
SELECT
P.ProductID ,
P.[Name] ,
POD.OrderQty ,
POD.UnitPrice ,
POD.LineTotal
FROM [Purchasing].[PurchaseOrderHeader] AS POH
JOIN [Purchasing].[PurchaseOrderDetail] AS POD ON POH.PurchaseOrderID = POD.PurchaseOrderID
JOIN Production.[Product] AS P ON POD.ProductID = P.ProductID
";
var result13 = dbContext.Database
.SqlQueryRaw<PurchaseOrderDetailViewModel>(TSQL13);
var result = await result13.ToListAsync();
Console.WriteLine(result.Count().ToString());
// TSQL 語法正常輸出
// SELECT
// P.ProductID ,
// P.[Name] ,
// POD.OrderQty ,
// POD.UnitPrice ,
// POD.LineTotal
// FROM[Purchasing].[PurchaseOrderHeader] AS POH
// JOIN[Purchasing].[PurchaseOrderDetail] AS POD ON POH.PurchaseOrderID = POD.PurchaseOrderID
// JOIN Production.[Product] AS P ON POD.ProductID = P.ProductID
正常執行,沒有拋出 FromSql ProductName 沒有值的錯誤,從中斷點去觀察,也發現值有塞進去,TSQL Name 欄位和 PurchaseOrderDetailViewModel ProudctName 屬性會自動對應。int productID = 1;
string TSQL14 = @"
SELECT
P.ProductID ,
P.[Name] ,
POD.OrderQty ,
POD.UnitPrice ,
POD.LineTotal
FROM [Purchasing].[PurchaseOrderHeader] AS POH
JOIN [Purchasing].[PurchaseOrderDetail] AS POD ON POH.PurchaseOrderID = POD.PurchaseOrderID
JOIN Production.[Product] AS P ON POD.ProductID = P.ProductID
";
var result14 = dbContext.Database
.SqlQueryRaw<PurchaseOrderDetailViewModel>(TSQL14)
.Where(vw => vw.ProductID == productID);
var result = await result14.ToListAsync();
Console.WriteLine(result.Count().ToString());
產生 TSQL
exec sp_executesql N'
SELECT
[e].[LineTotal],
[e].[OrderQty],
[e].[ProductID],
[e].[Name], -- 外層為 Name
[e].[UnitPrice]
FROM
(
SELECT
P.ProductID,
P.[Name], -- 內層為 Name
POD.OrderQty,
POD.UnitPrice,
POD.LineTotal
FROM[Purchasing].[PurchaseOrderHeader] AS POH
JOIN[Purchasing].[PurchaseOrderDetail] AS POD ON POH.PurchaseOrderID = POD.PurchaseOrderID
JOIN Production.[Product] AS P ON POD.ProductID = P.ProductID
) AS[e]
WHERE[e].[ProductID] = @__productID_1',
N'@__productID_1 int',@__productID_1=1
外層 TSQL 產生 Name 而不是 ProductNamenamespace EFCoreQuery.Models
{
public class PurchaseOrderDetailViewModel
{
public int ProductID { get; set; }
/// <summary>
/// 把 Name 轉為 ProductName
/// </summary>
public string ProductName { get; set; }
public Int16 OrderQty { get; set; }
public decimal UnitPrice { get; set; }
public decimal LineTotal { get; set; }
}
}
EFCore 7.0 實際測試namespace EFCoreQuery
{
internal class Program
{
static async Task Main(string[] args)
{
using var dbContext = new AdventureWorks2022Context();
// CASE 1:使用 EFCore 7.0,只能回傳 Scalar Value
var productID = new SqlParameter("@ProductID", SqlDbType.Int);
productID.Value = 403;
var result6 = await dbContext.Database.SqlQuery<int>(
$"SELECT ProductID FROM Production.[Product] WHERE ProductID = {productID}")
.ToListAsync();
Console.WriteLine(result6.Count().ToString());
// TSQL:exec sp_executesql
// N'SELECT ProductID FROM Production.[Product] WHERE ProductID = @ProductID',
// N'@ProductID int',@ProductID=403
// CASE 2:EFCore 7.0 不支援回傳 unmapped types
var purchaseOrderID = new SqlParameter("@PurchaseOrderID", SqlDbType.SmallInt);
purchaseOrderID.Value = 8;
string TSQL7 = @"
SELECT
P.ProductID ,
P.[Name] AS ProductName ,
POD.OrderQty ,
POD.UnitPrice ,
POD.LineTotal
FROM [Purchasing].[PurchaseOrderDetail] AS POD
JOIN Production.[Product] AS P ON POD.ProductID = P.ProductID
WHERE POD.PurchaseOrderID = @PurchaseOrderID
";
var result7 = await dbContext.Database.SqlQueryRaw<PurchaseOrderDetailViewModel>(TSQL7, purchaseOrderID).ToListAsync();
Console.WriteLine(result7.Count().ToString());
// EFCore 7.0 會拋出下列錯誤,EFCore 8.0 才能正常執行
// Unhandled exception. System.InvalidOperationException:
// The element type 'EFCoreQuery.Models.PurchaseOrderDetailViewModel' used in 'SqlQuery' method is not natively supported by your database provider.
// Either use a supported element type, or use ModelConfigurationBuilder.DefaultTypeMapping to define a mapping for your type.
}
}
}
EFCore 8.0 實際測試
namespace EFCoreQuery
{
internal class Program
{
static async Task Main(string[] args)
{
using var dbContext = new AdventureWorks2022Context();
// CASE 1::EFCore 8.0 支援 unmapped types
var orderDate = new SqlParameter("@OrderDate", SqlDbType.DateTime);
orderDate.Value = new DateTime(2014, 1, 1);
FormattableString TSQL8 = $@"
SELECT
P.ProductID ,
P.[Name] AS ProductName ,
POD.OrderQty ,
POD.UnitPrice ,
POD.LineTotal
FROM [Purchasing].[PurchaseOrderHeader] AS POH
JOIN [Purchasing].[PurchaseOrderDetail] AS POD ON POH.PurchaseOrderID = POD.PurchaseOrderID
JOIN Production.[Product] AS P ON POD.ProductID = P.ProductID
WHERE POH.OrderDate >= {orderDate}
";
var result8 = await dbContext.Database
.SqlQuery<PurchaseOrderDetailViewModel>(TSQL8)
.Where(vw => vw.UnitPrice > 80)
.ToListAsync();
Console.WriteLine(result8.Count().ToString());
// TSQL:exec sp_executesql
// N'SELECT [e].[LineTotal], [e].[OrderQty], [e].[ProductID], [e].[ProductName], [e].[UnitPrice]
// FROM(
// SELECT
// P.ProductID,
// P.[Name] AS ProductName,
// POD.OrderQty,
// POD.UnitPrice,
// POD.LineTotal
// FROM[Purchasing].[PurchaseOrderHeader] AS POH
// JOIN[Purchasing].[PurchaseOrderDetail] AS POD ON POH.PurchaseOrderID = POD.PurchaseOrderID
// JOIN Production.[Product] AS P ON POD.ProductID = P.ProductID
// WHERE POH.OrderDate >= @OrderDate
// ) AS[e]
// WHERE[e].[UnitPrice] > 80.0',
// N'@OrderDate datetime',@OrderDate='2014 - 01 - 01 00:00:00'
// CASE 2:模擬條件串接情況
int? productID = 1;
short? orderQty = 4;
string TSQL9 = @"
SELECT
P.ProductID ,
P.[Name] AS ProductName ,
POD.OrderQty ,
POD.UnitPrice ,
POD.LineTotal
FROM [Purchasing].[PurchaseOrderHeader] AS POH
JOIN [Purchasing].[PurchaseOrderDetail] AS POD ON POH.PurchaseOrderID = POD.PurchaseOrderID
JOIN Production.[Product] AS P ON POD.ProductID = P.ProductID
";
var result = dbContext.Database
.SqlQueryRaw<PurchaseOrderDetailViewModel>(TSQL9);
if (productID.HasValue)
result = result.Where(vw => vw.ProductID == productID.Value);
if (orderQty.HasValue)
result = result.Where(vw => vw.OrderQty >= orderQty.Value);
Console.WriteLine((await result.ToListAsync()).Count().ToString());
// TSQL:exec sp_executesql
// N'SELECT [e].[LineTotal], [e].[OrderQty], [e].[ProductID], [e].[ProductName], [e].[UnitPrice]
// FROM(
// SELECT
// P.ProductID,
// P.[Name] AS ProductName,
// POD.OrderQty,
// POD.UnitPrice,
// POD.LineTotal
// FROM[Purchasing].[PurchaseOrderHeader] AS POH
// JOIN[Purchasing].[PurchaseOrderDetail] AS POD ON POH.PurchaseOrderID = POD.PurchaseOrderID
// JOIN Production.[Product] AS P ON POD.ProductID = P.ProductID
// ) AS[e]
// WHERE[e].[ProductID] = @__productID_Value_1
// AND[e].[OrderQty] >= @__orderQty_Value_2',
// N'@__productID_Value_1 int, @__orderQty_Value_2 smallint',@__productID_Value_1=1,@__orderQty_Value_2=4
}
}
}
USE [AdventureWorks2022]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Asset](
[ID] [int] IDENTITY(1,1) NOT NULL,
[NO] [char](5) NOT NULL,
[Name] [nvarchar](100) NOT NULL,
[PurchDate] [date] NULL,
[Location] [nvarchar](20) NOT NULL,
[Department] [nvarchar](20) NOT NULL,
CONSTRAINT [PK_Asset] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[Asset] ADD CONSTRAINT [DF_Asset_NO] DEFAULT ('') FOR [NO]
GO
ALTER TABLE [dbo].[Asset] ADD CONSTRAINT [DF_Asset_Name] DEFAULT (N'') FOR [Name]
GO
ALTER TABLE [dbo].[Asset] ADD CONSTRAINT [DF_Asset_Location] DEFAULT (N'') FOR [Location]
GO
ALTER TABLE [dbo].[Asset] ADD CONSTRAINT [DF_Asset_Department] DEFAULT (N'') FOR [Department]
GO
-- 建立假資料
INSERT INTO [dbo].[Asset] ([NO],[Name],[PurchDate],[Location],[Department]) VALUES
('A0001' , N'商用電腦' , '20240101' , N'職員一' , N'行政') ,
('A0002' , N'事務機' , '20230707' , N'職員一' , N'行政') ,
('A0003' , N'油壓台車' , '20200820' , N'職員二' , N'運輸') ,
('A0004' , N'貨車' , '20190505' , N'職員二' , N'運輸') ,
('A0005' , N'手機' , '20190607' , N'職員二' , N'運輸')
GO
namespace ZXingQRCode
{
public class AssetReportModel : Asset
{
public byte[] QRcodeInfo { get; set; }
}
}
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using ZXing;
namespace ZXingQRCode
{
internal static class QRCodeGenerator
{
public static byte[] Create(string content)
{
if (string.IsNullOrWhiteSpace(content))
throw new ArgumentNullException(content);
BarcodeWriter writer = new BarcodeWriter
{
Format = BarcodeFormat.QR_CODE,
Options = new ZXing.QrCode.QrCodeEncodingOptions
{
Margin = 0,
Width = 200,
Height = 200
}
};
using (Bitmap bitmap = writer.Write(content))
using (MemoryStream stream = new MemoryStream())
{
bitmap.Save(stream, ImageFormat.Png);
return stream.ToArray();
}
}
}
}
using Microsoft.Reporting.WinForms;
using System;
using System.Data;
using System.Linq;
using System.Windows.Forms;
namespace ZXingQRCode
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
var dbContext = new AdventureWorks2022Entities();
var dataSources = dbContext.Asset.ToList();
var reportSource = dataSources.Select(s => new AssetReportModel()
{
NO = s.NO,
Name = s.Name,
PurchDate = s.PurchDate,
Location = s.Location,
Department = s.Department,
QRcodeInfo = QRCodeGenerator.Create(s.NO)
}).ToList();
reportViewer1.LocalReport.DataSources.Add(new ReportDataSource(nameof(AssetReportModel), reportSource));
reportViewer1.RefreshReport();
}
}
}
use tempdb
GO
DROP TABLE IF EXISTS #Temp
CREATE TABLE #Temp
(
ID int IDENTITY(1,1),
Data char(1) null
)
INSERT INTO #Temp VALUES
('1') , (null) , (null) ,
('5') , (null) , (null) ,
('8') , (null) , (null)
SELECT * ,
SUM(IIF(Data IS NULL , 0 , 1)) OVER
(
ORDER BY ID
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS GroupNo
FROM #Temp