Rubyの要点

Rubyを使っていて、自分の理解が浅かったところをまとめてみました。

map,reduce,filter

Lispなどの関数型言語でお馴染みの上記関数をRubyで使う。

map

rubyにもmapメソッドがあります。 実際はcollectというメソッドの別名ですが、mapを使うほうが混乱は少ないかな。

list = (1..10)
list.map{|i| i*2} #listの全要素を2倍します
reduce

これもinjectというメソッドの別名です。

list = (1..10)
list.reduce {|sum,n| sum+n} #すべての要素を足し合わせる
filter(=select)

Haskellなどで使われるfilterメソッドに対応するのはselectというメソッドです。

list = (1..10)
list.select{|n| n % 3 == 0} #3の倍数を抽出する

ブロック

do 〜 end(または{〜})で囲まれた部分。 メソッドの引数として使われる。

mapを実装してみる。

list = (1..10)
# 特異メソッドを使ってlistのインスタンスにmap2を実装
def list.map2(&block)
 new_list = []
 self.each do |i|
  new_list << block.call(i)
 end
 new_list
end
list.map2{|i| i*2}
=> [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]

&blockでブロック引数を受け取っている。
&はブロックをProcオブジェクトに変換するための記号。
(ブロック自体はオブジェクトとして存在できず引数として渡せないのでこのような方法をとるらしい)
ただ、この書き方は冗長である。なぜなら・・
「ブロック引数は仮引数の中で一番最後に渡されなければならない」
という制約があるから。 つまり、引数自体宣言する必要ないんじゃね?ということ。
このためRubyにはyield(イールド)という構文が用意されている。
これを使えばブロック引数の宣言すら必要ない。こんな感じでかけちゃう・・・

# yieldを使って書き直す
def list.map3()
 new_list = []
 self.each do |i|
  new_list << yield(i)
 end
 new_list
end

ただ、yieldを使うと個人的には少し読みにくい気もする。

Paizaの反省点

最近PaizaでAランクの問題に挑戦できるようになりました。

今日もある問題を解いてみたんですが、 回答に4時間もかかってしまいました・・・。

悔しいのは、 その半分以上をデバッグに費やしたことです。

原因はすごく簡単なことで、二度と同じミスを犯さないように 今回学んだことをメモしておきたいと思います。

1.オプショナル引数は使わない

バグの原因は、 オプショナル引数の渡し忘れでした。 当然、オプショナル引数は、”オプショナル”ですので渡し忘れていても エラーにはなりません。

通常のプログラムにおいては必要な機能ですが、 Paiza のような時間制限付きの問題を解く場合は焦りもあって、 ミスに気づきにくいです。 なので、オプショナル引数は使わない方針で行きたいと思います。

2.pryのデバッグ機能を使う

これも当たり前のことですが、 pryデバッグ機能は積極的に使っていこうと思います。

require 'pry'

def main()
    binding.pry #ここからデバッグを開始します。
end

main()

以上!

EntityFrameworkのデータファーストとDbContextの接続文字列を動的に変える方法

EntityFrameworkはAsp.NET MVCのO/Rマッパーです。

ユーザが Model クラスを作成し、 ビルド時にそれを元にEntityFrameworkがDBとテーブルを作成します。 コードからDBが作成されるので通常これはコードファーストと呼ばれます。

しかし、既存のDBからModelを自動生成する方法もEntityFrameworkには用意されています。 (実際の業務ではこっちの方が多い気がしますね。) これは、DBからコード(Model)を作るのでデータファースと呼ばれます。

DBからModelを作成する方法

では、データファーストにより既存のDBからModelを作成する方法を紹介します。

0.事前準備

ソリューションにDBを作成します。 今回はDB名を TestDB、その中にテーブル:users を用意しておきます。

CREATE TABLE [dbo].[users] (
    [Id]   INT        NOT NULL,
    [name] NCHAR (10) NULL,
    [age]  NCHAR (10) NULL,
    [sex]  NCHAR (10) NULL,
    PRIMARY KEY CLUSTERED ([Id] ASC)
);
1.プロジェクト内にModelsフォルダを作成します。
2.Modelsフォルダで右クリックし「新しい項目を追加」を選択します。
3.「ADO.NET Entity Data Model」を追加します。

名前は適当なものを入力します(ここではTestDBContext)。

最後に DBContext をつけるのが一般的です。

f:id:Turing:20160716160943p:plain

4.「データベースからCode Firstを選択。」

f:id:Turing:20160716161121p:plain

5.「新しい接続」をクリックして、

今回はソリューション内にDBを作ったので、データソースに「Micorosoft SQLServerデータソースファイルを選択」。

f:id:Turing:20160716161224p:plain

f:id:Turing:20160716161620p:plain

データベースファイル名には事前にソリューション内に用意しておいたDBを選択します。

6. 接続設定に名前をつけて次へ。

f:id:Turing:20160716162154p:plain

7.モデルを生成する対象のテーブルをすべて選択します。

f:id:Turing:20160716162415p:plain

8.ModelsフォルダにTestDBContextと各テーブルのModelクラスが追加されます。

f:id:Turing:20160716162615p:plain

できたクラスの中身はこんな感じ。

namespace DBFirstPractice.Models
{
    using System;
    using System.Data.Entity;
    using System.ComponentModel.DataAnnotations.Schema;
    using System.Linq;

    public partial class TestDBContext : DbContext
    {
        public TestDBContext()
            : base("name=TestDBContext")
        {
        }

        public virtual DbSet<user> users { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<user>()
                .Property(e => e.name)
                .IsFixedLength();

            modelBuilder.Entity<user>()
                .Property(e => e.age)
                .IsFixedLength();

            modelBuilder.Entity<user>()
                .Property(e => e.sex)
                .IsFixedLength();
        }
    }
}
namespace DBFirstPractice.Models
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    using System.ComponentModel.DataAnnotations.Schema;
    using System.Data.Entity.Spatial;

    public partial class user
    {
        [DatabaseGenerated(DatabaseGeneratedOption.None)]
        public int Id { get; set; }

        [StringLength(10)]
        public string name { get; set; }

        [StringLength(10)]
        public string age { get; set; }

        [StringLength(10)]
        public string sex { get; set; }
    }
}

DBContextの接続文字列を動的に変える方法

下記のようにすることで、 ソース上で動的に接続先を変えることもできます。 (先ほど作成した、users テーブルと同じ構造のテーブルを持つ、  TestDbBackupというDBがあるものとします。)

まず、自動生成されたDBContextのコンストラクタを下記のように変更します。

namespace DBFirstPractice.Models
{
    using System;
    using System.Data.Entity;
    using System.Data.Common;
    using System.ComponentModel.DataAnnotations.Schema;
    using System.Linq;

    public partial class TestDBContext : DbContext
    {
        public TestDBContext(DbConnection connection) : base(connection, true)
        {
        }

        public virtual DbSet<user> users { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<user>()
                .Property(e => e.name)
                .IsFixedLength();

            modelBuilder.Entity<user>()
                .Property(e => e.age)
                .IsFixedLength();

            modelBuilder.Entity<user>()
                .Property(e => e.sex)
                .IsFixedLength();
        }
    }
}

次に、DBContextをインスタンスを生成する際にConnectionを設定します。

namespace DBFirstPractice.Controllers
{
    public class HomeController : Controller
    {
        TestDBContext _db;

        public HomeController()
        {
            var connection = DbProviderFactories.GetFactory("System.Data.SqlClient").CreateConnection();
            connection.ConnectionString = "data source=(LocalDB)\\MSSQLLocalDB;attachdbfilename=|DataDirectory|\\TestDbBackup.mdf;integrated security=True;connect timeout=30;MultipleActiveResultSets=True;App=EntityFramework";

            _db = new TestDBContext(connection);
        }

        // GET: Home
        public ActionResult Index()
        {
            List<user> mans = _db.users.Where(u => u.sex == "man").ToList();

            return View();
        }
    }
}

この接続文字列のDataSourceを変更することでソース上で動的に接続するDBを切り替えることができます。

EntityFrameworkって何?

仕事でEntityFrameworkを使うかもしれないので、今のところの知識をまとめてみました。

EntityFrameworkとは?

Ruby on RailsにおけるActiveRecord .NETバージョン。O/Rマッパー。

ActiveRecordに相当するのは、POCOエンティティ(Plain Old CLR Object)という、プロパティだけを持つクラスです。ただ、これはRailsActiveRecordのように自動生成されずユーザーが自作します。

public class Student
{
    public virtual int Id { get; set; }
    public virtual string Name { get; set; }
    public virtual int Age { get; set; }
}

こんな感じのエンティティをModelフォルダ内に作ります。

そしてこのデータをDBから読み込むためにSystem.Data.Entity.DbContextを継承したコンテキストクラスを作ります。

// DbContext を継承する必要がある
public class MyContext : DbContext
{
    public DbSet<Student> Students { get; set; }
    public DbSet<Teacher> Teachers { get; set; }
}

プロジェクトをビルドしたタイミングで、コンテキストクラスと同名のデータベースが、そのDB内にPOCOエンティティと同名のテーブルが、自動生成されます。
DBのテーブルにアクセスする際は、このコンテキストクラスのプロパティを使います。

DBからデータを読み込むには↓のようにします。

using (var context = new MyDbContext())
{
   var student = context.Students.Find(x => x.Name == "Yuichi"); //Find()のタイミングでクエリ発行
   var students = context.Students.Where(x => x.Age <= 20).ToArray(); //ToArray()のタイミグでクエリ発行

    //...
}

usingを使っているのは、コンテキストクラスを確実にDisposeするためです。 また、上記2クエリはFindメソッドが実行されるタイミング、ToArrayメソッドが実行されるタイミングでそれぞれ、DBに対してクエリが発行されています。 ですので、クエリが発行されるタイミングを意識して、使用時には不要なDBアクセスを極力抑えるように注意する必要があります。

データの追加、更新、削除は↓のようにやります。

    using (var context = new MyDbContext())
    {
   // 追加
        // Addした段階ではSql文はDBに発行されない
        context.Students.Add(new Student
        {
            Name = "Daisuke",
            Age = 18,
        });

        //更新
   var student = context.Students.Find(x => x.Name == "Yuichi"); //Find()のタイミングでクエリ発行
        student.Age = 26;

        //削除
        var deleteTarget = context.Students.Find(x => x.Name == "Takashi");
        context.Students.Remove(deleteTarget);

        // SaveChangesが呼び出された段階で初めてInsert,Update,Delete文が発行されます
        context.SaveChanges();
    }

通常は、更新、削除処理はDBContext経由で読みだした(Find、Whereで読み込んだ)データに対してのみ行なえます。 しかし、例えば削除対象のデータの主キーが予めわかっている場合は、そのデータを事前に読み込むのは冗長な気がします。 そんなときは、Attachメソッドを使います。

    using (var context = new MyDbContext())
    {
   //削除(削除対象のIDが予めわかっている時)
        var deleteTarget = new Student { Id = 1 };
        context.Students.Attach(deleteTarget);
        context.Students.Remove(deleteTarget);
        
        context.SaveChanges();
    }

データベースファーストについて

この記事で紹介したのは、まずPOCOエンティティを作って、それに合わせて自動的にDBが作成される、いわゆるコードファーストという手法です。 (EntityFramework自体はこのコードファーストという手法をAsp.NET MVCで実現する為に考案されたものです) しかし、既にデータベースが存在している場合は、既存のテーブルからPOCOエンティティを作成する必要があります。 これをデータベースファースといいます。 これについては、また記事を分けてまとめていきたいと思います。

MVCってなに?

こんにちは。

暑くなってきましたね。

 

Webアプリケーションを作成する上で必須知識となりつつある MVCフレームワーク ですが、まだ良くわかってないという方もいると思います。

今回は、そんな超初心者の方でも3分で分かるようにまとめてみました。

 

MVCってなに?

MVC はソフトウェア・アプリケーションを作る上での設計手法の1つです。

Model(モデル)、View(ビュー)、Controller(コントローラ)という3つの領域に分割して1つのソフトウェアを作ります。

f:id:Turing:20160702104109p:plain

 

なんで分割するんや!

こんなふうにわざわざ3つの領域に分割するのは次のような理由があります。

1.プログラマとデザイナが分業するため。

2.保守性を上げるため。

MVCフレームワークを使うことによって、プログラマによる実装手法の差を軽減することができます。

 

M,V,Cそれぞれの役割  

View(ビュー)

読んで字のごとくです。表示を担当します。Webアプリケーションの場合は、Webページに当たります。

 

Contoller(コントローラ)

ユーザーからのリクエストを解析し、適切なビューを呼び出します。ただし、コントローラは中継役にすぎないので、ビジネスロジックは実装してはいけません

ビジネスロジックはモデルに実装します。コントローラは、モデルを初期化してビューに渡すだけです。

 

Model(モデル)

ビジネスロジックとデータアクセスをモデルに実装します。上の図を見てもわかると思いますが、データベースに直接アクセスできるのはモデルだけです。

モデルとコントローラの関係は1対 多 です。モデルは複数のコントローラから呼ばれることもあります。

  

Controller はただの中継役

MVCフレームワークを使う上で特に多い間違いが、Controller にビジネスロジックを書いて、Controllerを肥大化させてしまうことです。

先も書きましたが、Controllerは中継役にすぎないのです。Controllerには、どうしてもモデルに書けないこと(HTTPリクエストに対してWebページを表示するなど)を書くのです。