星期一, 2月 25, 2019

[EF] DataGridView 和導覽屬性 - Reflection

該篇筆記是延伸 [EF] DataGridView 和導覽屬性 - CellFormatting Event,該篇在 CellFormatting Event 內 Hard Code,完全沒有彈性,所以在這篇改用 Reflection 來達到相同需求,本篇也同樣是 DataGridView: How to Bind Nested Objects 的練習筆記

C# Code
using System.Data.Entity;
using System.Reflection;

namespace DataGridView4CellFormatting
{
    public partial class Form2 : Form
    {
        EFDbContext context;
        BindingSource bsOrder;

        public Form2()
        {
            InitializeComponent();
            context = new EFDbContext();
            bsOrder = new BindingSource();
        }

        private void Form2_Load(object sender, EventArgs e)
        {
            context.Order.Include(c => c.Customer).Load();
            bsOrder.DataSource = context.Order.Local.ToBindingList();

            dgvOrders.AutoGenerateColumns = false;
            dgvOrders.DataSource = bsOrder;
        }

        private readonly char DataPropertyNameSign = '.' ;
        private void dgvOrders_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
        {
            object obj = dgvOrders.Rows[e.RowIndex].DataBoundItem;
            string DataPropertyName = dgvOrders.Columns[e.ColumnIndex].DataPropertyName;

            if (obj != null &&
                DataPropertyName.Contains(DataPropertyNameSign))
            {
                e.Value = BindNavigationProperty(obj, DataPropertyName);
            }
        }

        /// <summary>
        /// 註解以該範例來說明註記,方便事後回憶
        /// </summary>
        /// <param name="Property">Order Class</param>
        /// <param name="DataPropertyName">Customer.CustName</param>
        /// <returns>CustName 資料</returns>
        private string BindNavigationProperty(object Property, string DataPropertyName)
        {
            if (DataPropertyName.Contains(DataPropertyNameSign) == false)
            {
                throw new Exception($"{nameof(DataPropertyName)} 資料不符合導覽屬性規則");
            }

            // Customer.CustName
            var DataPropertyInfo = ParseDataPropertyName(DataPropertyName);
            // Customer
            string NavigatrionPropertName = DataPropertyInfo.NavigationPropertyName;
            // CustName
            string PropertyName = DataPropertyInfo.PropertyName;

            // 取回 Order.Customer 相關資料
            var obj = GetPropertyValue(Property, NavigatrionPropertName);
            // 取回 Customer.CustName 資料
            return GetPropertyValue(obj, PropertyName).ToString();
        }

        /// <summary>
        /// 取得 object Property Value
        /// </summary>
        /// <param name="obj">欲取值的 object </param>
        /// <param name="propertyName">屬性</param>
        /// <returns>屬性值</returns>
        private object GetPropertyValue(object obj, string propertyName)
        {
            Type t = obj.GetType();

            // BindingFlags.IgnoreCase 表示不區分大小寫,Flag 只要重新設定就要全部都設,只設單一個會沒有效果
            PropertyInfo info = t.GetProperty(propertyName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
            if (info == null)
            {
                throw new Exception($"Property:{nameof(propertyName)} 不存在,無法取值");
            }

            object oValue = info.GetValue(obj, null);
            if (oValue == null)
            {
                throw new Exception($"無法取得 Property:{nameof(propertyName)} 的值");
            }

            return oValue;
        }

        /// <summary>
        /// 解析 DataPropertyName
        /// </summary>
        /// <param name="DataPropertyName">DataPropertyName</param>
        /// <returns>ClassName 和 PropertyName </returns>
        private (string NavigationPropertyName, string PropertyName) ParseDataPropertyName(string DataPropertyName)
        {
            if (string.IsNullOrEmpty(DataPropertyName))
            {
                throw new ArgumentNullException(nameof(DataPropertyName));
            }

            string[] result = DataPropertyName.Split(DataPropertyNameSign);

            int ArraryLength = 2;
            if (result.Length != ArraryLength)
            {
                string ErrorMessage = $"{nameof(DataPropertyName)} 參數格式必須為 ClassName.PropertyName,傳入值為 {DataPropertyName}";
                throw new ArgumentException(ErrorMessage);
            }

            return (result[0], result[1]);
        }
    }
}
  • 重點1:BindingFlags.IgnoreCase [Highlight 76 77 行]
練習時設定 DataPropertyName 時,手殘輸入造成 DataPropertyName 和 Class.Property 大小寫差異,找不到 Property 而拋出 null exception,所以特別去指定忽略大小寫,BindingFlags 一旦有重新設定,就要把相關 Flag 也一併補上,要不然會沒有作用喔
  • 重點2:SQL Profile 側錄 TSQL 語法
腦海裡的無聊想法,想說都用 Load() 取出 Customer 相關資料,會不會在 CellFormatting 時又回到 DB 一筆一筆抓 CustName,經由 SQL Profile 確定後,確定沒有該情況

沒有留言:

張貼留言