2018年4月4日 星期三

Entity Framework 資料異動寫入Log

Log寫得好,晚上睡覺睡得好
因為目前都在用Entity Framework來處理DB,想很久Log要怎麼寫比較好
在每個Method SaveChange前多一行寫Log的動作?
這樣做非常累也不是一勞永逸的方法,只要有新的CRUD,你都必須要每個都加上去
那麼新進人員進來,不知道要加這一行來寫Log不就糗了

所以我目前想到最好的方式就是覆寫EF的SaveChange的方法
加在裡面的好處就是,只要你是用EF來CRUD,我就一定會處理的到
新進人員也不需要處理寫Log這一塊,因為已經包在底層了

所以開了3個資料表
LogEntityRef 是存放什麼資料表需要寫入Log
LogEntityField 是存放資料表的那些欄位需要寫入Log
LogEntityValue 是寫入Log的資料

三個資料表可參考下圖


那麼怎麼覆寫EF的SaveChange呢,創建一個新的Class要與EF的名稱同名

using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.Core.Metadata.Edm;
using System.Data.Entity.Core.Objects;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace SportBook.Entity
{
    public partial class sbkEntities : DbContext
    {
        private string sysName = "SYSTEM";
        public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken))
        {
            SetValuesOnBeforeSaveChanges();
            return PrivateSaveChangesAsync(cancellationToken);
        }

        public override Task<int> SaveChangesAsync()
        {
            SetValuesOnBeforeSaveChanges();
            return PrivateSaveChangesAsync(CancellationToken.None);
        }

        public override int SaveChanges()
        {
            SetValuesOnBeforeSaveChanges();
            return PrivateSaveChangesAsync(CancellationToken.None).Result;
        }

        /// <summary>
        /// SaveChange之前把必要欄位寫入
        /// </summary>
        private void SetValuesOnBeforeSaveChanges()
        {            
            var trackerEntries = this.ChangeTracker.Entries()
                    .Where(x => x.State == EntityState.Modified || x.State == EntityState.Added).ToList();
            foreach (var entry in trackerEntries)
            {
                switch (entry.State)
                {
                    case EntityState.Modified:
                        entry.CurrentValues.SetValues(new { UserUpdated = sysName, DateUpdated = DateTime.UtcNow });
                        break;
                    case EntityState.Added:
                        entry.CurrentValues.SetValues(new { UserCreated = sysName, DateCreated = DateTime.UtcNow });
                        break;
                }
            }
        }

        /// <summary>
        /// SaveChange之後開始寫Log
        /// </summary>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        private async Task<int> PrivateSaveChangesAsync(CancellationToken cancellationToken)
        {
            ObjectContext context = ((IObjectContextAdapter)this).ObjectContext;
            await context.SaveChangesAsync(SaveOptions.DetectChangesBeforeSave, cancellationToken).ConfigureAwait(false);

            var trackerEntries = this.ChangeTracker.Entries()
                .Where(x => x.State == EntityState.Modified || x.State == EntityState.Added).ToList();

            var LogValue = new List<Entity.LogEntityValue>();
            foreach (var entry in trackerEntries)
            {
                var entityName = entry.Entity.GetType().Name;
                //判斷entityName是否需要寫入Log
                var LogEntityRef = this.LogEntityRef.AsNoTracking().Where(x => x.Code == entityName).FirstOrDefault();
                if (LogEntityRef != null)
                {
                    //取出Table那些欄位需要更新
                    var LogEntityField = this.LogEntityField.AsNoTracking().Where(x => x.LogEntityRefId == LogEntityRef.Id)
                        .Select(x => new
                        {
                            Id = x.Id,
                            FieldName = x.FieldName
                        }).ToList();

                    var primaryKey = entry.CurrentValues.GetValue<object>("Id");
                    foreach (var logfield in LogEntityField)
                    {
                        var originalValue = (entry.State == EntityState.Added) ? string.Empty : entry.OriginalValues.GetValue<object>(logfield.FieldName);
                        var currentValue = entry.CurrentValues.GetValue<object>(logfield.FieldName);
                        if (entry.State == EntityState.Modified)
                        {
                            if (originalValue.ToString() == currentValue.ToString())
                                continue;
                        }
                        LogValue.Add(new Entity.LogEntityValue
                        {
                            DateCreated = DateTime.UtcNow,
                            UserCreated = sysName,
                            LogEntityRefId = LogEntityRef.Id,
                            RefObjectId = (int?)primaryKey,
                            LogEntityFieldId = logfield.Id,
                            OldValue = originalValue.ToString(),
                            NewValue = currentValue.ToString()
                        });
                    }                    
                }
            }
            context.AcceptAllChanges();
            LogEntityValue.AddRange(LogValue);
            int result = await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
            return result;
        }
    }
}

以上就是寫Log的原始碼

2018年1月6日 星期六

React真心覺得很難入門

其實我本身都在寫後端,最近換公司,公司有一個專案想用React來寫前端
所以就叫我去看一看,就如以下的功能,可以把文字動態建立列表
我用jQuery可能不用寫幾行程式就可解決

















但最近學了React,真心覺得想哭,原始碼我放在Github,目前還在練習中寫得不太好
https://github.com/gn8866/React

我參考了很多在GitHub上的文章,覺得這個高手寫得非常棒,又很容易懂,推薦給要學React的初學者,一篇一篇耐心看會學到很多
https://ithelp.ithome.com.tw/users/20103131/ironman/1012

那還有一個網址是主管傳給我的,我也是覺得不錯,但還是建議把上面的鐵人30day的文章都看完再看以下的網址教學會比較好
https://github.com/LeonardoDanielSu/redux-ld

所以我把這幾天學的感想說一下
因為我之前是學jQuery,所以建議學React之前把jQuery忘記,就跟張無忌要學太極要先把九陽神功忘記是一樣的道理

因為React跟jQuery是很不一樣的設計概念,光前戲React就要先建置開發環境與開發工具

  • 安裝Visual Studio Code開發工具,以及調整相關設定
  • 安裝Node.js(已包含npm工具程式)
  • 安裝ES6樣版文件
  • 安裝Google Chrome
  • 撰寫第一支程式

開發React千萬別用cdn載入js的方式來設計,React精華就是元件的切割,讓開發上變得好維護不雜亂
若您只是一個網頁小功能要設計,那我真心建議您使用jQuery就好
殺雞焉用牛刀,千萬不要覺得用了React就潮的出水的概念

React是一種single page application(SPA)的概念,所以它裡面就只有一個HTML
而且那個HTML程式碼非常簡易
























你沒看錯就這樣而已,怎麼互動就是全部寫在js
怎麼煥頁?要使用Router來切換頁面,相當的方便
所以設計概念上讓我對以前的jQuery設計想法很驚訝
目前還在研究中....