Entity Framework Code Firstにおいて複数のリレーションシップを追加する(Fluent API)

題名が長いですね。
Entity Frameworkで、Code Firstでデータベースを作成する際に、例えば次の図のようなリレーションシップを構成したいときどうするか、という話です。

テーブル間に複数のリレーション

1つのテーブルに複数のリレーション

これは、モデルクラスの定義だけではできません。多分。
ただ参照を作っただけでは、当然、どのプロパティがどのリレーションに対応するのかEntity Frameworkは認識できないので、エラーが出るかとんでもないリレーションが作成されます。
こういった、モデルクラスの定義だけで表現できないリレーションを構成するには、Fluent APIを用います。

ではFluent APIの説明をする前に、モデルクラスの定義だけで構築した場合のリレーションを見てみましょう。
クラス定義はこんな感じ。

public class Block
{
    public int Id { get; set; }

    public virtual Block Parent { get; set; }
    public virtual ICollection<Block> Children { get; set; }

    public virtual Block Next { get; set; }
    public virtual Block Previous { get; set; }
}

他のクラスも似たような感じなので省略します。
属性は全くつけていません。この定義でデータベースを構築すると…?

おかしくなっちゃったリレーション

なんということでしょう。ナビゲーションプロパティ毎にリレーションが作成されてしまいました。
新しいグループに所属したら、そのGroupのUsersにも勝手に追加されてほしいのに、これではそんなこと全然してくれないわけです。
ナビゲーションプロパティ、GroupsとUsers間には関係があるんだということを、Entity Frameworkに教える必要があります。

ではどう教えるか。Fluent APIを使います。
Fluent APIは、ContextクラスでOnModelCreatingメソッドをオーバーライドして記述します。
今回の場合どうすればよいのか見てみましょう。

class TestContext : DbContext
{
    static TestContext()
    {
        Database.SetInitializer(new DropCreateDatabaseAlways<TestContext>());
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<User>().HasMany(model => model.MyGroups).WithOptional(model => model.Owner);
        modelBuilder.Entity<User>().HasMany(model => model.Groups).WithMany(model => model.Users);

        modelBuilder.Entity<Block>().HasMany(model => model.Children).WithOptional(model => model.Parent);
        modelBuilder.Entity<Block>().HasOptional(model => model.Next).WithOptionalDependent(model => model.Previous);
    }

    public DbSet<User> Users { get; set; }
    public DbSet<Group> Groups { get; set; }

    public DbSet<Block> Blocks { get; set; }
}

これで、各リレーションが、意図通りに作成されます。

カテゴリー: プログラミング | タグ: , , , , | 2件のコメント

StreamReaderとキャリッジリターン

StreamReaderのReadLine/ReadLineAsyncメソッド、まぁ便利です。
改行コードがLF/CR/CR+LFどれであっても、いい感じに読み込んでくれます。

が、…このメソッド、CRが来るだけでは処理を返してくれません!!!!
CRを読み込んだ後、他の文字が来るか、もしくはもうEOFだということがわかるまで、なんと処理を返さず待機するのです!

なんでやねん!CR読み込んだ時点で1行確定でしょ!!すぐに返してよ!!!!

この動作、テキストファイル等、すでにすべてのデータが揃っているストリームに対しては問題無いですね。
ですが、例えばネットワークなどでCRを区切りとしている系のAPIだと、応答が返ってきているはずなのに何も来ない…とか、1回分遅れて応答が帰ってくるように見える…ということが発生します。
CRの次の文字が来るまで返さないので、CRが来ただけでは、最新の1行分の応答がStreamReaderにバッファされたままになるのです。

先日、PJLinkのC#ラッパーを書いていてこの問題にぶち当たりました。
PJLinkとは、プロジェクタ等をLAN経由で操作するための標準仕様ですが、コマンドや応答の終端をCRとすることになっているのです。
つまり、
→コマンド1を送る
(←コマンド1の応答が返ってくるけどStreamReaderにバッファされるだけ
→コマンド2を送る
←コマンド1の応答がやっと返される(そしてコマンド2の応答はStreamReaderにバッファされる)
みたいな。

しかたがないので、改行コードがCRな1行を読み込んだら、すぐに返すクラスを書いてみた。
長いけどこんなん。

using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Threading.Tasks;

namespace ShComp
{
    /// <summary>
    /// 改行コードがCRの場合に、1行を読み込んですぐに応答を返すメソッドを提供します。
    /// </summary>
    class CRLineReader : IDisposable
    {
        private StreamReader _reader;
        private bool _isEof;
        private int _bufferStart;
        private int _bufferEnd;
        private char[] _buffer;

        public CRLineReader(Stream stream, int capacity = 136)
        {
            _reader = new StreamReader(stream);
            _buffer = new char[capacity];
        }

        /// <summary>
        /// CR区切りの1行を非同期に読み込みます。CR以外の改行コードは、改行コードとみなしません。
        /// </summary>
        public async Task<string> ReadLineAsync()
        {
            if (_isEof) return null;

            var sb = new StringBuilder();

            if (_bufferEnd > _bufferStart)
            {
                if (AppendFromBuffer(sb))
                {
                    return sb.ToString();
                }
            }

            _bufferStart = 0;

            while (true)
            {
                _bufferEnd = await _reader.ReadAsync(_buffer, 0, _buffer.Length);

                if (_bufferEnd == 0)
                {
                    _isEof = true;
                    return sb.ToString();
                }

                if (AppendFromBuffer(sb))
                {
                    return sb.ToString();
                }
            }
        }

        private bool AppendFromBuffer(StringBuilder sb)
        {
            for (int i = _bufferStart; i < _bufferEnd; i++)
            {
                if (_buffer[i] == 'r')
                {
                    sb.Append(_buffer, _bufferStart, i - _bufferStart);
                    _bufferStart = i + 1;
                    return true;
                }
            }

            sb.Append(_buffer, _bufferStart, _bufferEnd - _bufferStart);
            return false;
        }

        public void Dispose()
        {
            _reader.Dispose();
        }
    }
}

バッファサイズが136なのは、PJLinkの応答の最大長が136だから。
PJLink、ASCIIな文字しか使わないから、StreamReader介さず直接Streamから読み込んでもいい気がするけど、まぁ。

カテゴリー: プログラミング | タグ: , | コメントする

非同期の同期

awaitを使った時のロックって、どうやるんですかね。セマフォでいいの?

C#では、バージョン5.0から、async/awaitキーワードを用いることで、非同期処理がとてつもなく簡単に書けるようになりました。
今までは、時間のかかる処理は新しいスレッドを作ったり、Taskで実行したり、IO処理はBegin/Endパターンとか、EAP使ったりしました。うんざりしますね!まじで!!

今では、async/awaitが導入されたことにより、ほぼ同期的なコードと同じコードを書いてるにもかかわらず、時間のかかる処理やIO処理を非同期に行えるようになりました。

さて、同期的にコードが書けてしまうと、おもわず非同期部分をlockしたくなってしまいます。
たとえばファイルの書き込みとか。非同期にしたいけど、書き込み自体は排他的にしてほしい。

lock(lockObj)
{
    using (var stream = File.OpenWrite(path))
    {
         await stream.WriteLineAsync("A");
         await stream.WriteLineAsync("B");
    }
}

みたいな。でもこれはアウトです。
lockの実態はMonitorですが、これはあるスレッドでロックして、別スレッドで返すことはできません。
await前後は同じスレッドで実行されるとは限らないため、lock内にawaitを入れるとエラーが出るわけです。

で…
どうすればいいの?これ。
とりあえず私は、SemaphoreSlimを使っています。普通のSemaphoreでもいいんですが、Slimのほうにはシグナルを待つためにWaitAsyncというTask<bool>を返すメソッドがあって、非同期処理と相性が良いようにも思えます。こんな感じ↓

private SemaphoreSlim _semaphore = new SemaphoreSlim(1);
private async Task FunctionAsync()
{
    try
    {
        await _semaphore.WaitAsync();
        await 非同期だけど同時に実行してほしくない処理();
    }
    finally
    {
        _semaphore.Release();
    }
}

で、毎回try/finally書くのはめんどくさいんで、こんな↓クラス作って

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ShComp
{
    /// <summary>
    /// セマフォを用いた、非同期の状態でもロックを行うためのメソッドを提供します。
    /// </summary>
    class AsyncLock
    {
        private SemaphoreSlim _s;

        public AsyncLock()
        {
            _s = new SemaphoreSlim(1);
        }

        /// <summary>
        /// <see cref="System.Threading.SemaphoreSlim" /> に移行するために非同期に待機します。<para />
        /// 待機後、IDisposable型のオブジェクトを返します。
        /// 返されるオブジェクトをDisposeすることで <see cref="System.Threading.SemaphoreSlim" /> から出ます。
        /// usingと共に使うことを想定しています。
        /// </summary>
        public async Task<IDisposable> GetObjectAsync()
        {
            await _s.WaitAsync();
            return new _(_s);
        }

        private class _ : IDisposable
        {
            private SemaphoreSlim _s;

            public _(SemaphoreSlim s)
            {
                _s = s;
            }

            public void Dispose()
            {
                _s.Release();
            }
        }
    }
}

こんな感じ↓で使っています。 …いいのかな?

private AsyncLock _lock = new AsyncLock();
private async Task FunctionAsync()
{
    using (await _lock.GetObjectAsync())
    {
        await 非同期だけど同時に実行してほしくない処理();
    }
}
カテゴリー: プログラミング | タグ: , | コメントする

CとC#の違い(1)

バイトの上司に、CとC#は何が違うんだと聞かれました。すごい違います
といってもCとC#の違いを並べてるサイトなんてたくさんあるので、ここでは主観的なことを交えつつ話をしようと思います。

C#を書いてて思うのは、メモリ管理マジ助かる・ライブラリマジ便利・VisualStudio最高!ってところでしょうか。他にもいろいろありますけど、このへんがC#の最大の利点かな、と思っています。
まぁ、どう考えても長くなるので、CとC#の違い(1)ということで、何回かに分けて書きたいと思います。

ガベージコレクション ~メモリ管理マジ助かる~

C#では、基本的には、使ったメモリを開放する必要はありません。言語側が、使わなくなったメモリを認識し、自動で開放してくれます。
これを、ガベージコレクションといいます。日本語訳すれば、ごみ収集ですね。

メモリの解放なんて、バグの温床だし、めんどくさいです。

その時に必要なのは何と何で…とかちゃんと認識していれば、バグを減らすこともできます。が、そんなのどう考えてもめんどくさい。できるとかできないとかじゃなくて、もうとにかくめんどくさいわけです。freeのfも書きたくない!でもforのfは書きます。

で、

C#は、メモリが足りなくなると、ガベージコレクションが動き出します。オブジェクトが参照されているか。つまりは、手の届くところにあるかどうかが判定され、手の届かないものはどんどん開放されていきます。
手の届かないものは、もう使いようが無いですからね。ゴミなわけです。

あぁ、楽…… 楽です…freeとかdeleteとか自分で書かなくてもいいなんて…

ガベージコレクション自体は、複雑で、重い処理です。
まず、存在するすべてのオブジェクトに対して、生存しているスレッドから間接的にでもアクセスする手段があるかどうかが調べられます。オブジェクトの参照をたどっていって、到達できれば、到達できたよマークが付けられます。(ジェネレーションみたいなのもあるけど、割愛)
そして、到達できなかったオブジェクトに対して、メモリの開放処理を行っていきます。必要があれば、ファイナライズされたりもします。(ファイナライズのへんの処理は複雑なので割愛)
ついでに、メモリの使われなくなった部分が穴あきになりますので、前に詰める処理、コンパクションが行われます。HDDで言うところのデフラグですね。

これで、メモリにはもう必要なオブジェクトしか無く、しかも先頭から綺麗に詰まった状態になりました。
つまり、ガベージコレクションの利点は、メモリの開放が不要になるだけではありません。
新しくメモリを確保するのも、どんどん最後に追加していくだけで良い。Cでは、だんだんメモリが穴あきになっていきますので、空いている十分なスペースを探す必要があります。実はメモリ確保に関しては、CよりもC#のほうが速かったりするんですね。

もちろん、何も考えなきゃ、メモリリークもスタックオーバーフローも、当然起きます。それでもやっぱり、メモリ管理を自分でしなくてもいいのは良いです。精神衛生上、とても良い

上述しましたが、ガベージコレクションは大変複雑なことをするため、重い処理です。でも、いろんな技術でなるべく効率化されているので、普通に使ってる分には、いつガベージコレクションされたかなんて気づかないでしょう。
もし気になるようであれば、自分でガベージコレクションをスタートさせる事もできます。プログラムなんてほとんどの時間はユーザからの入力待ちです。その時間にこそっとやれば良い。無駄な時間をメモリのために使え、さらにCでメモリ確保にかかっていた時間も節約できて、一石二鳥です。
手動スタートする場合は、よくガベージコレクションを学んでからのほうがいいです。何も考えなしにやると、逆に遅くなることもあります。

と、いうことで、CとC#の違い1回目ですが、こんなかんじで。
今日はガベージコレクションの紹介でした。続きはまたいつか。
コンパクションの弊害で、オブジェクトのアドレスが変わってしまったりもする(わざわざポインタ操作しなきゃ気にする必要はない)けど、もうはっきり言ってCとかC++には戻れません。

C#最高!おしまい。

カテゴリー: プログラミング | タグ: | コメントする

stringとString、intとInt32、違いは?

結論:stringとStringは一緒のもの

研究室で先輩が遺したコードをいじっていると、文字列型をStringと定義しているのによく出くわします。

C#は大文字小文字を区別する言語ですが、文字列型は、stringとも、Stringとも定義できます。まぁ、一般的には先頭が小文字のstringを使うかと思います。

私の研究室でStringがよく使われているのは、昔はJavaがよく使われていたからです。Javaでは文字列型はStringなので、C#でもStringと書ける以上、みんなStringと書いてきた。

では、stringとStringは、何が違うのでしょう。他にもC#では、intをInt32と書けたり、doubleをDoubleと書けたりします。

実はintとInt32は、全く同一です。intは、ただのInt32(正確に書けばSystem.Int32)のエイリアスです。コンパイルすると、どちらで書いても全く同じコンパイル結果(System.Int32)となるのです。

これは、C#が、組み込み型については、省略形をキーワード(予約語)として提供してくれているためです。
.NET Frameworkでは、組み込み型であっても、特別扱いせず定義されています。たとえばStringは、正しくは名前空間が付いて、System.Stringです。using System;しているからStringと書けるわけですが、正式名称をいちいち書くのは長いのです。
そこでC#では、これらを短く、stringやdoubleと書けるようにしました。(stringはエイリアスなので、System.stringとは書けません。)

組み込み型は、頻繁に使うしね。それにintとかlongとか書ければ、他のプログラミング言語から移り住んで来た人もすぐに型を見つけられるし。

ということで私は、こいつJava使いだな…!とか思いながら、日々コードを直すのである。おしまい。

カテゴリー: プログラミング | タグ: , | コメントする

VisualStudio2013 ASP.NET MVC で SQLCEを使う

1投稿目からこれかよって感じですが、皆様はじめましてこんにちは。

さて、早速本題です。Visual Studio 2013でのASP.NET MVC開発において、
SQL Compact Editionが使いたい
Visual Studio 2012では、例えばWeb.configに以下の様な接続文字列を追加すれば使えました。

<add name="DefaultConnection" connectionString="data source=|DataDirectory|aspdb.sdf" providerName="System.Data.SqlServerCe.4.0" />

が、Visual Studio 2013では使えない。なんなんだ。

実行してみると、『ADO.NET プロバイダーに、不変名が ‘System.Data.SqlServerCe.4.0’ の Entity Framework プロバイダーがありません。アプリケーションの構成ファイルの “entityFramework” セクションにプロバイダーが登録されていることを確認してください。詳細については、http://go.microsoft.com/fwlink/?LinkId=260882 を参照してください。』とか言われます。
そして詳細のURL先は英語。やめて。

どうやら、プロバイダーは別で提供されているので、
SqlServerCe用のをNuGetでプロジェクトにインストールせよ、とのことですね。

プロジェクト名を右クリックして、NuGetパッケージの管理、オンラインを「entity」か何かで検索すれば、「EntityFramework.SqlServerCompact」というのが出てきます。選択して、パッケージをインストール!

プロジェクト作成して、そのままEntityFramework.SqlServerCompactをインストールすると、EntityFrameworkの6.0.0が完全にアンインストールされませんでした、とか言われるので、インストールする前に1回VSを開き直したほうがいいです。
EntityFramework.SqlServerCompactが6.0.2のEntityFrameworkを要求するために、EntityFramework.SqlServerCompactのインストールと一緒にEntityFrameworkがバージョンアップされますが、プロジェクト作成直後はなんかひっかかるみたい。

ついでにSqlServerCeのバイナリも一緒にインストールされて便利。

あとは、上記の接続文字列をそのまま書いて、おしまい!

カテゴリー: プログラミング | タグ: , , , | 1件のコメント