Home > .NET development > ORM своими руками (часть вторая)

ORM своими руками (часть вторая)

August 19th, 2009

В предыдущей части мы написали парочку своих аттрибутов, с помощью которых описали маппинг C# класса на таблицу базы данных. Дальше возьмем Reflection и Generic механизмы .NET и напишем класс, который будет являться CRUD контроллером наших сущностей. Для того чтобы класс работал побыстрее, парсинг метаданных вынесен в конструктор, и заглушкой в этой реализации является один метод - ResolveConnection(). Его можно сконфигурировать как в NHibernate классом-конфигуратором, либо впрыскиванием зависимости DbProviderFactory и строки подключения, но это вопросы лишь архитектуры, и я думаю у мало-мальски толкового разработчика будет много идей на этот счет.

Ниже листинг класса, позволяющего выполнять стандартные CRUD операции:

public class DataContext<T> where T: class, new()
{
private readonly string _dbTableName;
private readonly Dictionary<PropertyInfo, DbColumnAttribute> _dbAllFields;
private readonly Dictionary<PropertyInfo, DbColumnAttribute> _dbPKFields;
private readonly PropertyInfo _autoincrementProperty;
private readonly PropertyInfo _versionProperty;

public DataContext()
{
_dbAllFields = new Dictionary<PropertyInfo, DbColumnAttribute>();
_dbPKFields = new Dictionary<PropertyInfo, DbColumnAttribute>();
//parse metadata
Type t = typeof(T);
object[] attributes = t.GetCustomAttributes(typeof(DbTableAttribute), false);
if (attributes.Length > 0)
{
_dbTableName = ((DbTableAttribute)attributes[0]).Name;
}
PropertyInfo[] properties = t.GetProperties();
foreach (PropertyInfo info in properties)
{
object[] attrubutes = info.GetCustomAttributes(typeof(DbColumnAttribute), false);
if (attrubutes.Length > 0)
{
DbColumnAttribute att = ((DbColumnAttribute) attrubutes[0]);
_dbAllFields[info] = att;
if(att.IsPrimaryKey) _dbPKFields[info] = att;
if(att.AutoIncrement) _autoincrementProperty = info;
if(att.IsVersion) _versionProperty = info;
}
}
}

public IList<T> Query(string where, string order)
{
List<T> items = new List<T>();
string query = string.Format(“SELECT * FROM [{0}]“, _dbTableName);
if (!string.IsNullOrEmpty(where))
query = string.Format(“{0} WHERE {1}”, query, where);
if (!string.IsNullOrEmpty(order))
query = string.Format(“{0} ORDER BY {1}”, query, order);
//
using (DbConnection connection = ResolveConnection())
{
DbCommand cmd = connection.CreateCommand();
cmd.CommandText = query;
connection.Open();
using (DbDataReader reader = cmd.ExecuteReader())
{
while (reader.Read())
{
T item = GetItemFromReader(reader);
items.Add(item);
}
}
connection.Close();
}
//
return items;
}

public IList<T> Query(string where)
{
return Query(where, null);
}

public IList<T> LoadAll()
{
return Query(null, null);
}

public bool Delete(T item)
{
string query = string.Format(“DELETE FROM [{0}] WHERE {1}”, _dbTableName, BuildWherePKQuery(item));
using (DbConnection connection = ResolveConnection())
{
DbCommand cmd = connection.CreateCommand();
cmd.CommandText = query;
connection.Open();
return cmd.ExecuteNonQuery() > 0;
}
}

public T GetItemByPK(T itemWithPK)
{
string query = string.Format(“SELECT * FROM [{0}] WHERE {1}”, _dbTableName, BuildWherePKQuery(itemWithPK));
using (DbConnection connection = ResolveConnection())
{
DbCommand cmd = connection.CreateCommand();
cmd.CommandText = query;
connection.Open();
using (DbDataReader reader = cmd.ExecuteReader())
{
while (reader.Read())
{
T item = GetItemFromReader(reader);
return item;
}
}
connection.Close();
}
return null;
}

public bool Modify(T item)
{
if(_versionProperty != null)
{
T originalItem = GetItemByPK(item);
long versionold = Convert.ToInt64(_versionProperty.GetValue(originalItem, null));
long versionNew = Convert.ToInt64(_versionProperty.GetValue(item, null));
if(versionold != versionNew)
{
throw new BLException(“Concurency violation”, BLExceptionType.ConcurencyViolation);
}
versionold++;
_versionProperty.SetValue(item, Convert.ChangeType(versionold, _versionProperty.PropertyType) , null);
}
//
string query = string.Format(“UPDATE [{0}] SET {1} WHERE {2}”, _dbTableName, BuildModifySubQuery(item), BuildWherePKQuery(item));
using (DbConnection connection = ResolveConnection())
{
DbCommand cmd = connection.CreateCommand();
cmd.CommandText = query;
connection.Open();

return cmd.ExecuteNonQuery() > 0;
}
}

public bool Create(T item)
{
string query = string.Format(“INSERT INTO [{0}] {1}”, _dbTableName, BuildCreateQuery(item));
using (DbConnection connection = ResolveConnection())
{
connection.Open();
DbTransaction tran = connection.BeginTransaction();
try
{
DbCommand cmd = connection.CreateCommand();
cmd.CommandText = query;
cmd.Transaction = tran;
int n = cmd.ExecuteNonQuery();
if (n > 0 && _autoincrementProperty != null)
{
DbCommand cmdA = connection.CreateCommand();
cmdA.CommandText = “SELECT @@IDENTITY AS NEW_PK”;
cmdA.Transaction = tran;
object pk = cmdA.ExecuteScalar();
_autoincrementProperty.SetValue(item, Convert.ChangeType(pk, _autoincrementProperty.PropertyType), null);
}
tran.Commit();
return n > 0;
}
catch(Exception)
{
tran.Rollback();
throw;
}
}
}

public int CreateAll(IList<T> items)
{
int cnt = 0;
foreach (T item in items)
{
if (Create(item)) cnt++;
}
return cnt;
}

public int ModifyAll(IList<T> items)
{
int cnt = 0;
foreach (T item in items)
{
if (Modify(item)) cnt++;
}
return cnt;
}

public int DeleteAll(IList<T> items)
{
int cnt = 0;
foreach (T item in items)
{
if(Delete(item)) cnt++;
}
return cnt;
}

private static DbConnection ResolveConnection()
{
return new SqlConnection(“Data Source=.;Initial Catalog=test;Integrated Security=True;Pooling=False”);
}

private string BuildWherePKQuery(T item)
{
List<string> queryParts = new List<string>();
foreach (PropertyInfo info in _dbPKFields.Keys)
{
object val = info.GetValue(item, null);
string queryPart = null;
if(val != null)
queryPart = string.Format(“[{0}] = ‘{1}’”, _dbPKFields[info].Name, ToSqlVal(val));
else
queryPart = string.Format(“[{0}] IS NULL”, _dbPKFields[info].Name);
queryParts.Add(queryPart);
}
return string.Join(” AND “, queryParts.ToArray());
}

private string BuildModifySubQuery(T item)
{
List<string> queryParts = new List<string>();
foreach (PropertyInfo info in _dbAllFields.Keys)
{
if (!_dbAllFields[info].IsPrimaryKey)
{
object val = info.GetValue(item, null);
string queryPart = null;
if (val != null)
queryPart = string.Format(“[{0}] = ‘{1}’”, _dbAllFields[info].Name, ToSqlVal(val));
else
queryPart = string.Format(“[{0}] = NULL”, _dbAllFields[info].Name);
queryParts.Add(queryPart);
}
}
return string.Join(“, “, queryParts.ToArray());
}

private string BuildCreateQuery(T item)
{
List<string> fields = new List<string>();
List<string> values = new List<string>();
foreach (PropertyInfo info in _dbAllFields.Keys)
{
if (!_dbAllFields[info].AutoIncrement)
{
object val = info.GetValue(item, null);
string strVal = null;
strVal = val != null ? string.Format(“‘{0}’”, ToSqlVal(val)) : “NULL”;
values.Add(strVal);
fields.Add(_dbAllFields[info].Name);
}
}
return string.Format(“({0}) VALUES ({1})”, string.Join(“,”, fields.ToArray()), string.Join(“,”, values.ToArray()));
}

private static object ToSqlVal(object val)
{
if (val is string)
{
string strVal = (string) val;
if (!string.IsNullOrEmpty(strVal))
{
if (strVal.IndexOf(‘\”) > -1)
{
return strVal.Replace(“‘”, “””);
}
}
}
else if (val is DateTime || val is float || val is decimal || val is double)
{
return Convert.ToString(val, CultureInfo.InvariantCulture);
}

return val;
}

private T GetItemFromReader(IDataRecord reader)
{
T item = new T();
foreach (PropertyInfo info in _dbAllFields.Keys)
{
object val = reader[_dbAllFields[info].Name];
if (val != DBNull.Value)
{
if (info.PropertyType.IsGenericType && info.PropertyType.Name.IndexOf(“Nullable”) > -1)
{
PropertyInfo piNull = info.PropertyType.GetProperty(“Value”);
info.SetValue(item, Convert.ChangeType(val, piNull.PropertyType), null);
}
else
{
info.SetValue(item, Convert.ChangeType(val, info.PropertyType), null);
}
}
}
return item;
}
}

* This source code was highlighted with Source Code Highlighter.

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

DataContext<Entity1> context = new DataContext<Entity1>();
IList<Entity1> entities = context.Query(“Id < 190000″, “CreatedTs”);
Entity1 e = new Entity1() {CreatedTs = DateTime.Now, Description = “myDescription”, Name = “ee11″, Value = null };
context.Create(e);
e.Description = “Modified__”;
context.Modify(e);
context.Delete(e);

* This source code was highlighted with Source Code Highlighter.

.NET development

  1. No comments yet.
  1. No trackbacks yet.
31525 pages viewed, 36 today
17773 visits, 33 today
FireStats icon Powered by FireStats