ひっそりと生きるプログラマのブログ

日頃気になった事なりを書き留めるブログです。関心ごとは多くもう少し更新頻度を上げたいところです。

【Entity Framework】テーブルの主キーの値を渡して、レコードを取得する方法(ナビゲーションの値も含む)

以下のような実装で取得可能。

次の実装は呼び出し元

SampleTable というテーブルがあり、
主キーに相当するプロパティが SampleId になります。
SampleSubTable と SampleTable はナビゲーションプロパティが定義しています。

static void Main(string[] args)
{
    SampleTable data = null;
    using (var context = new Sample01Entities())
    {
        context.Configuration.ProxyCreationEnabled = false;
        data = GetSampleTableData(context, 2);
    }
    Console.WriteLine(data.SampleId);
    Console.WriteLine(data.SampleName);
    Console.WriteLine(data.SampleSubTable.Count);
    Console.ReadLine();
}

private static SampleTable GetSampleTableData(
    DbContext context, int key1)
{
    return DbContextUtility.GetData<SampleTable>(
        context,
        new SampleTable()
        {
            SampleId = key1
        });
}

次の実装は呼び出し先
リファクタリングの余地はあるが、
実現したいことはできていると思います。

public static class DbContextUtility
{
    #region EntityType
    private static Dictionary<Type, EntityType> entityTypes =
        new Dictionary<Type, EntityType>();

    private static EntityType GetEntityType(DbContext context, Type type)
    {
        EntityType result;
        if (entityTypes.TryGetValue(type, out result)) return result;
        var objectContext = ((IObjectContextAdapter)context).ObjectContext;
        var metadata = objectContext.MetadataWorkspace;
        result = metadata.GetItem<EntityType>(type.FullName, DataSpace.OSpace);
        entityTypes.Add(type, result);
        return result;
    }
    #endregion

    public static T GetData<T>(
        DbContext context, T entity) where T : class
    {
        var type = typeof(T);
        var entityType = GetEntityType(context, type);
        var keyProperties = entityType.KeyMembers.Select(k => type.GetProperty(k.Name));
        var values = keyProperties.Select(m => m.GetValue(entity)).ToArray();
        var result = (T)context.Set<T>().Find(values);
        GetEntity(context, result, new HashSet<object>());
        return (T)result;
    }

    private static void GetEntity(
        DbContext context, object entity, HashSet<object> values)
    {
        if (values.Contains(entity)) return;
        values.Add(entity);
        var type = entity.GetType();
        var entityType = GetEntityType(context, type);
        var navproperties = entityType.NavigationProperties.Select(
            m => entity.GetType().GetProperty(m.Name));
        foreach (var nav in navproperties)
        {
            if (nav.PropertyType.IsGenericType)
            {
                context.Entry(entity).Collection(nav.Name).Load();
                var enumerable = (IEnumerable)nav.GetValue(entity);
                foreach(var e in enumerable)
                    GetEntity(context, e, values);
            }
            else
            {
                context.Entry(entity).Reference(nav.Name).Load();
                var value = nav.GetValue(entity);
                GetEntity(context, value, values);
            }
        }
    }
}

【Entity Framework】Set やら Find やら使って、ナビゲーションプロパティも含めてDBから取得する方法

調べた結果、できそうな雰囲気。
眠いので今週中位に記事を書く予定。

EntityType から、いろいろな情報が取得できるので、
面白い事ができそうです。

DB(SQL ServerOracle)に、
依存しない形で汎用的な部分を作りこめるのはうれしいですね。

【ASP.NET MVC】モデルのバインドに関して (2)

前回投稿した記事で、
モデルのバインドに関して調べました。
saboten-sakura.hatenablog.com

以下のように複数の Name が存在した場合。

@using (Html.BeginForm("Send", "Home", null, FormMethod.Post))
{
    <input type="text" name="a" />
    <input type="checkbox" name="a" value="true" />
    <input type="hidden" name="a" value="false" />
    <input type="submit" value="送信" />
}

以下のような実装をする事でフォーム上の値が変数にバインドされます。
※引数の型が配列になっています。

public class HomeController : Controller
{
    public ActionResult Send(bool[] a)
    {
        return this.Index();
    }
}

bool 型なので、 true or false のみですが、
string 型 や int 型などでもキャスト可能であれば利用可能です。

【ASP.NET MVC】モデルのバインドに関して

ポストする時に同じ name があった場合、
どのような挙動となるのか確認しました。
※自分自身のメモです。時間も遅いのでわかり辛かったらすみません。。。

次のような Controller と View があるとします。

public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View("Index");
    }

    public ActionResult Send(bool a)
    {
        return this.Index();
    }
}

※ submit すると HomeController クラスの Send メソッドが呼ばれます。

@using (Html.BeginForm("Send", "Home", null, FormMethod.Post))
{
    <input type="checkbox" name="a" value="true" />
    <input type="hidden" name="a" value="false" />
    <input type="submit" value="送信" />
}

checkbox を チェック した状態で引数 a には true が設定されます。
また、
checkbox を チェック しない状態で引数 a には false が設定されます。

これは、name が複数同じ値が存在した場合、
先に読み込まれた値を元に解決しているようです。
その為、以下のような実装も可能となります。

@using (Html.BeginForm("Send", "Home", null, FormMethod.Post))
{
    <input type="text" name="a" />
    <input type="checkbox" name="a" value="true" />
    <input type="hidden" name="a" value="false" />
    <input type="submit" value="送信" />
}

type が text の入力値が bool型へ変換できない場合は実行時例外が発生します。
文字列そてい、 "true" および "false" と入力した場合は、
入力した値が bool 型へ変換され引数 a に設定されます。

この事から、上から下へ順に解決されている事が確認できました。

ASP.NET MVC モデルバインド時に DataAnnotation を実行しない方法

タイトルの通り、モデルバインド時に、
DataAnnotation を実行しない方法です。

DataAnnotationsModelValidatorProvider を、
ModelValidatorProviders.Providers から取得し削除します。

using System.Linq;
using System.Web.Mvc;

public class MvcApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        ModelValidatorProviders.Providers.Remove(
            ModelValidatorProviders.Providers.Single(
                p => p is DataAnnotationsModelValidatorProvider));
    }
}

排他制御

仕事で Mutex を使う機会というのは意外と多い。
同一のプロセスで排他制御するのであれば lock ステートメントでよいが、
他のプロセスなりと関連して排他制御する場合は lock ステートメントだと難しい。

その点、 Mutex を利用するとその辺りがお手軽に実現できて便利です。

たまに更新

公私共々、忙しく更新が疎かになっていました^^;
また、ボチボチと更新してゆきたいところです。

と、それとは別に、資格をとるべく勉強中。。。
C#ジャンプスタートのプログラミング