C#2.0:AppSettings再び

下の記事で書いた物をもっと汎用的にした物。特にIntに関係なく保管ができ、
様々な型にappSettingsの値を変換可能。
さらに、List>への変換メソッドを備える。
2011/6/22変更:ソースが簡潔になるように修正
2011/7/5変更:さらに微修正

using System;
using System.Collections.Generic;
using System.Text;
using System.Configuration;
using System.Collections;

namespace HtmlCreator
{
    // App.ConfigのAppSettingsをDictionaryに保管しておく。
    public class AppSettingsDic<IndexerType>
    {
        protected Dictionary<string, object> dic = new Dictionary<string, object>();

        public AppSettingsDic(bool changeBoolAndInt)
        {
            foreach (string key in ConfigurationManager.AppSettings.AllKeys)
            {
                string value = ConfigurationManager.AppSettings[key];

                dic.Add(key, value);
                if (changeBoolAndInt == true)
                {
                    // Int32やBooleanは予め変換して保持しておく。
                    int valNum = -1;
                    if ((Int32.TryParse(value, out valNum) == true) && (value.StartsWith("0") == false))
                    {
                        this.dic[key] = valNum;
                    }

                    bool valBol = false;
                    if (Boolean.TryParse(value, out valBol) == true)
                    {
                        this.dic[key] = valBol;
                    }
                }
            }
        }

        public bool Exists(string key)
        {
            return this.dic.ContainsKey(key);
        }

        public IndexerType this[string key]
        {
            get { return this.Get<IndexerType>(key); }
        }

        public delegate bool ConvertFunc<K, V>(KeyValuePair<string, object> input, out KeyValuePair<K, V> output);

        /// <summary>
        /// AppSettingsの辞書をKeyValuePairのListに変換する。その際にconvertFuncを使い、KeyValuePair<K,V>に変換してリストに入れる。
        /// また、convertFuncの戻り値がfalseの場合にはListにはaddしない。
        /// 例えば、AppSettingsにComment1,Comment2といったkeyがある場合、それを1,2といったキーに変換し、valueを入れる際に使用する。
        /// </summary>
        /// <typeparam name="K">変換するキーの型</typeparam>
        /// <typeparam name="V">変換するバリューの型</typeparam>
        /// <param name="convertFunc">変換メソッド(必須)</param>
        /// <returns>Listに変換したAppSettingsのKeyValuePair</returns>
        public List<KeyValuePair<K, V>> ConvertToList<K, V>(ConvertFunc<K, V> convertFunc)
        {
            List<KeyValuePair<K, V>> list = new List<KeyValuePair<K, V>>();

            foreach (KeyValuePair<string, object> kv in this.dic)
            {
                KeyValuePair<K, V> newKv;

                bool find = convertFunc(kv, out newKv);
                if (find == true)
                {
                    list.Add(newKv);
                }
            }

            return list;
        }

        /// <summary>
        /// AppSettingsの辞書をKeyValuePairのDicionaryに変換する。その際にconvertFuncを使い、KeyValuePair<K,V>に変換してリストに入れる。
        /// また、convertFuncの戻り値がfalseの場合にはDictionaryにはaddしない。
        /// 例えば、AppSettingsにComment1,Comment2といったkeyがある場合、それを1,2といったキーに変換し、valueを入れる際に使用する。
        /// </summary>
        /// <typeparam name="K">変換するキーの型</typeparam>
        /// <typeparam name="V">変換するバリューの型</typeparam>
        /// <param name="convertFunc">変換メソッド(必須)</param>
        /// <returns>Dictionaryに変換したAppSettings</returns>
        public Dictionary<K, V> ConvertToDic<K, V>(ConvertFunc<K, V> convertFunc)
        {
            Dictionary<K, V> dic = new Dictionary<K, V>();

            foreach (KeyValuePair<string, object> kv in this.dic)
            {
                KeyValuePair<K, V> newKv;

                bool find = convertFunc(kv, out newKv);
                if (find == true)
                {
                    dic.Add(newKv.Key,newKv.Value);
                }
            }

            return dic;
        }


        /// <summary>
        /// IFormatProviderを設定しない値の取得
        /// </summary>
        /// <typeparam name="T">変換したい型</typeparam>
        /// <param name="key">取得するキー</param>
        /// <returns>変換後の値</returns>
        public T Get<T>(string key)
        {
            return this.Get<T>(key, null);
        }

        /// <summary>
        /// キーによるデータの取得。特定の値型に変換可能
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public T Get<T>(string key, IFormatProvider provider)
        {
            // キーがなければdefault値を返す。
            if (Exists(key) == false)
            {
                return default(T);
            }

            // すでにその型で格納されている場合、キャストして値を返す。
            if (this.dic[key] is T)
            {
                return (T)this.dic[key];
            }

            // 値型の変換メソッド
            T element = (T)Convert.ChangeType(this.dic[key], typeof(T), provider);
            return element;
        }

        public string[] GetSplit(string key, string delimiter)
        {
            string data = Get<string>(key);
            if (data == null)
            {
                return null;
            }

            char[] delimiters = delimiter.ToCharArray();
            string[] dataAry = data.Split(delimiters);
            return dataAry;
        }
    }
}

C#2.0:AppSettingsの値を数値で保管するディクショナリ

App.ConfigのAppSettingsで数値に変換できるものは数値に変換して辞書にしておく。
.Net Framework 2.0用
ConfigurationManager.AppSetting[key]で文字列は取得できるが、
文字列を数値に変換する必要があるので、それを簡潔に行うためのクラス。
Booleanバージョンがあってもいいかもしれない。

using System;
using System.Collections.Generic;
using System.Text;
using System.Configuration;
using System.Collections;

namespace ComboAndTextBoxBinder
{
    // App.ConfigのAppSettingsでDecimalに変換できるものをDecimalのDictionaryに保管しておく。
    public class AppSettingNumDic
    {
        Dictionary<string, decimal> dic = new Dictionary<string, decimal>();

        // decimalの範囲は10^28のオーダーなので、それを超える値は格納できない。Int32.MaxValueもだめ。
        public AppSettingNumDic()
        {
            foreach (string key in ConfigurationManager.AppSettings.AllKeys)
            {
                string value = ConfigurationManager.AppSettings[key];

                decimal valNum = -1;
                if (Decimal.TryParse(value, out valNum) == true)
                {
                    this.dic.Add(key, valNum);
                }
            }
        }

        public bool Exists(string key)
        {
            return this.dic.ContainsKey(key);
        }

        public int this[string key]
        {
            get { return Convert.ToInt32(this.dic[key]); }
        }

        public long GetLong(string key)
        {
            return Convert.ToInt64(this.dic[key]);
        }

        public decimal GetDecimal(string key)
        {
            return this.dic[key];
        }

        // Doubleは有効桁数が15-16桁なので小数点以下が切り捨てられる可能性がある。
        public double GetDouble(string key)
        {
            return Convert.ToDouble(this.dic[key]);
        }
    }
}

C#2.0:エクセルの操作クラス

エクセルのファイルを開き、操作するためのクラス。
IDisposableなのでusingでの使用を想定。
Excel.Subtotalメソッドは、複数回呼ぶことで小計、中計を作成することができる。
Excelのバージョンは2007を想定。
リソースリークは完全には無くなっていないかもしれない。
完全を求めるならば計測する必要があるかも。
.Net Framework 2.0用

2011/6/22: COMObjectのスイーパーつけたり、CSV,タブ区切りファイルを読み込めるようにしたり色々強化

using System;
using System.Collections.Generic;
using System.Text;
using System.Configuration;
using System.Runtime.InteropServices;
using Excel = Microsoft.Office.Interop.Excel;
using System.Data;
using System.IO;

namespace ComboAndTextBoxBinder
{
    /// <summary>
    /// COMObjectを保管しておき、dispose(usingの終了)時に解放する。
    /// </summary>
    public class COMObjSweeper : IDisposable
    {
        protected Queue<object> queue = new Queue<object>();
        protected object queueLock = new object();
        protected object refLock = new object();

        int refCount = 0;

        /// <summary>
        /// usingするときに取得するオブジェクト(Reference Counter付き)
        /// </summary>
        public COMObjSweeper Ref
        {
            get
            {
                lock (this.refLock)
                {
                    refCount++;
                }                
                return this;
            }

        }

        /// <summary>
        /// COMオブジェクトをAddする
        /// </summary>
        /// <param name="obj">COMObject</param>
        public void Add(object obj)
        {
            lock (this.queueLock)
            {
                this.queue.Enqueue(obj);
            }
        }

        /// <summary>
        /// オブジェクトの開放
        /// </summary>
        /// <param name="obj"></param>
        public static void ReleaseComObject(object obj)
        {
            try
            {
                if (obj != null)
                {
                    System.Runtime.InteropServices.Marshal.ReleaseComObject(obj);
                }
            }
            catch
            {
            }
        }

        /// <summary>
        /// COMオブジェクトを解放する。
        /// </summary>
        public void Sweep()
        {
            lock (this.queueLock)
            {
                while (this.queue.Count > 0)
                {
                    object obj = this.queue.Dequeue();
                    ReleaseComObject(obj);
                }
            }
        }

        #region IDisposable メンバ

        public void Dispose()
        {
            lock (this.refLock)
            {
                refCount--;
                if (refCount <= 0)
                {
                    Sweep();
                    refCount = 0;
                }
            }
        }

        #endregion
    }
        

    /// <summary>
    /// エクセルを開き、データ集計などを行うためのクラス
    /// </summary>
    public class ExcelUtil : IDisposable
    {
        protected Excel.Application oXls = null;
        protected Excel.Workbook oWBook = null; // workbookオブジェクト
        protected Excel.Worksheet oSheet; // Worksheetオブジェクト
        protected bool quitWhenDispose = true;

        protected COMObjSweeper localSwp = new COMObjSweeper();
        protected COMObjSweeper clsSwp = new COMObjSweeper();

        /// <summary>
        /// エクセルを表示するか否か?
        /// </summary>
        public bool Visible
        {
            set
            {
                this.oXls.Visible = value;
            }
            get
            {
                return this.oXls.Visible;
            }
        }

        /// <summary>
        /// Dispose時にエクセルを終了するか?
        /// </summary>
        public bool QuitWhenDispose
        {
            set
            {
                this.quitWhenDispose = value;
            }
            get
            {
                return this.quitWhenDispose;
            }
        }

        /// <summary>
        /// エクセルファイルのオープン
        /// </summary>
        /// <param name="excelFileName">開くファイル名</param>
        /// <param name="readOnly">リードオンリーで開くか?</param>
        /// <param name="displayAlarts">終了時のセーブしますか?などのアラートを表示するかどうか?</param>
        public void OpenBook(string excelFileName,bool visible,bool readOnly,bool displayAlarts)
        {
            this.oXls = new Excel.Application();
            this.Visible = visible;

            this.oXls.DisplayAlerts = displayAlarts;

            this.oWBook = (Excel.Workbook)(oXls.Workbooks.Open(
              excelFileName, Type.Missing, readOnly, Type.Missing,
                Type.Missing, Type.Missing, Type.Missing, Type.Missing,
                Type.Missing, Type.Missing, Type.Missing,
                Type.Missing, Type.Missing, Type.Missing, Type.Missing
            ));

            // アクティブシートがあることが前提
            this.oSheet = (Excel.Worksheet)oWBook.ActiveSheet;

            this.clsSwp.Add(this.oSheet);
            this.clsSwp.Add(this.oWBook);
            this.clsSwp.Add(this.oXls);
        }

        /// <summary>
        /// データテーブルのRowをエクセルの行に設定
        /// </summary>
        /// <param name="startRow">エクセルへの設定開始行</param>
        /// <param name="startCol">エクセルへの設定開始列</param>
        /// <param name="endCol">エクセルへの設定終了列</param>
        /// <param name="dr">設定するデータテーブル</param>
        public void SetFromDataRow(int startRow, int startCol,int endCol, DataRow dr)
        {
            foreach (object obj in dr.ItemArray)
            {
                SetCell(startRow, startCol, obj);
                startCol++;
                if (startCol >= endCol)
                {
                    return;
                }
            }
        }

        /// <summary>
        /// データテーブルをエクセルに設定
        /// </summary>
        /// <param name="startRow">1から始まる行</param>
        /// <param name="startCol">1から始まる列</param>
        /// <param name="dt">データテーブル</param>
        public int SetFromDataTable(int startRow,int startCol,int endCol, DataTable dt)
        {
            foreach (DataRow dr in dt.Rows)
            {
                SetFromDataRow(startRow, startCol, endCol, dr);
                startRow++;
            }

            return startRow;
        }

        /// <summary>
        /// データテーブルをエクセルに設定
        /// </summary>
        /// <param name="startRow">1から始まる行</param>
        /// <param name="startCol">1から始まる列</param>
        /// <param name="dt">データテーブル</param>
        public int SetFromDataTable(int startRow, int startCol, DataTable dt)
        {
            foreach (DataRow dr in dt.Rows)
            {
                SetFromDataRow(startRow, startCol,Int32.MaxValue, dr);
                startRow++;
            }

            return startRow;
        }

        /// <summary>
        /// 1行分のテキストファイルデータをエクセルに設定する。
        /// </summary>
        /// <param name="startRow">開始行</param>
        /// <param name="startCol">開始列</param>
        /// <param name="endCol">終了列(Int32.MaxValueでテキストデータの最後の列まで)</param>
        /// <param name="textRowData">1行分のデータ</param>
        /// <param name="delimiterChars">デリミタとなる文字群(タブ、カンマなど)</param>
        /// <param name="trimChars">1列分のデータの最初と最後で取り除く文字(ダブルクオーテーションなど)</param>
        public void SetFromTextLine(int startRow, int startCol, int endCol, string textRowData, string delimiterChars,string trimChars)
        {
            char[] delimiters = delimiterChars.ToCharArray();
            string[] split = textRowData.Split(delimiters);
            char[] trims = (trimChars == null)?null:trimChars.ToCharArray();

            foreach (string data in split)
            {
                string srcdata = data;
                if ((trims != null) && (trims.Length > 0))
                {
                    srcdata = srcdata.TrimStart(trims);
                    srcdata = srcdata.TrimEnd(trims);
                }

                SetCell(startRow, startCol, srcdata);
                startCol++;
                if (startCol >= endCol)
                {
                    return;
                }
            }
        }


        /// <summary>
        /// textfileの内容をエクセルに設定
        /// </summary>
        /// <param name="startRow">1から始まる行</param>
        /// <param name="startCol">1から始まる列</param>
        /// <param name="endCol">終了の列(Int32.MaxValueでテキストデータの最後の列まで)</param>
        /// <param name="textFileData">タブ、カンマ区切りのテキストファイル</param>
        /// <param name="delimiterChars">デリミタとなる文字群(タブ、カンマなど)</param>
        /// <param name="trimChars">1列分の最初と最後で取り除く文字(ダブルクオーテーションなど)</param>
        public int SetFromTextFile(int startRow, int startCol, int endCol, string textFileData, string delimiterChars, string trimChars)
        {            
            using(StringReader sr = new StringReader(textFileData))
            {
                while (sr.Peek() >= 0)
                {
                    string line = sr.ReadLine();
                    SetFromTextLine(startRow, startCol, endCol, line, delimiterChars,trimChars);
                    startRow++;
                }
            }

            return startRow;
        }

        /// <summary>
        /// textfileの内容をエクセルに設定
        /// </summary>
        /// <param name="startRow">1から始まる行</param>
        /// <param name="startCol">1から始まる列</param>
        /// <param name="textFileData">タブ、カンマ区切りのテキストファイル</param>
        /// <param name="delimiterChars">デリミタとなる文字群(タブ、カンマなど)</param>
        /// <param name="trimChars">1列分の最初と最後で取り除く文字(ダブルクオーテーションなど)</param>
        public int SetFromTextFile(int startRow, int startCol, string textFileData, string delimiterChars,string trimChars)
        {
            return SetFromTextFile(startRow, startCol,Int32.MaxValue, textFileData, delimiterChars,trimChars);
        }


        // カラムの値はExcelではアルファベットで表されるので、整数のカラム値をアルファベットのカラム値に変換する。
        public static string ToAlphabetCol(int colNum)
        {
            string alphaStr = "";
            //カラムは1オリジンなので引いておく
            // アルファベットの数で割る。
            for (int i = colNum; i > 0; i /= 26)
            {
                // Aが最初の位置なので、それを値で足し込むことでアルファベットの値を作成
                char modChar = Convert.ToChar('A' + ((i % 26) - 1));
                // 小さい桁から挿入するので、0の位置にインサート
                alphaStr = alphaStr.Insert(0, new string(modChar, 1));
            }

            return alphaStr;
        }

        // 数値の行、桁の値からアルファベット+数値の値に変更
        public static string ToExcelRowCol(int rowNum, int colNum)
        {
            return ToAlphabetCol(colNum) + rowNum.ToString();
        }

        public Excel.Range GetRange(int row, int col)
        {
            string rngStr = ToExcelRowCol(row, col);
            return GetRange(rngStr, rngStr);
        }

        /// <summary>
        /// Rangeオブジェクトの取得
        /// </summary>
        /// <param name="startRange">取得開始列行("A1"形式) nullでA1を選択</param>
        /// <param name="endRange">取得終了列行("A1"形式) nullで最終列行を選択</param>
        /// <returns>取得したRange</returns>
        public Excel.Range GetRange(string startRange, string endRange)
        {
            if (String.IsNullOrEmpty(startRange) == true)
            {
                startRange = "A1";
            }

            Excel.Range rng;
            if (String.IsNullOrEmpty(endRange) == true)
            {
                Excel.Range endRng = oSheet.Cells.SpecialCells(Excel.XlCellType.xlCellTypeLastCell, Type.Missing);
                rng = this.oSheet.get_Range(startRange, endRng);
                this.localSwp.Add(endRng);
                this.localSwp.Add(rng);
                return rng;
            }

            rng = this.oSheet.get_Range(startRange, endRange);
            this.localSwp.Add(rng);
            return rng;
        }

        /// <summary>
        /// 1行分のRangeオブジェクトを取得する。
        /// </summary>
        /// <param name="startRange">取得開始列行("A1"形式) nullで自動的に"A1"になる</param>
        /// <param name="endRange">取得終了列行("A1"形式) nullで自動的に取得開始行の最終列</param>
        /// <returns>取得したRange</returns>
        public Excel.Range GetOneLineRange(string startRange, string endRange)
        {
            if (String.IsNullOrEmpty(startRange) == true)
            {
                startRange = "A1";
            }

            Excel.Range startRng = this.GetRange(startRange, startRange);
            Excel.Range rng;
            if (String.IsNullOrEmpty(endRange) == true)
            {
                Excel.Range endRng = oSheet.Cells.SpecialCells(Excel.XlCellType.xlCellTypeLastCell, Type.Missing);
                rng = this.oSheet.get_Range(startRng, ExcelUtil.ToExcelRowCol(startRng.Row,endRng.Column));
                this.localSwp.Add(endRng);
                this.localSwp.Add(rng);
                return rng;
            }

            rng = this.oSheet.get_Range(startRange, endRange);
            this.localSwp.Add(rng);
            return rng;
        }

        /// <summary>
        /// セルの上下左右と間に罫線を設定する。
        /// </summary>
        /// <param name="startColRow">罫線の設定を開始する列行の文字列("A1"形式)</param>
        /// <param name="endColRow">罫線の設定を終了する列行の文字列("A1"形式)</param>
        /// <param name="aroundLineStyle">周りの罫線のラインスタイル
        /// xlLineStyleNone = -4142,
        /// xlDouble = -4119,
        /// xlDot = -4118,
        /// xlDash = -4115,
        /// xlContinuous = 1,
        /// xlDashDot = 4,
        /// xlDashDotDot = 5,
        /// xlSlantDashDot = 13, 
        /// </param>
        public void SetAllBorderLine(string startColRow, string endColRow, int aroundLineStyle, int betweenLineStyle)
        {
            using (this.localSwp.Ref)
            {
                Excel.Range rng = this.GetRange(startColRow, endColRow);

                rng.Borders.get_Item(Excel.XlBordersIndex.xlEdgeBottom).LineStyle = aroundLineStyle;
                rng.Borders.get_Item(Excel.XlBordersIndex.xlEdgeTop).LineStyle = aroundLineStyle;
                rng.Borders.get_Item(Excel.XlBordersIndex.xlInsideHorizontal).LineStyle = betweenLineStyle;

                rng.Borders.get_Item(Excel.XlBordersIndex.xlEdgeLeft).LineStyle = aroundLineStyle;
                rng.Borders.get_Item(Excel.XlBordersIndex.xlEdgeRight).LineStyle = aroundLineStyle;
                rng.Borders.get_Item(Excel.XlBordersIndex.xlInsideVertical).LineStyle = betweenLineStyle;
            }

        }

        /// <summary>
        /// 特定行のセルの下に罫線を設定する。
        /// </summary>
        /// <param name="startColRow">罫線の設定を開始する列行の文字列("A1"形式)</param>
        /// <param name="endColRow">罫線の設定を終了する列行の文字列("A1"形式) nullで開始行の最後まで罫線を表示</param>
        /// <param name="lineStyle">罫線のラインスタイル(Excel.Borders.LineStyle)</param>
        public void SetBottomBorderLine(string startColRow, string endColRow, int lineStyle)
        {
            using (this.localSwp.Ref)
            {
                Excel.Range rng = this.GetOneLineRange(startColRow, endColRow); // 引数は"A1"形式で指定
                rng.Borders.get_Item(Excel.XlBordersIndex.xlEdgeBottom).LineStyle = lineStyle;
            }
        }

        /// <summary>
        /// ヘッダーのメッセージを変更(中央ヘッダー)
        /// </summary>
        /// <param name="message"></param>
        public void SetPageCenterHeader(string message)
        {
            this.oSheet.PageSetup.CenterHeader = message;
        }

        /// <summary>
        /// フッターのメッセージを変更(右フッター)
        /// </summary>
        /// <param name="message"></param>
        public void SetPageRightFooter(string message)
        {
            this.oSheet.PageSetup.RightFooter = message;
        }

        /// <summary>
        /// 印刷時に必ず表示する行を設定($1:$2のような形で設定)
        /// </summary>
        /// <param name="rowAndColumns"></param>
        public void SetPageTitleRows(string rows)
        {
            this.oSheet.PageSetup.PrintTitleRows = rows;
        }

        /// <summary>
        /// セルを設定(引数は数値)
        /// </summary>
        /// <param name="row">設定する行(1オリジン)</param>
        /// <param name="col">設定する列(1オリジン)</param>
        /// <param name="message">設定する文字列</param>
        public void SetCell(int row, int col, object val)
        {
            SetCell(ToExcelRowCol(row, col),val);
        }

        /// <summary>
        /// セルを設定(引数はA1のような形式)
        /// </summary>
        /// <param name="colRow">設定する列行("A1"形式)</param>
        /// <param name="message">設定する文字列</param>
        public void SetCell(string colRow, object val)
        {
            using(this.localSwp.Ref)
            {
                Excel.Range rng = this.GetRange(colRow, colRow);
                rng.Value2 = val;
            }
        }

        /// <summary>
        /// セルに一度に設定(引数はA1のような形式)
        /// </summary>
        /// <param name="startColRow">設定する列行の開始位置("A1"形式)</param>
        /// <param name="startColRow">設定する列行の終了位置("A1"形式)</param>
        /// <param name="message">設定する文字列</param>
        public void SetCells(string startColRow,string endColRow, object val)
        {
            using (this.localSwp.Ref)
            {
                Excel.Range rng = this.GetRange(startColRow, endColRow);
                rng.Value2 = val;
            }
        }

        /// <summary>
        /// ソート処理を行う。
        /// </summary>
        /// <param name="startRowCol">ソート範囲の開始("A1"形式)</param>
        /// <param name="endRowCol">ソート範囲の終了("A1"形式)</param>
        /// <param name="firstKeyCol">第1キーの列の位置(1オリジン)</param>
        /// <param name="secondKeyCol">第2キーの列の位置(1オリジン)</param>
        /// <param name="thirdKeyCol">第3キーの列の位置(1オリジン)</param>
        /// <param name="ascending">昇順・降順(全てのキーに適用)</param>
        public void Sort(string startRowCol,string endRowCol,int firstKeyCol, int secondKeyCol, int thirdKeyCol,bool ascending)
        {
            using (this.localSwp.Ref)
            {
                Excel.Range rng = this.GetRange(startRowCol, endRowCol);
                object key1 = (firstKeyCol > 0) ? rng.Columns[firstKeyCol, Type.Missing] : Type.Missing;
                object key2 = (secondKeyCol > 0) ? rng.Columns[secondKeyCol, Type.Missing] : Type.Missing;
                object key3 = (thirdKeyCol > 0) ? rng.Columns[thirdKeyCol, Type.Missing] : Type.Missing;

                this.localSwp.Add(key1);
                this.localSwp.Add(key2);
                this.localSwp.Add(key3);

                Excel.XlSortOrder asc = Excel.XlSortOrder.xlAscending;
                if (ascending == false)
                {
                    asc = Excel.XlSortOrder.xlDescending;
                }

                rng.Sort(key1,
                    asc,
                    key2,
                    Type.Missing,
                    asc,
                    key3,
                    asc,
                    Excel.XlYesNoGuess.xlNo,
                    Type.Missing,
                    Type.Missing,
                    Excel.XlSortOrientation.xlSortColumns,
                    Excel.XlSortMethod.xlPinYin,
                    Excel.XlSortDataOption.xlSortNormal,
                    Excel.XlSortDataOption.xlSortNormal,
                    Excel.XlSortDataOption.xlSortNormal);
            }

        }

        /// <summary>
        /// エクセルファイルの集計を行う
        /// </summary>
        /// <param name="startColRow">開始列行文字列("A1"形式)</param>
        /// <param name="endColRow">終了列行文字列("A1"形式)</param>
        /// <param name="totalList">集計を行う列の値</param>
        /// <param name="groupByField">同じ値のものを集計して表示するための列の値</param>
        public void AggregateTotal(string startColRow,string endColRow, int[] totalList, int groupByField)
        {
            using (this.localSwp.Ref)
            {
                Excel.Range rng = this.GetRange(startColRow, endColRow);

                // 集計を行う。groupByFieldが同じ値のモノについて、totalListの値を集計して表示する。集計と総計を表示
                rng.Subtotal(groupByField, Excel.XlConsolidationFunction.xlSum, totalList,
                    false, Type.Missing, Excel.XlSummaryRow.xlSummaryBelow);
            }
        }

        /// <summary>
        /// セルを取得
        /// </summary>
        /// <param name="row">行の値(1オリジン)</param>
        /// <param name="col">列の値(1オリジン)</param>
        /// <returns></returns>
        public string GetCell(int row, int col)
        {
            return GetCell(ToExcelRowCol(row, col));
        }
        
        /// <summary>
        /// セルを取得(引数はA1のような形式)
        /// </summary>
        /// <param name="colRow">取得するセルの列行("A1"形式)</param>
        /// <returns>取得した値</returns>
        public string GetCell(string colRow)
        {
            using (this.localSwp.Ref)
            {
                Excel.Range rng = this.GetRange(colRow, colRow);
                string str = rng.Value2 as string;
                return str;
            }
        }



        #region IDisposable メンバ
        /// <summary>
        /// エクセルプログラムの終了を行いリソースを解放する。
        /// </summary>
        public void Dispose()
        {
            try
            {
                if ((this.quitWhenDispose == true) && (this.oWBook != null))
                {
                    this.oWBook.Close(false, Type.Missing, Type.Missing);
                }
            }
            catch
            {
            }

            try
            {
                if ((this.quitWhenDispose == true) && (this.oXls != null))
                {
                    this.oXls.Quit();
                }
            }
            catch
            {
            }

            this.localSwp.Sweep();
            this.clsSwp.Sweep();
        }

        #endregion

    }
}

SQLServer2005:なんちゃってレプリケーション

2つのDB間の特定のテーブルの内容を同期させるためのストアドプロシジャ。
ID列を使い、ID列の数値が元よりも大きい行を取得してコピー先にInsertする。
コピー元はUpdateやDeleteが行われることは想定していない。
(insertのみ)


コピー先にストアドプロシジャを作成し、バッチを使って定期的にタスクを
走らせて利用することを想定している。

insertするレコードの行数が大きい場合、コピー先のIDの値も比較し、
where句で制限することで一度にinsertする行数を減らせると思われる。

コピー元はSQLサーバ認証により認証。

set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
go


-- =============================================
-- Description:	なんちゃってレプリケーション。
-- IDはID列としてを予め設定.コピー元はinsertのみ。
-- =============================================
ALTER PROCEDURE [dbo].[BogusReplication]
AS
BEGIN
	declare @count bigint
	select @count=max(ID) From コピー先テーブル
	set @count = IsNull(@count,0)     -- 何も入っていないなら0を指定

	SET IDENTITY_INSERT コピー先テーブル ON -- ID列挿入可能に

	-- IDで取得していないものを自サーバのDBにコピー
    -- その際にOpenDataSourceを使い他サーバDBとの接続を行う。
    -- これを行うためには下記を予め実行する必要があるかも知れない。OPENROWSETの場合だけ?
    -- sp_configure 'show advanced options', 1;
    -- RECONFIGURE;
    -- GO
    -- sp_configure 'Ad Hoc Distributed Queries', 1;
    -- RECONFIGURE;
    -- GO
	INSERT INTO コピー先テーブル (ID列を含む列の列挙。ex:ID,a,b,c,d)
	SELECT ID列を含む列の列挙。ex:ID,a,b,c,d
	FROM OPENDATASOURCE('SQLNCLI','Data Source=コピー元サーバ;User ID=ログイン名;Password=パスワード').コピー元テーブル名
	where ID > @count
    -- 他サーバのIDが@count(自サーバの最終ID)以上のもののみが対象

	-- ID列の挿入不可にする
	SET IDENTITY_INSERT コピー先テーブル名 OFF
END

CSharp:コンボボックスとコンボの内容検索用テキストボックス

いまさらWindows FormsとC#2.0

自分で再利用するため晒しておく。
コンボボックスに表示用の文字と対応するコードを割り当て、
テキストボックスでその内容を検索できるようにするクラス。

ComboAndTextBoxBinder.cs

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;

namespace ComboAndTextBoxBinder
{
    /// <summary>
    /// コンボボックスに表示されるテキストとそれに対応する非表示のコード
    /// </summary>
    public class StrTextCode
    {
        string code;
        string text;

        public StrTextCode(string text,string value)
        {
            this.code = value;
            this.text = text;
        }

        /// <summary>
        /// コンボに表示しないコード
        /// </summary>
        public string Code
        {
            get { return this.code; }
            set { this.code = value; }
        }

        /// <summary>
        /// コンボに表示するテキスト
        /// </summary>
        public string Text
        {
            get { return this.text; }
            set { this.text = value; }
        }

        /// <summary>
        /// テキストを表示する。
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return this.text;
        }

        /// <summary>
        /// 等価判定
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public override bool Equals(object obj)
        {
            StrTextCode stc = obj as StrTextCode;
            if (stc == null)
            {
                return false;
            }

            return ((stc.Code == this.Code) && (stc.Text == this.Text));
        }

        /// <summary>
        /// ハッシュ値作成
        /// </summary>
        /// <returns></returns>
        public override int GetHashCode()
        {
            return (this.Code.GetHashCode() ^ this.Text.GetHashCode());
        }
    }

    /// <summary>
    /// 表示テキストとそれに対応するコードが入っているコンボボックスと
    /// コード検索用のテキストボックス及び
    /// コンボに表示されている文字を検索するテキストボックスを結びつける。
    /// コンボボックスはDropDownStyleがDropDownListを想定。
    /// 検索用テキストボックスの検索はValidatingイベント中に処理を行う。
    /// </summary>
    public class ComboAndTextBoxBinder
    {
        #region インスタンス定義
        ComboBox cmb = null; // コンボボックス
        TextBox codeTxtBox = null; // コード検索用テキストボックス
        TextBox textTxtBox = null; // コンボの表示内容検索用テキストボックス

        /// <summary>
        /// コンボの中身のテキストと対応するコードの検索用匿名メソッドの型
        /// </summary>
        /// <param name="ct"></param>
        /// <param name="findText"></param>
        /// <returns></returns>
        public delegate bool TextCodeFindFunc(StrTextCode ct, string findText);

        /// <summary>
        /// コンボの表示内容に対応するコードを検索する。
        /// </summary>
        TextCodeFindFunc codeFinder = delegate(StrTextCode ct, string findCode)
        {
            if ((findCode == null) || (ct.Code == null))
            {
                return false;
            }
            // コードが完全に合致すればtrue
            return (ct.Code.Trim() == findCode.Trim());
        };

        /// <summary>
        /// コンボの表示内容を検索する。
        /// </summary>
        TextCodeFindFunc textFinder = delegate(StrTextCode ct, string findText)
        {
            if ((findText == null) || (ct.Text == null))
            {
                return false;
            }
            // コンボの表示内容が一部でも合致すればtrue
            return ct.Text.Trim().Contains(findText.Trim());
        };

        // 検索した結果見つからなかった場合に設定するデフォルトのコンボボックスItemのインデックス
        int defalultCmbIndex = -1;
        #endregion

        #region パブリックメソッド
        /// <summary>
        /// コンボボックスとテキストボックスを結びつけるコンストラクタ。
        /// コントロールの破棄については本クラスを使用する側が責任をもつこと。
        /// </summary>
        /// <param name="comboBox">テキストボックスと結びつけるコンボボックス</param>
        /// <param name="codeTextBox">コンボボックスと結びつけるコード検索用テキストボックス</param>
        /// <param name="textTextBox">コンボボックスと結びつけるコンボの表示内容検索用テキストボックス</param>
        /// <param name="useEnterKey">Enterキーで次のコントロールに移動させるか否か?</param>
        public ComboAndTextBoxBinder(ComboBox comboBox, TextBox codeTextBox, TextBox textTextBox,bool useEnterKey)
        {
            this.cmb = comboBox;
            this.codeTxtBox = codeTextBox;
            this.textTxtBox = textTextBox;

            //  コード検索用テキストボックスのイベントハンドラを設定
            if (codeTextBox != null)
            {
                this.codeTxtBox.Validating += new System.ComponentModel.CancelEventHandler(codeTxtBox_Validating);
                if (useEnterKey == true)
                {
                    this.codeTxtBox.KeyDown += new KeyEventHandler(control_CheckEnterKey);
                }
            }

            // コンボ表示内容検索用テキストボックスのイベントハンドラを設定
            if (textTextBox != null)
            {
                this.textTxtBox.Validating += new System.ComponentModel.CancelEventHandler(textTxtBox_Validating);
                if (useEnterKey == true)
                {
                    this.textTxtBox.KeyDown += new KeyEventHandler(control_CheckEnterKey);
                }
            }

            // コンボでEnterキーを押したとき用ハンドラ
            if (useEnterKey == true)
            {
                this.cmb.KeyDown += new KeyEventHandler(control_CheckEnterKey);
            }
        }

        /// <summary>
        /// コンボ表示用テキストと対応するコードのリストを設定する。
        /// </summary>
        /// <param name="txtAry">コンボ表示用テキストの配列</param>
        /// <param name="codeAry">対応するコードの配列。不要ならnullをセット</param>
        /// <param name="defaultCode">検索が失敗した場合に選択されるコンボのコード。不要ならnullをセット</param>
        public void SetTextCodes(string[] txtAry, string[] codeAry, string defaultCode)
        {
            // コードがnullならば空配列に            
            codeAry = (codeAry == null) ? new string[] { } : codeAry;

            for (int i = 0; i < txtAry.Length; i++)
            {
                string code = (codeAry.Length > i) ? codeAry[i] : null;
                this.cmb.Items.Add(new StrTextCode(txtAry[i], code));
                if ((defaultCode == code) && (code != null))
                {
                    this.defalultCmbIndex = i;
                    this.cmb.SelectedIndex = i;
                }
            }
        }

        /// <summary>
        /// コード検索用の匿名メソッドを設定
        /// </summary>
        /// <param name="finder">コードの内容検索用メソッド</param>
        public void SetCodeFinder(TextCodeFindFunc finder)
        {
            this.codeFinder = finder;
        }

        /// <summary>
        /// コンボの表示テキストを検索するための匿名メソッドを設定
        /// </summary>
        /// <param name="finder">コンボの表示テキスト検索用メソッド</param>
        public void SetTextFinder(TextCodeFindFunc finder)
        {
            this.textFinder = finder;
        }
        #endregion

        #region プライベートメソッド
        /// <summary>
        /// テキストボックスによる検索が失敗したときにテキストボックスの内容をクリア
        /// </summary>
        /// <param name="txtBox">テキストボックスコントロール</param>
        void ClearTextBox(TextBox txtBox)
        {
            if (txtBox == null)
            {
                return;
            }

            txtBox.Text = "";
        }

        /// <summary>
        /// コンボの内容(コード/テキスト)を実際に検索し、対応するコンボボックスを選択する。
        /// 検索処理の詳細はtextCodeFindFuncに記述する。
        /// </summary>
        /// <param name="txtBox">検索用テキストボックス</param>
        /// <param name="func">検索用匿名メソッド</param>
        /// <returns>検索に成功したかどうか</returns>
        bool SelectComboFromTextBox(TextBox txtBox, TextCodeFindFunc func)
        {
            if (txtBox == null)
            {
                return true;
            }

            string val = txtBox.Text ?? "";
            foreach (object obj in cmb.Items)
            {
                StrTextCode ct = obj as StrTextCode;

                if (func(ct, val) == true)
                {
                    cmb.SelectedItem = ct;
                    return true;
                }
            }
            return false;
        }
        #endregion

        #region イベントハンドラ
        /// <summary>
        /// テキストボックスの内容と合致するコンボの表示内容を検索する。
        /// 表示する内容が見つからなければテキストボックスを空にしてValidatingをキャンセル
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void textTxtBox_Validating(object sender, System.ComponentModel.CancelEventArgs e)
        {
            if (String.IsNullOrEmpty(this.textTxtBox.Text) == true)
            {
                return;
            }

            if ((this.SelectComboFromTextBox(this.textTxtBox, textFinder) == false)
                && (this.defalultCmbIndex != -1))
            {
                this.cmb.SelectedIndex = this.defalultCmbIndex;
                ClearTextBox(this.textTxtBox);
                e.Cancel = true;
            }
            ClearTextBox(this.codeTxtBox);
        }

        /// <summary>
        /// テキストボックスの内容と合致するコードをコンボから検索する。
        /// 表示する内容が見つからなければテキストボックスを空にしてValidatingをキャンセル
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void codeTxtBox_Validating(object sender, System.ComponentModel.CancelEventArgs e)
        {
            if (String.IsNullOrEmpty(this.codeTxtBox.Text) == true)
            {
                return;
            }

            if ((this.SelectComboFromTextBox(this.codeTxtBox, codeFinder) == false) 
                && (this.defalultCmbIndex != -1))
            {
                this.cmb.SelectedIndex = this.defalultCmbIndex;
                ClearTextBox(this.codeTxtBox);
                e.Cancel = true;
            }
            ClearTextBox(this.textTxtBox);
        }

        /// <summary>
        /// エンターキーが押されたかをチェックし、
        /// 次のコントロールに移動する(KeyDownイベント)
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void control_CheckEnterKey(object sender, KeyEventArgs e)
        {
            if (e.KeyCode != Keys.Enter)
            {
                return;
            }

            Control ctrl = sender as Control;
            if (ctrl == null)
            {
                return;
            }

            ctrl.Parent.SelectNextControl(ctrl, true, true, true, true);
        }
        #endregion
    }

}


上記の使用例:
Form1.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace ComboAndTextBoxBinder
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            // コンボボックスと検索用テキストボックス初期化
            ComboAndTextBoxBinder nyugaiComboBind = new ComboAndTextBoxBinder(this.comboBox1, this.textBox1, this.textBox2, true);
            // 表示内容はすべて、りんご、オレンジ。対応するコードはなし,0001,00002,デフォルトはコードなし=全て
            nyugaiComboBind.SetTextCodes(new string[] { "全て", "りんご", "オレンジ" }, new string[] { "", "0001", "0002" }, "");
        }

    }
}

上記は適当にFormにコンボボックス1つ(comboBox1)とテキストボックス2つ(textBox1,textBox2)あればいいはずなので、Form1.Deigner.csは晒さない。

追記:
StrTextCodeに等価判定を追加(SelectedItemで等価判定が呼ばれるため)
GetHashCodeをオーバーライド