[C#]使用AES進行檔案加密

這次的學習與參考來自:
[C#.NET] 字串及檔案 利用 DES / AES 演算法加解密 – 余小章 @ 大內殿堂

以下實作了一個AesController來使用
範例如下,設定KeyIV之後,測試加密與解密功能

static void Main(string[] args)
{
    string key = "YourKey16Bytes..";
    string iv = "YourIv16Bytes...";
    string file = "D:\\TestCase\\file.xml";
    string afterEncrypt = "D:\\TestCase\\afterEncrypt.xml";
    string afterDecrypt = "D:\\TestCase\\afterDecrypt.xml";

    AesController ctrl = new AesController(key, iv);
    ctrl.Encrypt(file, afterEncrypt);
    ctrl.Decrypt(afterEncrypt, afterDecrypt);
}

下圖是原始檔案的內容

2017-5-12 下午 04-25-51.png

經過加密之後,無法判讀原始內容

2017-5-12 下午 05-10-11.png

最後再解密回來

2017-5-12 下午 05-10-27.png

範例完成
最後則是AesController的內容

public class AesController
{
    private Aes aes;
    public AesController(string key, string iv)
    {
        Aes aes = CreateAes(key, iv);
    }
    public void Decrypt(string inputPath, string outputPath)
    {
        ICryptoTransform decryptor = aes.CreateDecryptor();
        ToCrypt(inputPath, outputPath, decryptor);
    }
    
 
    public void Encrypt(string inputPath, string outputPath)
    {
        ICryptoTransform encryptor = aes.CreateEncryptor();
        ToCrypt(inputPath, outputPath, encryptor);
    }
    private Aes CreateAes(string key, string iv)
    {
        byte setSize = 16;
        if (key.Length != setSize || iv.Length != setSize)
        {
            throw new Exception($"Input.Length != {setSize}");
        }
        aes = Aes.Create();
        aes.Key = Encoding.ASCII.GetBytes(key);
        aes.IV = Encoding.ASCII.GetBytes(iv);
        return aes;
    }
    private void ToCrypt(string inputPath, string outputPath, ICryptoTransform icrypt)
    {
        using (FileStream fsRead = new FileStream(inputPath, FileMode.Open, FileAccess.Read))
        using (FileStream fsWrite = new FileStream(outputPath, FileMode.Create, FileAccess.Write))
        {
            byte[] dataAry = new byte[fsRead.Length];
            fsRead.Read(dataAry, 0, dataAry.Length);

            using (CryptoStream cs = new CryptoStream(fsWrite, icrypt, CryptoStreamMode.Write))
            {
                cs.Write(dataAry, 0, dataAry.Length);
                cs.FlushFinalBlock();
            }
        }
    }
}

[C#][EF] Find & FirstOrDefault

這篇將做個小實驗
關於Entity Framework的查詢運算式
使用FindFirstOrDefault的差異


首先是關於Find的說明:DbSet.Find方法
可以看到它的查詢條件限定於Primary Key
並且是先從Cache中查詢
如果查不到,才到Database去查詢

所以若是查詢目標在Cache的狀況下,Find的效能是最快的
接著使用中文北風資料庫來測試一下

static void Main(string[] args)
{
    EFEntities ef = new EFEntities ();

    int id = 2;
    for (int i = 0; i < 5; i++)
    {
        var find = ef.產品資料.Find(id);
        Console.WriteLine(find.產品);
    }
    Console.ReadKey();
}

上方程式碼進行了5次的Find
但透過SQL Server Profiler
可以看到實際只進行1次查詢 (其餘4次直接從Cache拿到結果)

2017-3-10 下午 05-18-42

再來試試FirstOrDefault

static void Main(string[] args)
{
    EFEntities ef = new EFEntities();

    int id = 2;
    for (int i = 0; i < 5; i++)
    {
        var firstOrDefault = ef.產品資料.FirstOrDefault(x=> x.產品編號 == id);
        Console.WriteLine(firstOrDefault.產品);
    }
    Console.ReadKey();
}

結果如下,5次都會對Database進行查詢

2017-3-10 下午 05-25-57


結論:

[C#] 初探Entity Framework (九) – 關聯式查詢

這篇文章將介紹關聯式查詢
參考以下兩篇文章

範例將使用Northwind中的兩張Table: Orders, Customers,如下圖

2017-2-16-%e4%b8%8b%e5%8d%88-06-01-52

範例:從Orders關聯查詢Customers中的CompanyName, ContactName
首先使用SQL進行Table Join的查詢

2017-2-16 下午 06-09-05.png

接著換用Entity Framework來試試看
寫法如下圖,使用Include進行關聯查詢
之後就可以直接取得關聯表的欄位值

2017-2-16-%e4%b8%8b%e5%8d%88-06-28-04

輸出結果如下圖,完成!

2017-2-16-%e4%b8%8b%e5%8d%88-06-26-54

[C#] DataGridView 應用(六) – Copy Column Header

這一篇將實作Copy Column Header的功能


主要的情境是User需要把DataGridView中的資料複製貼上到Excel之中
但發現Ctrl + A 全選、複製、貼上之後,會缺少Column Header
如下圖
2016-12-2-%e4%b8%8b%e5%8d%88-05-08-38
複製貼上到Excel,缺少了Column Header
2016-12-2-%e4%b8%8b%e5%8d%88-05-09-43


方法1
將DataGridView的ClipboardCopyMode,設定為EnableAlwaysIncludeHeaderText
不論複製任何儲存格,皆會連帶Column Header一起複製
如下圖
2016-12-2-%e4%b8%8b%e5%8d%88-05-18-46
複製貼上至Excel,成功包含Column Header
2016-12-2-%e4%b8%8b%e5%8d%88-05-19-51


方法2
若User不希望AlwaysIncludeHeader
可以將ClipboardCopyMode改回EnableWithAutoHeaderText(預設值)
然後將SelectionMode設定為ColumnHeaderSelect

如果有預設的Column,可能會產生下圖錯誤
2016-12-2-%e4%b8%8b%e5%8d%88-05-26-11
原因是必須取消Column的Sort排序功能才行
將Column的SortMode改為NotSortable
下圖是IDE中的設定
2016-12-2-%e4%b8%8b%e5%8d%88-05-28-49
下圖則是程式新增Column的寫法
2016-12-2 下午 05-56-30.png
測試如下,可以成功選取Column Header,並進行複製貼上的動作。
2016-12-2-%e4%b8%8b%e5%8d%88-05-58-57


最後總結一下

  • 方法1
    • DataGridView.ClipboardCopyMode = EnableAlwaysIncludeHeaderText
    • 強制所有複製貼上都包含Column Header
  • 方法2
    • DataGridView.ClipboardCopyMode = EnableWithAutoHeaderText
    • DataGridView.SelectionMode = ColumnHeaderSelect
    • Column.SortMode = NotSortable
    • 只有選取Column Header的時候才會將其包含在內
    • 但必須取消原本預設的排序功能

 

 

[C#] 初探Entity Framework (八) – Transaction

這篇簡單實作Entity Framework的Transaction使用方式

2016.11.08修正

一般寫asp.net時,通常一個DBContext用完即丟
參考:Entity Framework DbContext 物件的生命週期 – Huan-Lin學習筆記

不過我目前是開發Winform專案
User 之間本身就是隔離的獨立個體
因此有時會重複使用DBContext
好處是可以善用快取、追蹤、交易管理等功能

交易處理的參考網頁:Working with Transactions (EF6 Onwards)
簡單的實作方法如下

public void UploadToDB(EFEntities ef)
{
    using (DbContextTransaction tran = ef.Database.BeginTransaction())
    {
        try
        {
            //暫時取消AutoDetect,提高新增資料的效率
            ef.Configuration.AutoDetectChangesEnabled = false; 
            
            //這邊自行撰寫,對你的資料庫進行新增、修改、刪除等工作
            ef.SaveChanges();

            tran.Commit(); //成功SaveChange後,才讓此交易成立
            MessageBox.Show("上傳完成");
        }
        catch (Exception ex)
        {
            MessageBox.Show("上傳錯誤,將顯示錯誤訊息");
            MessageBox.Show(ex.ToString());
            tran.Rollback();//SaveChange失敗,取消此交易動作
        }
        finally
        {
            ef.Configuration.AutoDetectChangesEnabled = true;
        }
    }
}

 

[C#] DataGridView 應用(五) – ComboBoxCell置換技巧

上一篇:[C#] DataGridView 應用(四) – DataGridViewCell
介紹了DataGridViewCell的幾種類型與使用方法
這篇將示範Cell置換的技巧


在第三篇中 ([C#] DataGridView 應用(三) – 修改資料、條件設定)
我們學到修改資料的方法
如下圖,我們可以手動輸入ContactName的內容
但若是我們已經有ContactName的選項清單
希望使用者不要手動輸入
就可以利用ComboBoxCell
把修改資料的方式,改為用清單選擇的方式
2016-5-11 下午 05-50-48


首先加入「修改」到右鍵選單,如下圖
(可參考:[C#] DataGridView 應用(二) – 右鍵選單、新增資料)

2016-7-7 上午 10-53-41

記得在Mouse Down的事件中
利用HitTestInfo來捕捉滑鼠所點擊的位置
這邊我用兩個變數,儲存要編輯的Row Index及Column Index

int editRow;
int editCol;
private void dataGridView1_MouseDown(object sender, MouseEventArgs e)
{
    if (e.Button == MouseButtons.Right)//按下右鍵
    {
        //捕捉使用者點擊的Row及Column
        DataGridView.HitTestInfo hti = dataGridView1.HitTest(e.X, e.Y);
        editRow = hti.RowIndex;
        editCol = hti.ColumnIndex;
        //顯示右鍵選單
        menu.Show(dataGridView1, new Point(e.X, e.Y));
    }
}

然後在右鍵選單的「修改」事件
把ComboBox清單,置換到要修改的格子中
讓使用者可以直接點選

string strBeforeEdit;
bool bComboBoxEdit = false;
private void miEdit_Click(object sender, System.EventArgs e)
{
    DataGridViewCell editCell = dataGridView1.Rows[editRow].Cells[editCol];//欲修改的格子
    strBeforeEdit = editCell.Value.ToString();//紀錄修改前的值

    //建立ComboBox的清單
    DataGridViewComboBoxCell cbCell = new DataGridViewComboBoxCell();
    cbCell.Items.Add("Yang Wang");
    cbCell.Items.Add("Pedro Afonso");
    cbCell.Items.Add("Sven Ottlieb");
    cbCell.Items.Add("Roland Mendel");
    cbCell.Items.Add("");
    cbCell.Items.Add("Cancel Edit");//讓使用者可以取消編輯

    //置換格子
    dataGridView1.Rows[editRow].Cells[editCol] = cbCell;
    bComboBoxEdit = true;//紀錄為ComboBox編輯
}

呈現的效果如下圖

2016-7-7 上午 11-19-22.png


記得在CellEndEdit的前段寫入bComboBoxEdit的判斷
ComboBox的點擊事件要另外做

private void dataGridView1_CellEndEdit(object sender, DataGridViewCellEventArgs e)
{
    if (bComboBoxEdit == true)
        return;

然後修改DataGridView的EditingControlShowing事件
用來幫我們的ComboBox關聯SelectedIndexChanged事件

private void dataGridView1_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
    if (e.Control is ComboBox)
    {
        ComboBox comboBox = e.Control as ComboBox;
        comboBox.SelectedIndexChanged -= ComboBox_SelectedIndexChanged;
        comboBox.SelectedIndexChanged += ComboBox_SelectedIndexChanged;
    }
}

最後編寫SelectedIndexChanged事件
一樣使用Entity Framework修改資料庫的資料

private void ComboBox_SelectedIndexChanged(object sender, EventArgs e)
{
    //取得編輯中的格子內容
    ComboBox cb = (ComboBox)sender;
    string text = cb.Text;
    if (text == "Cancel Edit")//取消
    {
        //復原編輯前狀態
        DataGridViewTextBoxCell cell = new DataGridViewTextBoxCell();
        cell.Value = strBeforeEdit;
        dataGridView1.Rows[editRow].Cells[editCol] = cell;
        //後續也不需實際進資料庫修改
        return;
    }
    else
    {
        //進資料庫修改
        //取得第一格的CustomerID
        string customerID = (dataGridView1.Rows[editRow].Cells[0].Value ?? "").ToString();
        //從資料庫根據ID取得目標Customer
        Customers cus = ef.Customers.Where(x => x.CustomerID == customerID).First();
        cus.ContactName = text;
        ef.Entry(cus).State = System.Data.Entity.EntityState.Modified;
        ef.SaveChanges();
        MessageBox.Show("修改成功");

        //將ComboBox回復為TextCell
        DataGridViewTextBoxCell cell = new DataGridViewTextBoxCell();
        cell.Value = text;
        dataGridView1.Rows[editRow].Cells[editCol] = cell;
    }
}

到這邊就把功能完成了
不過多玩幾次就可以發現有些小bug
例如:點擊修改→取得ComboBox→未實際點選,又修改別的儲存格
這時就要記得把上一個ComboBox變回TextCell (取消修改)

記得多模擬一些使用情境
撰寫防止錯誤的方法唷!