2017年2月21日火曜日

大文字、小文字を無視してDistinct

小文字でグループ化したデータのうち最初のデータを取り出す

using System;
using System.Collections.Generic;
using System.Linq;

public class Hello{
    public static void Main(){
        // Here your code !
        List data = new List{"CSharp", "CSHARP", "csharp"};
        
        foreach(string d in data.GroupBy(d => d.ToLower()).Select(d => d.First())){
            Console.WriteLine(d);
        }
        
    }
}

実行すると CSharpが出力される

2017年2月19日日曜日

デフォルトで許可されていないURLの文字

例えば、ブログなんかをイメージしてもらえればよいのだがタグのリンクを以下のようにしたとする

/Blog/Tags/ASP.NET
/Blog/Tags/SQL+Server

.や半角スペースのエンコードした+なんかが含まれているとアクセスするとエラーになる。

■.を許可する。
認識するために/Web.configに、以下を追加する

  
    
      
    
  


まずは対象のパスを指定 今回は/Blog/Tags/の次の部分を対象とするので /Blog/Tags/* としてある。


■+を許可する
+を含んだパスにアクセスすると
以下のエラーが出る
要求フィルター モジュールが、ダブル エスケープ シーケンスを含む要求を拒否するように構成されています。

可能性のある原因:
要求にダブル エスケープ シーケンスが含まれていました。要求フィルターはダブル エスケープ シーケンスを拒否するように Web サーバーで構成されています。

対処方法:
applicationhost.config または web.confg ファイルにある configuration/system.webServer/security/requestFiltering@allowDoubleEscaping 設定を確認します。

以下設定を追加すると許可される

  
    
      
    
  

2017年2月12日日曜日

ASP.NET MVC5メモ

バインディング

ViewのデータをPostする際に、Nameの名前が所定のルールに従っていれば自動的に
バインドしてくれるので、Controllerの引数から受け取ることができます。


基本は、InputタグのNameに クラス名.プロパティとかけばバインドしてくれます。
あとは、プロパティがリストだという場合は、 クラス名.プロパティ[インデックス]
クラス自体がリストで受け取るのであれば クラス名[インデックス].プロパティ
といった感じでかけば受け取ることができます。

わかりやすくするため、ViewでHTMLをハードコードします。

Models/Sample.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace SampleCode.Models
{
    public class Sample
    {
        public int ID { get; set; }
        public string Name { get; set; }
    }
}

IDとNameを持つだけのクラスです。

適当にHomeという名前のControllerを作成して以下のように書きました

Controllers/HomeController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace SampleCode.Controllers
{
    public class HomeController : Controller
    {
        // GET: Home
        public ActionResult Create()
        {
            return View();
        }

        [HttpPost]
        public ActionResult Create(Models.Sample sample)
        {
            return View();
        }
    }
}
ブレイクポイントを設定して確認します。

Views/Home/Create.cshtml
@{
    ViewBag.Title = "Create";
}

Create

@using (Html.BeginForm()) { @Html.AntiForgeryToken() }




@modelにクラスが設定されていれば、Nameのところでクラス名を省略できます。
Views/Home/Create.cshtml
@model SampleCode.Models.Sample

@{
    ViewBag.Title = "Create";
}

Create

@using (Html.BeginForm()) { @Html.AntiForgeryToken() }



今度は、クラス内にListがある時

Models/Sample.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace SampleCode.Models
{
    public class Sample
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public List Memo { get; set; }
    }
}



Views/Home/Create.cshtml
@model SampleCode.Models.Sample

@{
    ViewBag.Title = "Create";
}

Create

@using (Html.BeginForm()) { @Html.AntiForgeryToken() }

リストはプロパティ名に[インデックス]番号つければバインドできます




今度は、Dictionaryをモデルに持っている時です
Models/Sample.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace SampleCode.Models
{
    public class Sample
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public Dictionary Hash { get; set; }
    }
}


Controllers/HomeController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace SampleCode.Controllers
{
    public class HomeController : Controller
    {
        // GET: Home
        public ActionResult Create()
        {
            return View();
        }

        [HttpPost]
        public ActionResult Create(Models.Sample sample)
        {
            return View();
        }
    }
}

Views/Home/Create.cshtml
@model SampleCode.Models.Sample

@{
    ViewBag.Title = "Create";
}

Create

@using (Html.BeginForm()) { @Html.AntiForgeryToken() }

KeyValueのうち、Keyは inputのhiddenに、Valueは取得したいinput要素のNameに指定します。
KeyとValueをペアリングするために Nameにプロパティ名[文字列] が一致する者同士を KeyValueのペアとみなします。

<input type = "hidden" name="プロパティ名[文字列].key" value="キー名" />
<input type = "text" name="プロパティ名[文字列].value" value="値" />

[文字列]はループ処理とかを考えると数字が来やすいけども
keyとvalueをペアリングするための識別子なので、任意の文字を指定できます



今度は、Controllerで受け取る引数をリスト(List)にしてみます。

Models/Sample.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace SampleCode.Models
{
    public class Sample
    {
        public int ID { get; set; }
        public string Name { get; set; }
    }
}

Controllers/HomeController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace SampleCode.Controllers
{
    public class HomeController : Controller
    {
        // GET: Home
        public ActionResult Create()
        {
            return View();
        }

        [HttpPost]
        public ActionResult Create(List list)
        {
            return View();
        }
    }
}

Viewes/Home/Create.cshtml
@model SampleCode.Models.Sample

@{
    ViewBag.Title = "Create";
}

Create

@using (Html.BeginForm()) { @Html.AntiForgeryToken() }
基本は、クラス名[数字].プロパティ ですが、modelで明示してあるので省略してあるだけです。



クラス内でListを使い、そのクラスをリストで受けるとなると、今までの方法を組み合わせるだけです
Models/Sample.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace SampleCode.Models
{
    public class Sample
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public List Memo { get; set; }
    }
}

Controllers/HomeController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace SampleCode.Controllers
{
    public class HomeController : Controller
    {
        // GET: Home
        public ActionResult Create()
        {
            return View();
        }

        [HttpPost]
        public ActionResult Create(List list)
        {
            return View();
        }
    }
}

Views/Home/Create.cshtml
@model SampleCode.Models.Sample

@{
    ViewBag.Title = "Create";
}

Create

@using (Html.BeginForm()) { @Html.AntiForgeryToken() }

何が複数なのかを考えればクラスに[数字]をつけるかプロパティに[数字]をつけるか明確ですね


ちなみに、クラスのインスタンスに、一加工してから格納したいので
ひとまずリストで受け取りたいなんて場合、いちいちクラスを作らなくてもバインドしてくれます
Models/Sample.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace SampleCode.Models
{
    public class Sample
    {
        public int ID { get; set; }
        public string Name { get; set; }
    }
}

Controllers/HomeController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace SampleCode.Controllers
{
    public class HomeController : Controller
    {
        // GET: Home
        public ActionResult Create()
        {
            return View();
        }

        [HttpPost]
        public ActionResult Create(List list, List hoge)
        {
            return View();
        }
    }
}
Modelに定義せずに List を用意。
名前をhogeにしたので、Viewにもhogeの名前でnameにつけます。

Views/Home/Create.cshtml
@model SampleCode.Models.Sample

@{
    ViewBag.Title = "Create";
}

Create

@using (Html.BeginForm()) { @Html.AntiForgeryToken() }

これで、 List list にも List hogeにもバインドされます。

Entity Framework 1対1

コンソールアプリケーションで試します。
基本的なことはこちら→ConsoleApplicationでCodeFirst

1対1です。

1つのテーブルにまとめようとするとあるレコードはnullが
入る列とデータが入る列があるので分離してnullを排除した結果
1対1のテーブルになったとか、あると思いますが。

今までどおりに作成していくと、どちらのテーブルがどちらを参照するか表現できません。
そこで、Fluentd APIを利用します。

メインのテーブルと、そのIDを参照する参照テーブルで考えてみます。
model/TableMain.cs
namespace EntityFramework.model
{
    class TableMain
    {
        public int Id { get; set; }

        public string Name { get; set; }

        public virtual TableRef TableRef { get; set; }
    }
}

model/TableRef.cs
namespace EntityFramework.model
{
    class TableRef
    {
        [Key]
        public int TableMainId { get; set; }

        public string Memo { get; set; }

        public virtual TableMain TableMain { get; set; }
    }
}

TableMainIdがTableMainテーブルのidを参照する流れです。
ただし、TableMain.csもTableRef.csもともに public virtual クラス名 で記載しているので
どちらがどちらを参照しているのか、この情報だけだと判断がつきません。
ためしに、Add-Migrationを行うと

model/SampleContext.cs(NG)
using System.Data.Entity;

namespace CodeFirst.model
{
    class SampleContext : DbContext
    {
        public DbSet TableMain { get; set; }
        public DbSet TableRef { get; set; }
    }
}

下記のように怒られます。
Unable to determine the principal end of an association between the types 'CodeFirst.model.TableRef' and 'CodeFirst.model.TableMain'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.

fluent APIで解決する方法

model/SampleContext.cs
using System.Data.Entity;

namespace CodeFirst.model
{
    class SampleContext : DbContext
    {
        public DbSet TableMain { get; set; }
        public DbSet TableRef { get; set; }
    
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity()
            .HasRequired(t => t.TableRef)
            .WithRequiredPrincipal(t => t.TableMain);
        }
    }
}

HasMany →1:nの関連
HasRequired→1:1の関連
HasOptional→1:0..1の関連
-------------------------------------
data annotaionsで解決する方法

model/TableRef.cs
sing System.ComponentModel.DataAnnotations;

namespace CodeFirst.model
{
    class TableRef
    {
        [Key]
        public int TableMainId { get; set; }

        public string Memo { get; set; }
        [Required]
        public virtual TableMain TableMain { get; set; }
    }
}

[Required]を追加すると期待したリレーションが付きます

2017年2月10日金曜日

Entity Framework 多対多

コンソールアプリケーションで試します。
基本的なことはこちら→ConsoleApplicationでCodeFirst

多対多です。
ブログの記事にタグがぶら下がる状態がまさにそれですね。
イメージしやすいのでこれを題材にいろいろ掘り下げてみます。





記事のテーブル。
名前と本文だけのシンプルな構成

model/Article.cs
using System.Collections.Generic;

namespace EntityFramework.model
{
    class Article
    {
        public int ID { get; set; }
        public string Title { get; set; }
        public string Body { get; set; }

        //リレーション(相手側が多なのでCollection)
        public virtual ICollection Tag { get; set; }
    }
}


タグのテーブル

model/Tag.cs
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace EntityFramework.model
{
    class Tag
    {
        public int ID { get; set; }
        [Required]
        [Index(IsUnique=true)]
        [MaxLength(50)]
        public string Name { get; set; }

        //リレーション(相手側が多なのでCollection)
        public virtual ICollection
Article { get; set; } } }

tagはユニークにしたいので Index(IsUnique=true)をつけています。
ユニーク制約をつけるためには、文字数が最大になっていたらつかないので文字数制限をつけます

プレーンなクラスとDBをつなぐクラス
model/BlogContext.cs
using System.Data.Entity;

namespace EntityFramework.model
{
    class BlogContext : DbContext
    {
        public DbSet
Articles { get; set; } public DbSet Tags { get; set; } } }

Add-Migrationして Update-Databaseしてテーブルを作成します。
※こまかいとこはすっとばします。



多対多なので自動的に中間テーブル(TagArticles)が作られています。


データを格納してみます。

Program.cs
using EntityFramework.model;
using System.Collections.Generic;

namespace EntityFramework
{
    class Program
    {
        static void Main(string[] args)
        {
            BlogContext db = new BlogContext();

            // Article側にTagをぶら下げる
            Article article = new Article { Body = "Body_A", Title = "Title_A" };
            Tag tag1 = new Tag { Name = "C#" };
            Tag tag2 = new Tag { Name = "SQL Server" };
            article.Tag = new List { tag1, tag2 };
            db.Articles.Add(articleA);
            db.SaveChanges(); // 登録
        }
    }
}

記事Aに C#とSQL Serverを登録しています。
タグはUniqueキー制約をつけているのでこんな登録の仕方したらだめなわけですが
サンプルということもあって、初回登録で空なのでこんな書き方しています。




記事Bを追加します。
今回は、タグがすでにあることを想定して事前に取得して、なければタグのインスタンスを生成するようにしています。
??演算子で nullだったら後ろの処理をするようにしています。

Program.cs
using EntityFramework.model;
using System.Collections.Generic;
using System.Linq;

namespace EntityFramework
{
    class Program
    {
        static void Main(string[] args)
        {
            BlogContext db = new BlogContext();

            // Article側にTagをぶら下げる
            Article article = new Article { Body = "Body_B", Title = "Title_B" };

            Tag tag1 = db.Tags.Where(t => t.Name == "C#").FirstOrDefault();
            tag1 = tag1 ?? new Tag { Name = "C#" };

            Tag tag2 = db.Tags.Where(t => t.Name == "SQL Server").FirstOrDefault();
            tag2 = tag2 ?? new Tag { Name = "SQL Server" };

            Tag tag3 = db.Tags.Where(t => t.Name == "ASP.NET").FirstOrDefault();
            tag3 = tag3 ?? new Tag { Name = "ASP.NET" };

            article.Tag = new List { tag1, tag2, tag3 };
            db.Articles.Add(article);
            db.SaveChanges(); // 登録
        }
    }
}



Program.cs
using EntityFramework.model;
using System.Collections.Generic;
using System.Linq;

namespace EntityFramework
{
    class Program
    {
        static void Main(string[] args)
        {
            BlogContext db = new BlogContext();

            // Article側にTagをぶら下げる
            Article article = new Article { Body = "Body_C", Title = "Title_C" };

            Tag tag1 = db.Tags.Where(t => t.Name == "C#").FirstOrDefault();
            tag1 = tag1 ?? new Tag { Name = "C#" };

            Tag tag2 = db.Tags.Where(t => t.Name == "ASP.NET").FirstOrDefault();
            tag2 = tag2 ?? new Tag { Name = "ASP.NET" };

            article.Tag = new List { tag1, tag2 };
            db.Articles.Add(article);
            db.SaveChanges(); // 登録
        }
    }
}


2017年2月8日水曜日

Entity Framework 1対多

コンソールアプリケーションで試します。
基本的なことはこちら→ConsoleApplicationでCodeFirst


1対多です。
ブログの記事にコメントが複数ぶら下がる状態がまさにそれですね。
イメージしやすいのでこれを題材にいろいろ掘り下げてみます。

まずは、記事のテーブル。
名前と本文だけのシンプルな構成

model/Article.cs
using System.Collections.Generic;

namespace EntityFramework.model
{
    class Article
    {
        public int ID { get; set; }
        public string Title { get; set; }
        public string Body { get; set; }

        //リレーション(相手側が多なのでCollection)
        public virtual ICollection Comment { get; set; }
    }
}


コメントも同様にシンプルな構成
model/Comment.cs
namespace EntityFramework.model
{
    class Comment
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Body { get; set; }

        //リレーション(相手側が1なのでCollectionじゃない)
        public virtual Article Article { get; set; }
    }
}

プレーンなクラスとDBをつなぐクラス
model/BlogContext.cs
using System.Data.Entity;

namespace EntityFramework.model
{
    class BlogContext : DbContext
    {
        public DbSet
Articles { get; set; } public DbSet Comments { get; set; } } }


Add-Migrationして Update-Databaseしてテーブルを作成します。
※こまかいとこはすっとばします。

blogの記事とコメントの関係を考えると
まずは、記事が単独で入って、そのあとにコメントがつくことになると思います。
まず記事を適当に挿入します。



Program.cs
namespace EntityFramework
{
    class Program
    {
        static void Main(string[] args)
        {
            Article article = new Article();
            article.Body = "Body_A";
            article.Title = "Title_A";

            BlogContext db = new BlogContext();
            db.Articles.Add(article);
            db.SaveChanges();
        }
    }
}




今度はコメントがついたので、ID1の記事にコメントを追加します。
Program.cs
using EntityFramework.model;
using System.Collections.Generic;

namespace EntityFramework
{
    class Program
    {
        static void Main(string[] args)
        {
            int articleId = 1;

            BlogContext db = new BlogContext();
            Article article = db.Articles.Find(articleId);

            // 追加の仕方は2通り
            // 記事とコメントの関係は1対多なので
            // 記事につける場合は、コメントは多なのでリストを追加します。
            Comment commentA1 = new Comment { Name = "Name_A1", Body = "Body_A1" };
            Comment commentA2 = new Comment { Name = "Name_A2", Body = "Body_A2" };
            article.Comment = new List { commentA1, commentA2 };
            //↑articleはDBからとったオブジェクト

            // コメントに記事情報を与える場合は、記事は1なので単独でセットします。
            Comment commentA3 = new Comment { Name = "Name_A3", Body = "Body_A3" };
            commentA3.Article = article;
            db.Comments.Add(commentA3);
            // commentA3は、単なるクラスのインスタンス。
            // DBの情報をもったものに登録してあげる必要ある
            // 複数コメントを追加する時は、一つコメントをインスタンスして db.Comments.Addに一つずつ追加すればよい

            db.SaveChanges();
        }
    }
}





今回は、ブログを想定しているので、記事が挿入されてからコメントが追加されましたが、一度に挿入する場合を掲載しておきます。
using EntityFramework.model;
using System.Collections.Generic;

namespace EntityFramework
{
    class Program
    {
        static void Main(string[] args)
        {
            BlogContext db = new BlogContext();

            // 1対多で1側に追加して Contextに追加するパターン
            Article article1 = new Article { Title = "Title_B", Body = "Body_B" };
            Comment comment1 = new Comment { Name = "Name_B", Body = "Body_B" };

            article1.Comment = new List { comment1};
            db.Articles.Add(article1);


            Article article2 = new Article { Title = "Title_C", Body = "Body_C" };
            Comment comment2 = new Comment { Name = "Name_C1", Body = "Body_C1" };
            Comment comment3 = new Comment { Name = "Name_C2", Body = "Body_C2" };

            comment2.Article = article2;
            comment3.Article = article2;

            db.Comments.Add(comment2);
            db.Comments.Add(comment3);

            db.SaveChanges();
        }
    }
}

2017年2月7日火曜日

EntityFrameworkメモ

ConsoleApplicationでCodeFirst

VS2015 Communityでメモ

ASP.NET MVC5をいじってCodeFirstがなかなかいい感じなので
ConsoleApplicationで実行するメモ


まずはEntityFrameworkを追加します。
ツール→NuGetパッケージマネージャー→ソリューションのNuGetパッケージの管理

参照にして、EntityFrameworkで検索。
追加するプロジェクトを選択(ここではCodeFirstって名前にしてるのでそれにチェック)

そしてインストール

これで参照にEntityFrameworkが追加される。


まずは1対多のテーブルを作る。
Blogの記事に対し、コメントをつけるデータを格納する部分を作ります。
ひとまずmodelフォルダ作って格納しておきます。

model/Article.cs
namespace CodeFirst.model
{
    class Comment
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Body { get; set; }

        //リレーション(相手側が1なのでCollectionじゃない)
        public virtual Article Article { get; set; }
    }
}

model/Comment.cs
namespace CodeFirst.model
{
    class Comment
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Body { get; set; }

        //リレーション(相手側が1なのでCollectionじゃない)
        public virtual Article Article { get; set; }
    }
}

今回は、単純にConsoleApplicationで使うのをメモするだけなので、
NotNullとかの細かい部分は飛ばしていきます。

この2クラスは単なるプレーンなクラスなのでこれらとDBとをつなぐためのクラスを作ります。

model/BlogContext.cs
using System.Data.Entity;

namespace CodeFirst.model
{
    class BlogContext : DbContext
    {
        public DbSet
Articles { get; set; } public DbSet Comments { get; set; } } }

■DBの準備と接続文字列
SQL ServerにDBを作ります。
SampleCodeDBって名前で作りました。

App.configに接続文字列を追加します。

    
  

nameには、先ほど作ったBlogContextを記載します。
接続文字は先ほど作ったDBの情報を記載してください。


■マイグレーションを実行する
ツール→NuGetパッケージマネージャー→パッケージマネージャーコンソール を実行。
PM> Enable-Migrations

これでMigrationsフォルダが作られ、Configuration.csファイルが作られます。

Seedメソッドにデータを書けば初期データを挿入することができますが、今回はProgram.csで普通に追加します。
そのまえにMigrationを行ってテーブルを作成します。

マイグレーションでまず覚えておくのはたった2つ。
Add-MigrationとUpdate-Database


まずは、Migrationのファイルを作ります。前回のマイグレーションから今回のマイグレーションまでの差分を作ります。
PM> Add-Migration Init
(Add-Migration Migration名)

問題なければMigrationsフォルダにMigrationファイルがつくられます。
yyyyMMdd********_マイグレーション名.cs で作られます

データベースに反映します。
PM> Update-Database

エラーがなければDBにテーブルが作られます。


リレーションも張られてます。


■データを挿入
ひとまず、何か簡単にデータを入れてみます。

Program.cs
using CodeFirst.model;
using System.Collections.Generic;

namespace CodeFirst
{
    class Program
    {
        static void Main(string[] args)
        {
            Article article = new Article();
            article.Body = "Body_A";
            article.Title = "Title_A";

            List comments = new List() {
                    new Comment {Name = "Name_A1", Body = "Body_A1" },
                    new Comment {Name = "Name_A2", Body = "Body_A2" }
            };

            article.Comment = comments;

            BlogContext db = new BlogContext();
            db.Articles.Add(article);
            db.SaveChanges();
        }
    }
}