Первая попытка получилась немного сумбурна, поэтому я решил написать более последовательно.
Повторим основную идею: работу с базой нужно организовать через специально заточенный класс, а не разбрасывать код вызовов по всему проекту.
Но этому может препятствовать то, какой интерфейс предлагают производители баз данных. Мы потренируемся на базе MongoDB.
Здесь я предложу реализацию следующей логики работы:
Должен быть базовый класс, от которого наследуются все классы, которые будем сохранять в базе, пусть это будет DBData. Тогда у него будут просто 3 метода: Load, Save, Delete. А обращение к базе уже будет делом DBData.
Польза от такой концепции должна быть очевидна — пользователь объектов может не думая о реализации и нюансах баз данных просто сохранять и восстанавливать свои объекты. Но на этом пути есть ряд сложностей. Рассмотрим их.
Начнем с нашего управляющего класса в нашем ПО. Он в том или иной виде всегда должен быть. Его ответственность достаточно обширна — он занимается склеиванием всех системных классов в нашем ПО, в нашем случае управляет также базами данных.
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();
и тому подобное. Что может быть проще и приятнее :)
Автор: tac