Как стать автором
Обновить

Отделение логики базы данных (попытка №2)

ПрограммированиеСовершенный кодC#
Первая попытка получилась немного сумбурна, поэтому я решил написать более последовательно.

Повторим основную идею: работу с базой нужно организовать через специально заточенный класс, а не разбрасывать код вызовов по всему проекту.

Но этому может препятствовать то, какой интерфейс предлагают производители баз данных. Мы потренируемся на базе MongoDB.

Здесь я предложу реализацию следующей логики работы:

Должен быть базовый класс, от которого наследуются все классы, которые будем сохранять в базе, пусть это будет DBData. Тогда у него будут просто 3 метода: Load, Save, Delete. А обращение к базе уже будет делом DBData.


Польза от такой концепции должна быть очевидна — пользователь объектов может не думая о реализации и нюансах баз данных просто сохранять и восстанавливать свои объекты. Но на этом пути есть ряд сложностей. Рассмотрим их.

upd Дискуссия с моей стороны закончена. Спасибо всем за внимание, но правда слишком много времени ушло на дискуссию.



Начнем с нашего управляющего класса в нашем ПО. Он в том или иной виде всегда должен быть. Его ответственность достаточно обширна — он занимается склеиванием всех системных классов в нашем ПО, в нашем случае управляет также базами данных.

public class TaskManager
{
/// Ссылка менеджер задач
private static TaskManager thisInstance;
/// Ссылка на наш адаптер к MongoDB
private Database currentDatabase;
public TaskManager()
{
thisInstance = this;
currentDatabase = new Database();
currentDatabase.RunServer();
}
/// Получить указатель на менеджер задач
public static TaskManager GetInstance()
{
return thisInstance;
}
/// Получить указатель на базу данных
public Database GetDatabase()
{
return currentDatabase;
}
public void CloseDBServer()
{
if (currentDatabase != null)
{ currentDatabase.CloseServer(); }
}
}


Теперь собственно адаптер к MongoDB. Как мы видим тут может понадобится использование отображения для обобщенных методов, причем в самых разных вариациях.

public class Database
{
/// Процесс сервера MongoDB
private Process MongoDBProcess;
/// Коннект с сервером MongoDB
Mongo mongo = new Mongo();
/// Текущая база данных
IMongoDatabase db;

/// Запустить сервер
public void RunServer()
{ ... }
/// Отключить сервер
public void CloseServer()
{ ... }

/// Загрузить объект по ID
public DBData Load(DBData argObject)
{
MethodInfo locMethodGetCollection = GetCollection(argObject);
var locCollection = locMethodGetCollection.Invoke(db, null);
MethodInfo locMethodLoad = GetMethod(locCollection, "FindOne", 1, "Object");
object[] locArgs = { new { ID = argObject.ID } };
return (DBData)locMethodLoad.Invoke(locCollection, locArgs);
}
/// Сохранить объект
public void Save(DBData argObject)
{
MethodInfo locMethodGetCollection = GetCollection(argObject);
var locCollection = locMethodGetCollection.Invoke(db, null);
MethodInfo locMethodSave = GetMethod(locCollection, "Save", 1, "Object");
object[] locArgs = { argObject };
locMethodSave.Invoke(locCollection, locArgs);
}
/// Удалить объект
public void Delete(DBData argObject)
{
MethodInfo locMethodGetCollection = GetCollection(argObject);
var locCollection = locMethodGetCollection.Invoke(db, null);
MethodInfo locMethodDelete = GetMethod(locCollection, "Delete", 1, "Object");
object[] locArgs = { new { ID = argObject.ID } };
locMethodDelete.Invoke(locCollection, locArgs);
}

/// Получить число объектов
public long Count(DBData argObject)
{
MethodInfo locMethodGetCollection = GetCollection(argObject);
var locCollection = locMethodGetCollection.Invoke(db, null);
MethodInfo locMethodCount = GetMethod(locCollection, "Count");
return (long)locMethodCount.Invoke(locCollection, null);
}

/// Загрузить идентификацию всех объектов
public ArrayList LoadAllID(DBData argObject)
{
MethodInfo locMethodGeneric = InstantiationLoadAllID(argObject);
object[] locArgs = { argObject };
return (ArrayList)locMethodGeneric.Invoke(this, locArgs);
}

/// Загрузить идентификацию всех объектов
private ArrayList LoadAllID<T>(DBData argObject) where T:DBData
{
ArrayList retArray = new ArrayList();
MethodInfo locMethodGetCollection = GetCollection(argObject);
var locCollection = locMethodGetCollection.Invoke(db, null);

MethodInfo locMethodFindAll = GetMethod(locCollection, "FindAll");
ICursor<T> locCursor = locMethodFindAll.Invoke(locCollection, null) as ICursor<T>;

foreach (DBData d in locCursor.Documents)
{ retArray.Add(d.ID); }
return retArray;
}

private MethodInfo GetCollection(object argObject)
{
MethodInfo locMethodGetCollectionGeneric = null;

Type locType = typeof(IMongoDatabase);
MethodInfo[] myMethod = locType.GetMethods();
foreach (MethodInfo m in myMethod)
{
if (m.Name == "GetCollection")
{
ParameterInfo[] pi = m.GetParameters();
if (m.ReturnType.IsGenericType && pi.Length == 0)
{
locMethodGetCollectionGeneric = m;
break;
}
}
}
Type locObjectType = argObject.GetType();
Type[] locTypeArgs = { locObjectType };
return locMethodGetCollectionGeneric.MakeGenericMethod(locTypeArgs);
}

private MethodInfo InstantiationLoadAllID(object argObject)
{
MethodInfo locMethodGeneric = null;
Type locType = typeof(Database);
MethodInfo[] locMethod = locType.GetMethods(BindingFlags.NonPublic|BindingFlags.Instance);
foreach (MethodInfo m in locMethod)
{
if (m.Name == "LoadAllID")
{
ParameterInfo[] pi = m.GetParameters();
if (m.IsGenericMethod && pi.Length == 1)
{
locMethodGeneric = m;
break;
}
}
}
Type locObjectType = argObject.GetType();
Type[] locTypeArgs = { locObjectType };
return locMethodGeneric.MakeGenericMethod(locTypeArgs);
}

private MethodInfo GetMethod(object argObject, string argMethodName)
{
return GetMethod(argObject, argMethodName, 0, null);
}

private MethodInfo GetMethod(object argObject, string argMethodName, int argParamCount, string artTypeP1)
{
MethodInfo locMethod = null;

Type locType = argObject.GetType();
MethodInfo[] locMethods = locType.GetMethods();
foreach (MethodInfo m in locMethods)
{
if (m.Name == argMethodName)
{
ParameterInfo[] pi = m.GetParameters();
if (pi.Length == argParamCount)
{
if (pi.Length == 1)
{
if (pi[0].ParameterType.Name != artTypeP1)
{ continue; }
}
locMethod = m;
break;
}
}
}
return locMethod;
}
}


Но суть в том, что к адаптеру никто не обращается. У нас есть класс-родитель, наследники которого могут сохраняться и восстанавливаться из базы. Вот класс родитель:

public class DBData
{
public int ID;
public DBData()
{ }

public DBData(int argID)
{ ID = argID; }

private Database GetDB()
{
return TaskManager.GetInstance().GetDatabase();
}

public int Count()
{
return (int)GetDB().Count(this);
}
public void Save()
{
GetDB().Save(this);
}
public void Delete()
{
GetDB().Delete(this);
}
public object Load()
{
return GetDB().Load(this);
}
public ArrayList LoadAllID()
{
return GetDB().LoadAllID(this);
}
}


Далее создаем наследника:

public class StrategiesData : DBData
{
public string Name;

public StrategiesData() : base()
{ }

public StrategiesData(int argID) : base(argID)
{ }

public StrategiesData(int argID, string argName)
{
ID = argID;
Name = argName;
}
}


и далее работаем с ним элементарным образом:

StrategiesData SD = new StrategiesData(1, "Test1");
SD.Save();



и тому подобное. Что может быть проще и приятнее :)

upd. Тут я кину код, что поменяется если различных баз несколько (видим, что интерфейсы тут совсем лишние, и не нужны)

public abstract class Database
{

public abstract DBData Load(DBData argObject)
{ }
public abstract void Save(DBData argObject)
{ }
public abstract void Delete(DBData argObject)
{ }
}
public class MongoDb : Database
{
public DBData Load(DBData argObject)
{ }
public void Save(DBData argObject)
{ }
public void Delete(DBData argObject)
{ }
}
public class AnyDb : Database
{
public DBData Load(DBData argObject)
{ }
public void Save(DBData argObject)
{ }
public void Delete(DBData argObject)
{ }
}
public class TaskManager
{
private Database currentDatabase;
/// Получить указатель на базу данных
public Database GetDatabase()
{
return currentDatabase;
}
public void ChangeDB()
{
if (условия когда Mongo)
{
currentDatabase = new MongoDb()
}
if (условия когда любая другая)
{
currentDatabase = new AnyDb()
}
}
}
Теги:C#reflectionMongoDBgeneric object
Хабы: Программирование Совершенный код C#
Всего голосов 31: ↑10 и ↓21 -11
Просмотры4.4K
Комментарии Комментарии 531

Похожие публикации

Лучшие публикации за сутки