VB C# .NET Freamworkの備忘録

C#, VB.NET Freamworkの備忘録を掲載しています。 コントロール、WPF、スレッド、共通関数と実用的なコードを掲載してきます。

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Objects;
using System.Linq;
using System.Net;
using System.Net.Mail;


namespace Web.Mail
{
    /// <summary>
    /// メール送信機能を提供するクラス
    /// </summary>
    public class SmtpMailClient : MailClient
    {

        /// <summary>
        /// 更新フラグ
        /// </summary>
        public enum SendStatus : int
        {
            Default = 0,
            Success = 1,
            Error = -1,
            Cancell = -2,
        }


        #region 定数

        /// <summary>
        /// メールクライアント数の最大数
        /// </summary>
        private const int MAX_CLIENT_COUNT = 1;

        /// <summary>
        /// 最大のメールクライアント検索回数
        /// </summary>
        private const int MAX_LIMIT_COUNT = 100;

        /// <summary>
        /// メール送信の待機時間(すべてSMTPクライアントがメール送信中の場合)ミリ秒
        /// </summary>
        private const int WAIT_TIME = 2000;

        #endregion

        #region 内部変数


        /// <summary>
        /// 利用しているSMTPクライアント
        /// </summary>
        private List<CustomSmtpClient> Clients = null;
        /// <summary>
        /// 終了フラグ
        /// </summary>
        private bool IsEnd { get; set; }

        /// <summary>
        /// コールバック更新処理
        /// </summary>
        private Action<long, int> UpdateCallback = null;

        #endregion

        #region コンストラクタ

        /// <summary>
        /// コンストラクタ
        /// </summary>
        public SmtpMailClient()
            : this(null)
        {

        }

        /// <summary>
        /// コンストラクタ
        /// </summary>
        public SmtpMailClient(Action<long, int> callback)
            : base()
        {
            this.IsEnd = false;
            this.Clients = new List<CustomSmtpClient>();
            this.UpdateCallback = callback;
        }

        #endregion

        #region SMTPサーバ

        /// <summary>
        /// SMTPサーバの取得
        /// </summary>
        /// <param name="mail"></param>
        private CustomSmtpClient CreateSmtpClient()
        {
            var smtpClient = new CustomSmtpClient();


            switch (this.SmtpConnectionType)
            {
                case SmtpConnectionType.SMTPOverSSL:
                    // SMTP over SSLは465版ポート固定
                    smtpClient.EnableSsl = true;
                    smtpClient.Host = this.SmtpServer;
                    smtpClient.Port = 465;

                    break;
                case SmtpConnectionType.STARTTLS:
                    // STARTTLSは"SSL"を付ける
                    smtpClient.EnableSsl = true;
                    smtpClient.Host = this.SmtpServer;
                    smtpClient.Port = this.SmtpPort;
                    break;
                default:
                    smtpClient.EnableSsl = false;
                    smtpClient.Host = this.SmtpServer;
                    smtpClient.Port = this.SmtpPort;
                    break;
            }


            smtpClient.Credentials = new NetworkCredential(this.SmtpUser, this.SmtpPassword);
            smtpClient.DeliveryMethod = SmtpDeliveryMethod.Network;

            //アンチウィルス系のアプリケーションを使用しており、送信されるメールのチェックを行っている場合などでは、
            //SmtpClient.Sendメソッドでメールを送信してもすぐには送信されない場合があるので
            //1ミリ秒にして対応
            smtpClient.ServicePoint.MaxIdleTime = 1;


            //
            // POP before SMTPを使用する場合は、一度POPサーバにログイン
            //
            if (this.PopBeforeSmtp)
            {
                // POPログイン
                ConnectPopBeforeSmtp(this.PopServer, this.PopPort, this.PopUser, this.PopPassword);
            }


            // メールの完了イベント追加
            smtpClient.SendCompleted += new SendCompletedEventHandler(SmtpClient_SendCompleted);

            return smtpClient;
        }

        /// <summary>
        /// SMTPの取得
        /// </summary>
        /// <returns></returns>
        private CustomSmtpClient GetSmtpClient()
        {

            int limitCount = 0;

            while (limitCount < MAX_LIMIT_COUNT)
            {

                // 送信中でないSMTPクライアントの取得
                foreach (var client in this.Clients)
                {
                    // 送信中か否かチェック
                    OutputDebugString(System.DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss.fff") + " SmtpMailClient():Smtp Client Wait[client.IsSending:" + client.IsSending.ToString() + "]\n");
                    if (client.IsSending == false)
                        return client;
                }


                // SMTPクライアントの作成
                if (this.Clients.Count < MAX_CLIENT_COUNT)
                {
                    // SMTPの作成
                    var client = CreateSmtpClient();
                    //追加
                    this.Clients.Add(client);

                    return client;
                }


                limitCount = limitCount + 1;

                // メール送信に時間がかっかているため、一定時間待機
                OutputDebugString(System.DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss.fff") + " SmtpMailClient():Smtp Client Wait[milliSeconds:" + WAIT_TIME.ToString() + "]\n");
                System.Threading.Thread.Sleep(WAIT_TIME);


            }

            OutputDebugString(System.DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss.fff") + " SmtpMailClient():Smtp Client Wait Over[LIMIT_COUNT:" + limitCount.ToString() + "]\n");

            // 一定時間待機しても、メール送信ができない場合、例外を発生させて終了する。
            foreach (var item in this.Clients)
                Release(item);

            throw new Exception("メールの送信処理でタイムアウトが発生しました。");
        }

        #endregion

         /// <summary>
        /// メールを送信する
        /// </summary>
        /// <param name="mail">メールデータ</param>
        /// <returns>送信結果</returns>
        public void SendAsync(Mail mail)
        {
            this.SendAsync(new List<Mail>() { mail });
        }


        /// <summary>
        /// メールを送信する
        /// </summary>
        /// <param name="mail">メールデータ</param>
        /// <returns>送信結果</returns>
        public void SendAsync(List<Mail> mails)
        {
    
            if (this.IsEnd)
            {
                throw new Exception("Not Call 2 times.");
            }

            try
            {

                OutputDebugString(System.DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss.fff") + " SmtpMailClient():Start\n");



                int complete = 0;
                // メッセージの送信
                foreach (var item in mails)
                {

                    CustomSmtpClient smtpClient = GetSmtpClient();
                    OutputDebugString(System.DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss.fff") + " SmtpMailClient():GetSmtpClient[MailDataId:" + item.MailDataId.ToString() + "]\n");


                    if (item.To.Count <= 0)
                    {
                        OutputDebugString(System.DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss.fff") + " SmtpMailClient():item.To.Count <= 0\n");
                        continue;
                    }


                    smtpClient.IsSending = true;

                    // メッセージの作成
                    var mail = new CustomMailMessage();

                    mail.MailDataId = item.MailDataId;
                    
                    // 送信元の設定
                    mail.From = new System.Net.Mail.MailAddress(item.From);

                    //
                    // TO, CC, BCCフィールドの設定
                    //
                    foreach (string strTo in item.To)
                        mail.To.Add(strTo);

                    foreach (string strCc in item.Cc)
                        mail.CC.Add(strCc);

                    foreach (string strBcc in item.Bcc)
                        mail.Bcc.Add(strBcc);

                    System.Text.Encoding enc = System.Text.Encoding.GetEncoding(50220);

                    // 件名
                    mail.Subject = item.Subject;
                    mail.SubjectEncoding = enc;

                    //本文
                    mail.Body = item.Body;
                    mail.BodyEncoding = enc;

                    try
                    {
                        // 同一のSMTPサーバでメールを非同期にメール送信する。
                        smtpClient.SendAsync(mail, mail);

                        // 他スレッドの実行を行う。
                        System.Threading.Thread.Sleep(1);

                        complete++;
                    }
                    catch (Exception ex)
                    {
                        OutputDebugString(System.DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss.fff") + "[Error]SmtpMailClient():SendAsync[MailDataId:" + item.MailDataId.ToString() + "] ErroMsg:" + ex.ToString() + "\n");

                        // メールデータの更新
                        this.InvkeUpdateMailData(item.MailDataId, (int)SendStatus.Error);

                        mail.Dispose();

                    }
                }

                OutputDebugString(System.DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss.fff") + " SmtpMailClient():End[SuccessCount:" + complete.ToString() + "]\n");
            }
            catch (Exception ex)
            {
                OutputDebugString(System.DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss.fff") + "[Error]SmtpMailClient():Send ErroMsg:" + ex.ToString() + "\n");
                throw new Exception("SmtpMailClient Error", ex);
            }
            finally
            {
                this.IsEnd = true;

                // 送信中以外のリソースを破棄する。
                this.Dispose();
            }
        }

        #region メール送信完了イベント

        /// <summary>
        /// メール送信完了した際にコールバックされます。
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SmtpClient_SendCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
        {
            var smtpClient = sender as CustomSmtpClient;
            var mailMsg = e.UserState as CustomMailMessage;

            
            int intResult = 0;
            if (e.Cancelled)
            {
                //キャンセル
                OutputDebugString(System.DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss.fff") + "[Cancelled]SmtpMailClient():SmtpClient_SendCompleted[MailDataId:" + mailMsg.MailDataId.ToString() + "] \n");

                intResult = (int)SendStatus.Cancell;

            }
            else if(e.Error != null)
            {
                //エラー
                OutputDebugString(System.DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss.fff") + "[Error]SmtpMailClient():SmtpClient_SendCompleted[MailDataId:" + mailMsg.MailDataId.ToString() + "] ErroMsg:" + e.Error.ToString() + "\n");

                intResult = (int)SendStatus.Error;

            }
            else
            {
                OutputDebugString(System.DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss.fff") + "[Success]SmtpMailClient():SmtpClient_SendCompleted[MailDataId:" + mailMsg.MailDataId.ToString() + "] \n");
                //送信完了
                intResult = (int)SendStatus.Success;
            }

            // メッセージの解放
            mailMsg.Dispose();

            // メールデータの更新
            this.InvkeUpdateMailData(mailMsg.MailDataId, intResult);

            // 終了処理
            this.EndCallback(mailMsg.MailDataId, smtpClient);

            OutputDebugString(System.DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss.fff") + "[Success]SmtpMailClient():SmtpClient_SendCompleted End[MailDataId:" + mailMsg.MailDataId.ToString() + "] \n");
        }

        /// <summary>
        /// SMTPクライアントの終了処理を行います。
        /// </summary>
        /// <param name="client"></param>
        private void EndCallback(long mailDataId, CustomSmtpClient client)
        {
            OutputDebugString(System.DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss.fff") + "SmtpMailClient():EndCallback Flag:OFF[MailDataId:" + mailDataId.ToString() + "] \n");

            // 送信中のフラグを戻す
            client.IsSending = false;
            OutputDebugString(System.DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss.fff") + " SmtpMailClient():EndCallback[client.IsSending:" + client.IsSending.ToString() + "]\n");
            // メールの送信処理が終了しているかチェック
            if (this.IsEnd)
            {
                // 終了している場合は、SMTPのリソースの破棄を行う。
                Release(client);
            }
        }

        #endregion

        #region 更新処理
         
        /// <summary>
        /// 更新処理を行います。
        /// </summary>
        /// <param name="mailDataId"></param>
        /// <param name="intResult"></param>
        delegate void UpdateDelgate(long mailDataId, int intResult);

        /// <summary>
        /// メール送信の結果を更新します。
        /// </summary>
        /// <param name="mailDataId"></param>
        /// <param name="intResult"></param>
        private void InvkeUpdateMailData(long mailDataId, int intResult)
        {
            OutputDebugString(System.DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss.fff") + "SmtpMailClient():InvkeUpdateMailData [MailDataId:" + mailDataId.ToString() + "] \n");

            if (this.UpdateCallback != null)
            {
                // 非同期にて実行
                var thread = new UpdateDelgate(this.UpdateCallback);
                IAsyncResult ar = thread.BeginInvoke(mailDataId, intResult, null, null);
            }


        }

           #endregion

        #region Pop Before Smtp

        /// <summary>
        /// Pop Before Smtp認証のためPOPサーバに接続
        /// </summary>
        /// <param name="serv">POPサーバー</param>
        /// <param name="port">POPポート番号</param>
        /// <param name="user">ユーザID</param>
        /// <param name="pass">パスワード</param>
        public void ConnectPopBeforeSmtp(String serv, int port, String user, String pass)
        {

            try
            {
                String rstr;
                using (var client = new System.Net.Sockets.TcpClient())
                {

                    // POPサーバーに接続
                    client.Connect(serv, port);

                    using (var stream = client.GetStream())
                    {

                        // POPサーバー接続時のレスポンス受信
                        rstr = WriteAndRead(stream, "");
                        if (rstr.IndexOf("+OK") != 0)
                        {
                            throw new Exception("POPサーバー接続エラー");
                        }

                        // ユーザIDの送信
                        rstr = WriteAndRead(stream, "USER " + user + "\r\n");
                        if (rstr.IndexOf("+OK") != 0)
                        {
                            throw new Exception("ユーザIDエラー");
                        }

                        // パスワードの送信
                        rstr = WriteAndRead(stream, "PASS " + pass + "\r\n");
                        if (rstr.IndexOf("+OK") != 0)
                        {
                            throw new Exception("パスワードエラー");
                        }
                        // ステータスの送信
                        rstr = WriteAndRead(stream, "STAT" + "\r\n");
                        if (rstr.IndexOf("+OK") != 0)
                        {
                            throw new Exception("STATエラー");
                        }

                        // 終了の送信
                        rstr = WriteAndRead(stream, "QUIT" + "\r\n");


                        stream.Close();
                        stream.Dispose();
                    }

                    client.Close();
                }
            }
            catch (Exception ex)
            {
                OutputDebugString(System.DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss.fff") + "[Error]SmtpMailClient():PopBeforeSmtp ErroMsg:" + ex.ToString() + "\n");
                throw ex;

            }
        }

        /// <summary>
        /// POPサーバ送受信
        /// </summary>
        /// <param name="stm">ストリーム</param>
        /// <param name="req">リクエスト</param>
        /// <returns>レスポンス</returns>
        private String WriteAndRead(System.Net.Sockets.NetworkStream stm, String req)
        {

            // POPサーバへリクエスト送信
            if (req != "")
            {
                Byte[] sdata;
                sdata = System.Text.Encoding.ASCII.GetBytes(req);
                stm.Write(sdata, 0, sdata.Length);
            }
            for (int i = 1; i < 300; i++)
            {
                if (stm.DataAvailable) break;
                System.Threading.Thread.Sleep(10);
            }

            // POPサーバからのレスポンス受信
            String rtn = "";
            Byte[] rdata = new Byte[1024];
            while (stm.DataAvailable)
            {
                int l = stm.Read(rdata, 0, rdata.Length);
                if (l > 0)
                {
                    Array.Resize<Byte>(ref rdata, l);
                    rtn = rtn + System.Text.Encoding.ASCII.GetString(rdata);
                }
            }

            // レスポンス返信
            return rtn;
        }
        #endregion

        #region リソース破棄

        /// <summary>
        /// リソースの破棄を行います。
        /// </summary>
        private void Dispose()
        {
            foreach (var item in this.Clients)
            {
                if (item.IsSending == false)
                    Release(item);

            }

        }

        /// <summary>
        /// SMTPClinetのリリース
        /// </summary>
        /// <param name="smtp"></param>
        private void Release(CustomSmtpClient smtp)
        {
            // QUIT メッセージを SMTP サーバーに送信し、TCP 接続を適切に終了して、すべてのリソースを解放します。
            smtp.SendCompleted -= new SendCompletedEventHandler(SmtpClient_SendCompleted);
            smtp.Dispose();
        }

        #endregion


    }
}

Azureで遅いと思ったら・・・

SQL Azureパフォーマンスチューニングのための情報収集

SQL AzureのレスポンスタイムとNW通信
実行時間=SQLの送信時間+DBの処理時間+結果の受信時間

NWの通信時間を調べるためには
結果の受信時間が限りなく0に近づくSQLを発行する。「SELECT 1」

http://gihyo.jp/admin/serial/01/sql_azure/0005

1. 統計情報を更新する

統計情報を最新の状態にすることで、現在のデータ分布に最適な実行プランが選択されるようになる可能性があります。


■データベース単位で実行   

EXEC sp_updatestats;


sp_updatestats (Transact-SQL)
http://msdn.microsoft.com/ja-jp/library/ms173804.aspx



■テーブル単位 or インデックス単位で実行

UPDATE STATISTICS <テーブル名 or インデックス付きビュー名> <インデックス名 or 統計名>;


使用例) tbl_01 テーブルのインデックス idx_01 の統計を更新します。

UPDATE STATISTICS dbo.tbl_01 idx_01;


UPDATE STATISTICS (Transact-SQL)
http://msdn.microsoft.com/ja-jp/library/ms187348.aspx

WITH FULLSCAN  オプション
すべての行をスキャンして統計を計算することにより、高品質のクエリプランを作成できる場合もあります。


2. 実行プランを作成し直す

パラメータクエリ、ストアドプロシージャに有効です。
実行プラン生成時に使用されたパラメータ値が特殊だった場合、大半のパラメータ値にとっては最適な実行プランになっていないことがあります。   
実行プランを作成し直すことによって、より適した実行プランが生成される可能性があります。

アドホッククエリの中には毎回実行プランが作成されるものもあるため、そのようなクエリに対してはこの対処は効果が期待できません。


プランキャッシュをクリアする(インスタンス単位)   


DBCC FREEPROCCACHE;


DBCC FREEPROCCACHE (Transact-SQL)
http://msdn.microsoft.com/ja-jp/library/ms174283(v=sql.110).aspx



次回実行時にリコンパイルする(テーブル or ストアドプロシージャ単位)


EXEC sp_recompile N'<テーブル名 or ストアドプロシージャ名>'; 


使用例) tbl_01 テーブルを対象とするストアド プロシージャおよびトリガーが次回実行時に再コンパイルされます。

EXEC sp_recompile N'dbo.tbl_01';


sp_recompile (Transact-SQL)
http://msdn.microsoft.com/ja-jp/library/ms181647(v=sql.110).aspx



インデックスを再構築する

インデックスの断片化を解消することにより、I/O 負荷を軽減し、パフォーマンスが向上する可能性があります。

ALTER INDEX <インデックス名> ON <テーブル名> REBUILD;


使用例) tbl_01 テーブルのインデックス idx_01 を再構築します。

ALTER INDEX idx_01 ON dbo.tbl_01 REBUILD;


ALTER INDEX (Transact-SQL)
http://msdn.microsoft.com/ja-jp/library/ms188388.aspx 


※ 断片化率の確認方法

以下のクエリを実行すると、再構築が必要と思われるインデックスに対する ALTER INDEX ... REBUILD 文が生成されます。
再構築の対象とする条件は、断片化率が 30 % 以上かつページの合計数が 1000 ページ以上のインデックスです。


USE <データベース名>;
GO
SELECT 'ALTER INDEX ' + '[' + C.name + ']' + ' ON [' + D.name + '].[' + B.name + '] REBUILD' cmd,     
             D.name AS schemaname,     
             B.name AS table_name,     
             C.name AS index_name,     
             C.index_id,
             A.partition_number,
             A.avg_fragmentation_in_percent,
             A.page_count
  FROM sys.dm_db_index_physical_stats (DB_ID(),null,null,null,null) as A
    JOIN  sys.objects AS B
      ON  A.object_id = B.object_id
    JOIN  sys.indexes AS C
      ON  A.object_id = C.object_id  AND A.index_id = C.index_id
    JOIN  sys.schemas D
      ON  B.schema_id = D.schema_id
WHERE B.type = 'U'
      and C.index_id > 0
      and A.page_count > 1000
      and A.avg_fragmentation_in_percent > 30
ORDER BY A.avg_fragmentation_in_percent DESC;
GO

sys.dm_db_index_physical_stats (Transact-SQL)
http://msdn.microsoft.com/ja-jp/library/ms188917.aspx

補足

インデックスの再構築によりインデックスは再作成されます。インデックスが再構築される時、同時に統計情報も作成されます。ここで作成される統計情 報は、データサンプル率 100% で作成されます。インデックス作成時には、すべての行が読み取られるため、その読み取られた行を使って、同時に統計情報も作成されるためです。

これは、テーブルにデータがある状態で、新規にインデックスを作成した場合も同様です。


この状態で再度統計情報を更新することは、仮に 100% のサンプル率で更新したとしても、同じことを繰り返す分だけ無駄です。また、統計情報の更新 (UPDATE STATISTICS や sp_updatestats の実行) を行う時、統計を作成するために参照されるデータは、明示的にサンプル率 100% と指定していない限り、既定では、ランダムに読み取られた少数のデータです。ある程度大きなテーブルでは、テーブル全体の行の数パーセントです。せっかく 全データを使って作成された統計情報を破棄してまで、少数のデータで統計情報を作り直す必要はありません。サンプルデータは多い方が精度の高い統計になり ます。



↑このページのトップヘ