2012年9月30日日曜日

[C#] NLog

http://news.mynavi.jp/articles/2010/03/19/nlog/index.html

ログの出力には log4net を使っていたのだけどこういうのもあったんだー
log4netに比べて導入がかんたん!と書いてある。


NuGet の Package Manager Consoleからインストールした

こんな感じでコマンド打った
http://nuget.org/packages/NLog 

PM> Install-Package NLog

これだけでインストール完了
便利!!



VisualStudioのプロジェクトに、
NLogの設定ファイルであるところの NLog.config ファイルを追加する。


設定ファイル(NLog.config )のおれおれサンプル

<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

  <!--(1)出力先(ターゲット)の設定-->
  <targets>
    <!--ファイル(100KByte毎ローテーション)-->
    <target name="file" xsi:type="File" fileName="${basedir}/logs/log.txt"
            layout="${longdate}|${level:uppercase=true}|${logger}|${message} ${exception:format=tostring}"
            archiveFileName="${basedir}/logs/${date:format=yyyyMMdd}/log.{###}.txt"
            archiveAboveSize="102400"
            archiveNumbering="Sequence"
            maxArchiveFiles="100" />
  </targets>

  <!--(2)出力ルールの設定-->
  <rules>
    <!-- デバッグレベル以上のログをfileターゲットに出力-->
    <logger name="*" minlevel="Debug" writeTo="file" />
  </rules>

</nlog>




出力のレイアウトは、デフォルトのレイアウトに例外の文字列を足しただけ。


プログラムから出力してみる



    using NLog;

    public class LoggingTest
    {
        // ロガーの生成
        private static Logger logger = LogManager.GetCurrentClassLogger();

        public static void Test()
        {
            logger.Info("Infoメッセージ");
            logger.Warn("Warnメッセージ");
            logger.Error("Errorメッセージ");
            // 他にもいろいろある(適当)

            try
            {
                ExceptionTest();
            }
            catch (Exception ex)
            {
                logger.ErrorException("予期せぬエラーが発生しました", ex);
                // InfoException とか WarnException もあるみたい(適当)
            }
        }

        public static void ExceptionTest()
        {
            try
            {
                throw new Exception("これは内部例外だよ");
            }
            catch (Exception ex)
            {
                throw new Exception("なんかエラー発生!", ex);
            }
        }
    }



例外の詳細をロギングするには ErrorException を使う必要があるようだ。


ログファイルにはこんな感じで書き込まれた。


2012-09-25 18:04:07.9166|INFO|TestApplication1.LoggingTest|Infoメッセージ
2012-09-25 18:04:07.9296|WARN|TestApplication1.LoggingTest|Warnメッセージ
2012-09-25 18:04:07.9296|ERROR|TestApplication1.LoggingTest|Errorメッセージ
2012-09-25 18:04:07.9296|ERROR|TestApplication1.LoggingTest|予期せぬエラーが発生しました System.Exception: なんかエラー発生! ---> System.Exception: これは内部例外だよ
   場所 TestApplication1.LoggingTest.ExceptionTest() 場所 C:\VisualStudioProjects\TestApplication1\TestApplication1\Logging.cs:行 37
   --- 内部例外スタック トレースの終わり ---
   場所 TestApplication1.LoggingTest.ExceptionTest() 場所 C:\VisualStudioProjects\TestApplication1\TestApplication1\Logging.cs:行 41
   場所 TestApplication1.LoggingTest.Test() 場所 C:\VisualStudioProjects\TestApplication1\TestApplication1\Logging.cs:行 24



ところで NuGet ってなんて読むの?ニューゲット?エヌユーゲット?ヌゲット?

2012年9月23日日曜日

[ASP.NET MVC] WebAPI で画像を返す

画像ファイルを読み込んで返す。

        // ロギング用
        private static Logger logger = LogManager.GetCurrentClassLogger();

        // ローカル画像を読み込んで返す
        public HttpResponseMessage Get()
        {
            try
            {
                var stream = new FileStream(@"C:\temp\1.png", FileMode.Open);

                var res = new HttpResponseMessage()
                {
                    Content = new StreamContent(stream)
                    ,
                    StatusCode = System.Net.HttpStatusCode.OK
                };
                res.Content.Headers.ContentType 

                    = new System.Net.Http.Headers.MediaTypeHeaderValue("image/jpeg");
                return res;
            }
            catch (Exception ex)
            {
                logger.ErrorException("画像取得中にエラーが発生しました", ex);
                return new HttpResponseMessage()
                {
                    Content = new StringContent("予期せぬエラーが発生しました。")
                    ,
                    StatusCode = System.Net.HttpStatusCode.InternalServerError
                };
            }
        }




Imageクラスを(バイト配列にして)返す事もできる。
   

        // ローカルの画像ファイルをImageクラスに読み込んで返す
        public HttpResponseMessage Get()
        {
            try
            {
                byte[] bytes = null;

                // 画像をImageクラスに読み込む
                using (Image img = Image.FromFile(@"C:\temp\1.png"))
                using (Graphics g = Graphics.FromImage(img))
                {
                    // 現在時刻とか書き込んでみる

                    g.DrawString("Hello WebAPI."
             , new Font("MS Gothic", 10)
             , Brushes.Black, 10, 80);
                    g.DrawString(DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"), 

                        new Font("MS Gothic", 10)
           , Brushes.Black, 10, 100);

                    // バイト配列にする
                    ImageConverter imgconv = new ImageConverter();
                    bytes = (byte[])imgconv.ConvertTo(img, typeof(byte[]));
                }

                var res = new HttpResponseMessage()
                {
                    Content = new ByteArrayContent(bytes)                    ,
                    StatusCode = System.Net.HttpStatusCode.OK
                };
                res.Content.Headers.ContentType 

                    = new System.Net.Http.Headers.MediaTypeHeaderValue("image/png");
                return res;
            }
            catch (Exception ex)
            {
                logger.ErrorException("画像取得中にエラーが発生しました", ex);
                return new HttpResponseMessage()
                {
                    Content = new StringContent("予期せぬエラーが発生しました。")
                    ,
                    StatusCode = System.Net.HttpStatusCode.InternalServerError
                };
            }
        }



ロギングとかエラーの返し方は適当








2012年9月16日日曜日

[C#] LINQ To Entities でコレクションのプロパティに値を突っ込む

なにを言いたいのかいまいちよくわからないタイトル。


コレクションをプロパティに持つクラスがあるとして

    public class SchoolViewModel    {
        public int ClassID { get; set; }
        public int Grade { get; set; }
        public int Class { get; set; }
        public IEnumerable<SchoolerViewModel> SchoolerList { get; set; }
    } 


    // 上のコレクションの要素となるクラス
    public class SchoolerViewModel
    {
        public int ClassID { get; set; }
        public string Name { get; set; }
        public int Number { get; set; }
    }


このにコレクションにLINQでがつんと値を入れる


from school in db.School
   join sc in db.Schooler on school.ClassID equals sc.ClassID into schooler
   select new SchoolViewModel()
   {
       ClassID = school.ClassID
       ,
       Grade = school.Grade
       ,
       Class = school.Class
       ,
       SchoolerList = schooler.Select(
           s => new SchoolerViewModel() { 

               ClassID = s.ClassID, Name = s.Name, Number = s.Number 
           } )
   };
}


LINQの into 句とか select の中で もう一度 select してるあたりがミソ(だと思ってる)。
SQLは一発しか発行されていない。
取得されるデータをLINQさんが勝手にグループ化してコレクションのプロパティに入れてくれるという。LINQさんってすげぇ…。

2012年9月9日日曜日

[C#] IEnumerable と IQueryable

http://www.codeproject.com/Articles/231163/IQueryable-Vs-IEnumerable-in-terms-of-LINQ-to-SQL

LINQの結果をIEnumerableとして取得するかIQueryableとして取得するかで発行されるSQLが変わる…?

よくわからないので自分で試してみる。

SchoolClassなる学校のクラスデータが入っているテーブルと、
それに紐つく Scholler(生徒) テーブルを用意。


まずはIEnumerable

        private IEnumerable<SchoolClass> GetSchoolAsEnumarable()
        {
            return from school in db.SchoolClass

                   where school.Grade > 1
                   select school;
        }

        public void ConsoleOutScholler()
        {

             // クラスデータをIEnumerableとして取得
            IEnumerable<SchoolClass> schoolList = GetSchoolAsEnumarable();

            // IEnumerableとして取得したクラスデータに生徒テーブルを結合 
            var schoolerList =
                   from school in schoolList
                   join schooler in db.Schooler
                   on school.ClassID equals schooler.ClassID
                   where school.Grade > 2

                   select schooler;

            foreach (var schooler in schoolerList)
            {
                Console.WriteLine("出席番号:" + schooler.Number + " 名前:" + schooler.Name);
            }
        }


実際にどのようなSQLが発行されているかトレースしてみると…
(データベースはSQLServer)


■発行されたSQL1
SELECT
[Extent1].[ClassID] AS [ClassID],
[Extent1].[Number] AS [Number],
[Extent1].[Name] AS [Name]
FROM [dbo].[Schooler] AS [Extent1]

■発行されたSQL2
SELECT
[Extent1].[ClassID] AS [ClassID],
[Extent1].[Grade] AS [Grade],
[Extent1].[Class] AS [Class]
FROM [dbo].[SchoolClass] AS [Extent1]
WHERE [Extent1].[Grade] > 1


SQLが2つ発行されてる…遅延実行で最終的に一個のSQLになると思ってたんだけど…。
しかも生徒テーブルの方を先に読み込んでいるのがよくわかない件について。
あと school.Grade > 2  がない。えっ。どうやらそちらの条件は.NETの方で絞っているようだ。


次に IQueryable として受け取ってみる

        private IQueryable<SchoolClass> GetSchoolAsQueryable()
        {
            return from school in db.SchoolClass

                   where school.Grade > 1
                   select school;
        }

        public void
ConsoleOutScholler()
        {

            // クラスデータをIQuaryableとして取得
            IQueryable<SchoolClass> schoolList = GetSchoolAsQueryable();

            // IQuaryableとして取得したクラスデータに生徒テーブルを結合
             var schoolerList =
                  from school in schoolList
                   join schooler in db.Schooler
                   on school.ClassID equals schooler.ClassID
                   where school.Grade > 2

                   select schooler;

            foreach (var schooler in schoolerList)
            {
                Console.WriteLine("出席番号:" + schooler.Number + " 名前:" + schooler.Name);
            }
        }


■発行されたSQL
SELECT
[Extent2].[ClassID] AS [ClassID],
[Extent2].[Number] AS [Number],
[Extent2].[Name] AS [Name]
FROM  [dbo].[SchoolClass] AS [Extent1]
INNER JOIN [dbo].[Schooler] AS [Extent2] ON [Extent1].[ClassID] = [Extent2].[ClassID]
WHERE ([Extent1].[Grade] > 1) AND ([Extent1].[Grade] > 2)


SQLが一つになったよ!やったね!
で、なんで IEnumerable のときは2つ発行されてたの?


発行されるSQLが違う理由についてはこのサイト↓にIEnumerableは「pull型」、IQueryableは「外部クエリ」という説明があった。
http://www.atmarkit.co.jp/fdotnet/chushin/roadtolinq_01/roadtolinq_01_03.html

> IEnumerable<T>インターフェイスを使っても外部システムからの
> データ取得はできなくはないのだが、データを全件返すという部分がネックになる

そうなの…。

とにかく LINQ to SQL と LINQ to Entities のときは素直に IQueryable として使いますね?(それでいいのか)

2012年9月2日日曜日

[C#] LINQ to Entities で数値を文字列に変換

LINQ to Entities の話し。

LINQで数値の列を ToString() で文字列にしようとしたらプログラム実行時にエラーになった。


NotSupportedException: メソッド 'System.String ToString()' は LINQ to Entities では認識されないため、ストア式に変換できません。



ToString()をSQLに変換する方法知らないんです!できません!と言っているようだ。
知らないなら実行時じゃなくてもっと早い段階で教えてくださいっ、とか思うけどそれはまあ難しいのだろう。

じゃあどうすればいいのという事で調べてみると StringConvert というメソッドが見つかった。
http://msdn.microsoft.com/ja-jp/library/vstudio/dd466166


LINQで使ってみる


from hoge in db.HogeTable
select SqlFunctions.StringConvert((double?)hoge.Num)



StringConvertの部分がどのようなSQLに変換されているかLINQPadで確認。
データベースはSQLServer。


SELECT
STR( CAST( [Extent1].[Num] AS float)) AS [C1]
FROM [dbo].[HogeTable] AS [Extent1]



確かに文字列に変換しているようだった(当たり前)。