📜 ⬆️ ⬇️

Universal ADO.NET Entity Repository

Dear programmers!
There was no sadness It took me a lot of work with ASP.NET MVC2 + Entity Framework, but the basic functionality of working with the database didn’t impress me at all, because I had to choose the right collection of objects from the list each time. How to avoid writing a few classes and use only one - it goes on.

Requirements

To begin with, we’ll decide what we would really like to be at the exit.

I want to work with objects more conveniently:


using constructions of the form:
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 .

')
Moreover, in order not to explicitly specify with which set ( ObjectSet <...>) ObjectContext should work.

A bit of reflection theory


Since all my code will be based on this principle, you should understand what it is.

Reflection is a process during which a program can monitor and modify its own structure and behavior at run time.

In short, the base Object class has a GetType () method, which returns the structure of the class (all its properties, fields, methods). Therefore, we can use it to call a function and set values ​​to properties at runtime, even without knowing at the stage of writing code about the presence of these fields and functions in the class.

The GetType () method returns a Type object, which we actually need. Following the link, you can see for yourself the number and variety of methods and properties of this object. For now, we need GetMethod () and GetProperty ().

Actually implementation

To begin, I will describe the principle of operation.

In a nutshell:
  1. Get the type of object transferred.
  2. Get name from type.
  3. Select the desired entity set from the ObjectContext.
  4. "Pull" the desired method (Add, Delete).


/// <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 .


When transferring an object in angle brackets, we can already get all the necessary data from it (in particular, the name, which is important). Since the default objects in the ObjectContext are called ****** Set, where ****** is the name of the entity in the visual editor. Using reflection, we get the opportunity to reach out to any set of objects, without specifying this name in the code. Thus, all the manipulations are performed.

For example, in all ObjectSet 's contains the DeleteObject method.

Next, we need the ObjectContext itself (where, in fact, we will write and receive data):

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


* This source code was highlighted with Source Code Highlighter .


You have already received it can be obtained using the wizard and visual editing of objects.

A few more lines of code just improve readability:
/// <summary>
/// Reflection - get name of T.
/// </summary>
private string name
{
get { return typeof (T).Name; }
}


* This source code was highlighted with Source Code Highlighter .


This is getting the type name T (above) and a small “macro” getting the ObjectContext type:
//Simple macros
Type dbT = typeof (DBContainer);


* This source code was highlighted with Source Code Highlighter .


In general, the entry is complete and you can go to the whole code. Further I will tell about not absolutely obvious pieces of code.

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 .


Underwater rocks

Here I would like to explain some points about which I was too lazy to read which did not immediately work, as well as unobvious pieces of code.

The private object function AllItemsAsObj () is used to get the object set itself (ObjectSet) and is available only inside the class. It is needed in almost all cases, since it is our basic object, which will be followed by manipulations.

The AddItem (T item) function was written first.
On the move did not want the method Invoke method GetMethod. It turned out that it is necessary to pass as an argument the object on which we perform actions. Putting null (as I saw in the examples on unknown sites) the method was not called. Pay attention to this!

/// <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 .


This part of the code selects one object from the set by its ID. In my project, each object has a field ID like Guid. If you have it different - just replace all references to "ID" with your name.
In this function, all elements of the collection are searched, and if the value of the ID field coincides with the input parameter, the desired object is returned.

At the end of each function there is a line:
db.SaveChanges();

* This source code was highlighted with Source Code Highlighter .

It saves all changes made to the database.

Usage example

Suppose you have two classes: Unit and City.
To declare two repositories working with the database, do the following:
BaseRepository<Unit> unitRepository = new BaseRepository<Unit>();
BaseRepository<City> cityRepository = new BaseRepository<City>();


* This source code was highlighted with Source Code Highlighter .

Everything, no more manipulations are needed. You already have ready-made objects that will help in the work. If you wish, you can extend the functionality with the functions you need by inheriting your class.

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


All Articles