星期六, 8月 24, 2019

[EF] 資料驗證-驗證錯誤訊息

紀錄在 AP 中檢查資料驗證錯誤訊息方式

  • 利用中斷點來找出驗證錯誤訊息
Step1:

[EF] 資料驗證-錯誤訊息查詢-1

Step2:

[EF] 資料驗證-錯誤訊息查詢-2

Step3:

[EF] 資料驗證-錯誤訊息查詢-3

Step4:

[EF] 資料驗證-錯誤訊息查詢-4

Step5:

[EF] 資料驗證-錯誤訊息查詢-5

Step6:

[EF] 資料驗證-錯誤訊息查詢-6

  • 透過 EntityValidationErrors 來顯示驗證錯誤訊息
try
 {
     uw.Save();
 }
 catch (DbEntityValidationException ex)
 {
     StringBuilder sb = new StringBuilder();
     foreach (DbEntityValidationResult e in ex.EntityValidationErrors)
     {
         foreach (DbValidationError ve in e.ValidationErrors)
         {
             sb.AppendLine($"欄位 {ve.PropertyName} 發生錯誤: {ve.ErrorMessage}");
         }
     }
     Console.WriteLine(sb.ToString());
 }
[EF] 資料驗證-錯誤訊息查詢-7
  • 自訂錯誤訊息
在 Entity 上設定 ErrorMessage 來自訂錯誤訊息
[Table("Employee")]
    public partial class Employee
    {
        [Key]
        public int EmployIeeD { get; set; }

        [Required(ErrorMessage ="員工姓名為必填資訊")]
        [StringLength(100)]
        public string EmployeeName { get; set; }
    }
[EF] 資料驗證-驗證錯誤訊息-8

星期三, 8月 21, 2019

PowerLink 檔名異常

廠長告知同事,NC 檔案無法抓進機器內,無法進行加工,一起到現場確認,錯誤訊息明確,就檔名異常,不知道為什麼會突然想輸入有小數點檔名就是

PowerLink 檔名異常

星期六, 8月 17, 2019

[VFP] Excel 常數

[VFP] Automation 在 Excel 上的應用 下方有段 [利用錄製巨集來了解 VBA 語法] 內容,以往都是查到 Excel 常數後,拿去 Google 查看到底要如何轉換,在該篇討論 - 關於VFP使用EXCEL時的變數代碼哪裡找? 內發現,原來 VBA 內就可以查到了,趕快學下來

利用即時運算視窗

[VFP] Excel 常數-1

利用瀏覽物件

[VFP] Excel 常數-2

混在一起觀察

[VFP] Excel 常數-3

星期五, 8月 16, 2019

[EF] RemoveRange

延續該篇 - [EF] identity 產生的資料,現在來觀察 Remove() 和 RemoveRange() 大量刪除時,是如何產生 TSQL 來進行作業刪除作業
namespace EFIdentity
{
    class Program
    {
        static void Main(string[] args)
        {
            using (SchoolContext context = new SchoolContext())
            {

                // 不用在意刪除順序,DbContext 會自行判斷
                var personalData = context.Person.Where(p => p.PersonID == 1074 || p.PersonID == 1075).ToList();
                context.Person.RemoveRange(personalData);
                var studentGradeData = context.StudentGrade.Where(s => s.StudentID == 1074 || s.StudentID == 1075).ToList();
                context.StudentGrade.RemoveRange(studentGradeData);

                // 透過 SaveChange() 進行資料刪除
                context.SaveChanges();

            }
        }
    }
}
SQL Profile 錄 TSQL 語法

[EF] RemoveRange
exec sp_executesql N'DELETE [dbo].[StudentGrade]
WHERE ([EnrollmentID] = @0)',N'@0 int',@0=68
go
exec sp_executesql N'DELETE [dbo].[StudentGrade]
WHERE ([EnrollmentID] = @0)',N'@0 int',@0=69
go
exec sp_executesql N'DELETE [dbo].[StudentGrade]
WHERE ([EnrollmentID] = @0)',N'@0 int',@0=70
go
exec sp_executesql N'DELETE [dbo].[Person]
WHERE ([PersonID] = @0)',N'@0 int',@0=1075
go
結論
  • SaveChange() 會自行決定刪除資料順序,不須要刻意先刪除 Detail Table 再去刪除 Master Table
  • 雖然是利用 RemoveRange() 來移除資料,但是還是根據 Table PK 一筆一筆產生 Delete 語法,這一點跟 VFP CursorAdapter 的行為是一致的

星期四, 8月 15, 2019

[EF] identity

Master-Detail 架構下,假如 Master PK 是使用 identity 情況下,通常都必須先儲存 Master Table 後取回 identity 值,再把 identity 塞進 Detail Table,學習 EF 時發現,竟然直接設定就行

[EF] 學校範例資料庫 內可以看出,Person 和 StudentGrade 是 1 對 多關係,就拿它當範例來使用

[EF] 取得 identity-2

C# Code 測試
using EFIdentity.Models;

namespace EFIdentity
{
    class Program
    {
        static void Main(string[] args)
        {
            using (SchoolContext context = new SchoolContext())
            {
                // Add() 多載可以回傳 TEntity
                var p = context.Person.Add(new Person() { LastName = "Identity", FirstName = "demo", EnrollmentDate = DateTime.Today, Discriminator = "Student" });
                // StudentGrade.StudentID 直接設定 Person.Add() 回傳的 TEntity.PersonID
                context.StudentGrade.Add(new StudentGrade() { StudentID = p.PersonID, CourseID = 4022, Grade = 3 });
                context.StudentGrade.Add(new StudentGrade() { StudentID = p.PersonID, CourseID = 4041, Grade = 4 });
                context.StudentGrade.Add(new StudentGrade()
                {
                    StudentID = p.PersonID,
                    CourseID = 4061,
                    Grade = 5
                });
                // 透過 SaveChange() 進行資料儲存
                context.SaveChanges();
            }
        }
    }
}
從 SSMS 內去查詢結果

[EF] 取得 identity-1

從 SQL Profile 去側錄產生的 TSQL 語法,事後才想說要觀察 TSQL,所以和上圖的 identity 不同

[EF] 取得 identity-3
exec sp_executesql N'INSERT [dbo].[StudentGrade]([CourseID], [StudentID], [Grade])
VALUES (@0, @1, @2)
SELECT [EnrollmentID]
FROM [dbo].[StudentGrade]
WHERE @@ROWCOUNT > 0 AND [EnrollmentID] = scope_identity()',N'@0 int,@1 int,@2 decimal(3,2)',@0=4022,@1=1068,@2=3.00

星期三, 8月 14, 2019

[C#] Delegate 各版本寫法

使用 Predicate、Func 來複習 Delegate 各版本用法
using System;
using System.Collections.Generic;

namespace DelegateSample
{
    class Program
    {
        static void Main(string[] args)
        {
            var data = GetDate();

            // 寫法 1:C# 1.0
            Predicate<Employee> d1 = new Predicate<Employee>(GetEmployee1);
            Employee e1 = data.Find(e => d1(e));
            Console.WriteLine(e1.ToString());

            // 寫法 2:C# 2.0
            Predicate<Employee> d2 = GetEmployee2;
            Employee e2 = data.Find(e => d2(e));
            Console.WriteLine(e2.ToString());

            // 寫法 3:C# 2.0 - 匿名方法
            Predicate<Employee> d3 = delegate (Employee e) { return e.EmployeeID == 3; }; // delegate 字樣可以省略
            Employee e3 = data.Find(d3);
            Console.WriteLine(e3.ToString());

            // C# 3.0 Lambda Expression
            // 寫法 4:
            Employee e4 = data.Find((Employee e) => e.EmployeeID == 4);
            Console.WriteLine(e4.ToString());
            
            // 寫法 5:實務上使用
            Employee e5 = data.Find(e => e.EmployeeID == 5);
            Console.WriteLine(e5.ToString());

            // 寫法 6:利用 Func 來練習
            Func<Employee, int, bool> d6 = new Func<Employee, int, bool>(GetEmployee);
            Employee e6 = data.Find(e => d6(e, 6));
            Console.WriteLine(e6.ToString());
        }

        private static List<Employee> GetDate()
        {
            return new List<Employee>()
            {
                new Employee(){ EmployeeID = 1 , EmployeeName = "Name1"} ,
                new Employee(){ EmployeeID = 2 , EmployeeName = "Name2"} ,
                new Employee(){ EmployeeID = 3 , EmployeeName = "Name3"} ,
                new Employee(){ EmployeeID = 4 , EmployeeName = "Name4"} ,
                new Employee(){ EmployeeID = 5 , EmployeeName = "Name5"} ,
                new Employee(){ EmployeeID = 6 , EmployeeName = "Name6"}
            };
        }

        private static bool GetEmployee1(Employee employee)
        {
            return GetEmployee(employee, 1);
        }

        private static bool GetEmployee2(Employee employee)
        {
            return GetEmployee(employee, 2);
        }

        private static bool GetEmployee(Employee employee, int targetEmployeeID)
        {
            return employee.EmployeeID == targetEmployeeID ? true : false;
        }
    }

    public class Employee
    {
        public int EmployeeID { get; set; }
        public string EmployeeName { get; set; }

        public override string ToString()
        {
            return $"{EmployeeID}-{EmployeeName}";
        }
    }
}
    延伸閱讀
  • [C#] Delegate 練習 12

星期一, 8月 12, 2019

[SQL] 增加適合的 WHERE 條件

之前記錄過 [SQL] 限制資料筆數 12,是因為多餘的 WHERE 條件導致效能問題,這篇則是剛好相反,少了適合的 WHERE 條件

剛好懂這幾個 Table 商業邏輯,所以很明確地知道 [訂單明細訂單剩餘數量] 條件是可以加上去,請同事確認後,確定沒問題
SELECT
  pa.訂單編號
  ,c1.客戶名稱
FROM 訂單 AS pa
  JOIN 訂單明細 AS i ON pa.訂單編號 = i.訂單編號
  JOIN 派工單 AS S ON S.派工單號 = I.派工單號
  JOIN 
   (
     ................
   ) AS c1 ON c1.客戶編號 = pa.客戶編號
WHERE S.是否結案 = ''
  -- 加上去的 WHERE 條件
  AND I.訂單剩餘數量 > 0
改善前後執行計畫成本比較

[SQL] 增加適合的 WHERE 條件-1

訂單 Table 改善前的執行計畫,拋出了全部資料

[SQL] 增加適合的 WHERE 條件-2
訂單 Table 改善後的執行計畫,只有抓出 159 筆資料

[SQL] 增加適合的 WHERE 條件-3

訂單、訂單明細 Table Statistics 前後比較

[SQL] 增加適合的 WHERE 條件-4

星期日, 8月 11, 2019

[SQL] SQL Profile 觀察交易

學習 Entity Framework 時,查到 SaveChange() 或透過 EF 來執行 TSQL 會自行管理交易,想說用 Profile 來觀察,但是卻一直沒有錄到交易產生,反而是開啟
DbContext.Database.Log = Console.Write;
有觀察到,後來才想到 Profile 內的交易事件,要手動勾選開啟,Orz

[SQL] SQL Profile觀察交易

星期四, 8月 01, 2019

[C#] 參數 - Out

Out 參數簡易筆記
namespace Out
{
    class Program
    {
        static void Main(string[] args)
        {
            #region 宣告方式差異
            // C# 7.0 前
            int result1;
            OutMethod(10, out result1);

            // C# 7.0 開始
            OutMethod(10, out int result2);
            #endregion

            OutMethod(10, out int result3);
            Console.WriteLine(result3);

            OutMethod(out int result4, 20);
            Console.WriteLine(result4);

            int input = 11;
            Method_ReturnMultiResult(input, out int inputX2, out bool IsOdd);
            Console.WriteLine($"{input}-{inputX2}-{IsOdd}");

            var result6 = Method_ReturnMultiResult(22);
            Console.WriteLine($"{result6.input}-{result6.inputX2}-{result6.IsOdd}");
        }

        #region out 參數位置
        // MS 函數大多把 out 寫在後方
        private static void OutMethod(int i, out int result)
        {
            result = i * i;
        }

        // 同事偏好把 out 放在前方
        private static void OutMethod(out int result, int i)
        {
            result = i * i;
        }
        #endregion

        #region 回傳多值
        // 利用 Out 回傳多值
        private static void Method_ReturnMultiResult(int i, out int inputX2, out bool IsOdd)
        {
            inputX2 = i * 2;
            IsOdd = (i % 2 == 0 ? false : true);
        }

        // C# 7.0 開始有 ValueTuple 後,大多利用它來回傳多值
        private static (int input, int inputX2, bool IsOdd) Method_ReturnMultiResult(int i)
        {
            return (i, i * 2, i % 2 == 0 ? false : true);
        }
        #endregion
    }
}