ユニバーサルADO.NETエンティティリポジトリ

親愛なるプログラマー!
ASP.NET MVC2 + Entity Frameworkと密接に連携する必要はありませんでしたが、リストから必要なオブジェクトのコレクションを選択するたびに、データベースを操作する基本的な機能にまったく感動しませんでした。 複数のクラスを記述せずに、1つだけを使用することはさらに先に進みます。

必要条件

そもそも、私が何をしたいのかを決めます。

便利にオブジェクトを操作したい:


フォームの構造を使用する:
Unit a = new Unit();
BaseRepository<Unit> unitRepository = new BaseRepository<Unit>();
...........
unitRepository.AddItem(a);
unitRepository.ChangeItem(a);
unitRepository.DeleteItem(a.ID);


* This source code was highlighted with Source Code Highlighter .


さらに、特定のセット( ObjectSet <...>)を明示的に指定しないために、 ObjectContextは動作する必要があります。

リフレクションに関する少しの理論


私のコードはすべてこの原則に基づいているため、それが何であるかを理解する必要があります。

リフレクションは、実行中にプログラムが独自の構造と動作を追跡および変更できるプロセスです。

つまり、基本クラスObjectには、クラス構造(そのすべてのプロパティ、フィールド、メソッド)を返すGetType()メソッドがあります。 したがって、実行時に関数を呼び出してプロパティに値を設定するために、クラス内のこれらのフィールドと関数の存在についてコードを記述する段階でさえ知らなくても、それを使用できます。

GetType()メソッドは、実際に必要なType型のオブジェクトを返します。 リンクをたどると、このオブジェクトのメソッドとプロパティの数と多様性を確認できます。 今のところ、GetMethod()とGetProperty()が必要です。

実際に実装

最初に、動作原理について説明します。

一言で言えば:
  1. 転送されたオブジェクトのタイプを取得します。
  2. 型名から取得します。
  3. ObjectContextから目的のエンティティセットを選択します。
  4. 目的のメソッドを「プル」します(追加、削除)。


/// <summary>
/// Base repository for all sets.
/// </summary>
/// <typeparam name="T">Class from Business Model, should be EntityType</typeparam>
public class BaseRepository<T> where T : EntityObject


* This source code was highlighted with Source Code Highlighter .


山括弧でオブジェクトを転送する場合、必要なすべてのデータ(特に重要な名前)を既に取得できます。 デフォルトでは、 ObjectContext内のオブジェクトは****** Setと呼ばれるため、******はビジュアルエディター内のエンティティの名前です。 リフレクションを使用すると、コードでこの名前を示すことなく、オブジェクトのセットに手を差し伸べることができます。 したがって、すべての操作が実行されます。

たとえば、すべてのObjectSetsにはDeleteObjectメソッドが含まれています

次に、 ObjectContext自体が必要です(実際には、データの書き込みと受信を行います)。

//DataBase container
DBContainer db = new DBContainer();


* This source code was highlighted with Source Code Highlighter .


ウィザードとオブジェクトの視覚的な編集を使用して、 すでに取得できます。

さらに数行のコードを使用すると、読みやすくなります。
/// <summary>
/// Reflection - get name of T.
/// </summary>
private string name
{
get { return typeof (T).Name; }
}


* This source code was highlighted with Source Code Highlighter .


これは、タイプT(上記)の名前と、タイプObjectContextの小さな「マクロ」を取得しています
//Simple macros
Type dbT = typeof (DBContainer);


* This source code was highlighted with Source Code Highlighter .


一般に、紹介は完了しており、コード全体に進むことができます。 次に、あまり明らかでないコードについて説明します。

using System;
using System.Collections. Generic ;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Data.Objects;
using System.Data.Objects.DataClasses;

namespace Chib.Lib
{
/// <summary>
/// Base repository for all sets.
/// </summary>
/// <typeparam name="T">Class from Business Model, should be EntityType</typeparam>
public class BaseRepository<T> where T : EntityObject
{
/// <summary>
/// Reflection - get name of T.
/// </summary>
private string name
{
get { return typeof (T).Name; }
}

//DataBase container
DBContainer db = new DBContainer();
//Simple macros
Type dbT = typeof (DBContainer);

/// <summary>
/// Get all items in Set as IQueryable, making easy to operate with data.
/// </summary>
/// <returns>Items in Set</returns>
public IQueryable<T> AllItems
{
get
{
return (IQueryable<T>)AllItemsAsObj;
}
}

/// <summary>
/// Get the items (ObjectSet) as Object. For internal use only.
/// </summary>
/// <returns>Object</returns>
private object AllItemsAsObj
{
get
{
PropertyInfo mi = dbT.GetProperty(name + "Set" );
object obj = mi.GetValue(db, null );
return obj;
}
}

/// <summary>
/// Add item to collection and save changes.
/// </summary>
/// <typeparam name="T">The type of item</typeparam>
/// <param name="item">Added item</param>
/// <returns>True if no errors.</returns>
public bool AddItem(T item)
{
try
{
object obj = AllItemsAsObj;
obj.GetType().GetMethod( "AddObject" ).Invoke(obj, new object [] { item });
db.SaveChanges();
return true ;
}
catch
{ return false ; }
}

/// <summary>
/// Get the single T item by it's ID
/// </summary>
/// <param name="id">Guid ID</param>
/// <returns>Null if nothing found.</returns>
public T GetItem( Guid id)
{
foreach ( var item in AllItems)
{
if ( new Guid (item.GetType().GetProperty( "ID" ).GetValue(item, null ).ToString()) == id)
return (T)item;
}
return null ;
}

/// <summary>
/// Delets an item by it's ID.
/// </summary>
/// <param name="id">ID of item</param>
/// <returns>True if no errors.</returns>
public bool DeleteItem( Guid id)
{
try
{
T item = GetItem(id);
object set = AllItemsAsObj;
set .GetType().GetMethod( "DeleteObject" ).Invoke( set , new object [] { item });
db.SaveChanges();
return true ;
}
catch
{
return false ;
}
}

public bool ChangeItem(T item)
{
try
{
var guid = new Guid (item.GetType().GetProperty( "ID" ).GetValue(item, null ).ToString());
T modyfying = AllItems.Single(x => x.GetType().GetProperty( "ID" ).GetValue( null , null ).ToString() == guid.ToString());
modyfying = item;
db.SaveChanges();
return true ;
}
catch
{
return false ;
}
}

/// <summary>
/// Force save changes to DB.
/// </summary>
/// <returns>True if no errors.</returns>
public bool SaveChanges()
{
try
{
db.SaveChanges();
return true ;
}
catch
{
return false ;
}
}
}
}


* This source code was highlighted with Source Code Highlighter .


落とし穴

ここでは、私が読むのが面倒で、すぐには機能しなかったいくつかのポイント 、非自明なコードを説明したいと思います。

プライベートオブジェクトAllItemsAsObj()関数は、オブジェクトのセット(ObjectSet)を取得するために使用され、クラス内でのみ使用可能です。 これは、操作が実行される基本オブジェクトであるため、ほとんどすべての場合に必要です。

AddItem(Tアイテム)関数が最初に記述されました。
GetMethodメソッドのInvokeメソッドは必要ありませんでした。 アクションを実行するオブジェクトを引数として渡す必要があることが判明しました。 nullを設定することで(不明なサイトの例で見たように)、メソッドは呼び出されませんでした。 それに注意してください!

/// <summary>
/// Get the single T item by it's ID
/// </summary>
/// <param name="id">Guid ID</param>
/// <returns>Null if nothing found.</returns>
public T GetItem( Guid id)
{
foreach ( var item in AllItems)
{
if ( new Guid (item.GetType().GetProperty( "ID" ).GetValue(item, null ).ToString()) == id)
return (T)item;
}
return null ;
}


* This source code was highlighted with Source Code Highlighter .


コードのこの部分は、IDによってセットから1つのオブジェクトを選択します。 私のプロジェクトでは、すべてのオブジェクトにGuidタイプのIDフィールドがあります。 異なる場合は、「ID」へのすべての参照を自分の名前に置き換えてください。
この関数では、コレクションのすべての要素が列挙され、IDフィールドの値が入力パラメーターと一致すると、目的のオブジェクトが返されます。

各関数の最後には次の行があります。
db.SaveChanges();

* This source code was highlighted with Source Code Highlighter .

データベースに加えられたすべての変更を保存します。

使用例

UnitとCityの2つのクラスがあるとします。
データベースを操作するための2つのリポジトリを宣言するには、次の手順に従います。
BaseRepository<Unit> unitRepository = new BaseRepository<Unit>();
BaseRepository<City> cityRepository = new BaseRepository<City>();


* This source code was highlighted with Source Code Highlighter .

すべて、これ以上の操作は必要ありません。 作業に役立つ既製のオブジェクトが既にあります。 必要に応じて、クラスを継承することで、必要な機能を使用して機能を拡張できます。

Source: https://habr.com/ru/post/J124619/


All Articles