В
этой статье продолжим тему реализации паттерна репозиторий Repository с помощью
компонентов BLToolkit. Вспомним, о чем шла речь в первой части. Мы реализовали базовый
контейнер для хранения данных с базы данных. Также описали, почему мы
использовали создание кастомного контейнера, который наследовали от написанного
нами интерфейса IEntity и зачем мы добавили
наследование от класса EditableObject. Также были описаны способы простой реализации данного контейнера и чем наш контейнер в данном случае
может помочь. 
Приступим к реализации нашего репозитория. Для того чтобы у нас был доступ к нашему контейнеру для работы с базой данных, необходимо добавить в проект UsingGenericRepository.Repository ссылку на проект UsingGenericRepository.Model. После этого создадим интерфейс для нашего репозитория.
Приступим к реализации нашего репозитория. Для того чтобы у нас был доступ к нашему контейнеру для работы с базой данных, необходимо добавить в проект UsingGenericRepository.Repository ссылку на проект UsingGenericRepository.Model. После этого создадим интерфейс для нашего репозитория.
public interface IRepository<T>
       where T :
Model.EntityBase
{
       Table<T> GetTable();
       T SelectById(long id);
       long
Insert(T entity);
       long Insert(DbManager db, T entity);
       long
InsertWithIdentity(T entity);
       long
InsertWithIdentity(DbManager db, T entity);
       long Save(T
entity);
       long
Update(T entity);
       long Update(DbManager db, T entity);
       //Обновить одно поле в базе данных
       long
UpdateField<TV>(long id, Expression<Func<T, TV>> extract, TV value);
       //Обновить одно поле в базе данных
       long
UpdateField<TV>(DbManager db, long id, Expression<Func<T, TV>> extract, TV value);
       void
Delete(T entity);
       void Delete(DbManager db, T entity);
       void DeleteById(long id);
       void
DeleteById(DbManager db, long id);
       T
CreateNewEntity();
       IQueryable<T> SelectBy(FilterType
filterType, IEnumerable<KeyValuePair<string, object>> parameters);
       IQueryable<T> SelectBy(DbManager db, FilterType filterType, IEnumerable<KeyValuePair<string, object>> parameters);
       Type GetTypeEntity();
}
public interface IRepository
{
       object
SelectById(long id);
       long Insert(object
entity);
       long Insert(DbManager db, object entity);
       long
InsertWithIdentity(object entity);
       long
InsertWithIdentity(DbManager db, object entity);
       long Save(object
entity);
       long Save(DbManager db, object entity);
       long Update(object
entity);
       long Update(DbManager db, object entity);
       void Delete(object
entity);
       void Delete(DbManager db, object entity);
       void
DeleteById(long id);
       void
DeleteById(DbManager db, long id);
       object
CreateNewEntity();
       IQueryable SelectBy(FilterType filterType, IEnumerable<KeyValuePair<string, object>> parameters);
       IQueryable SelectBy(DbManager dbManager, FilterType
filterType, IEnumerable<KeyValuePair<string, object>> parameters);
       IQueryable Select();
       Type GetTypeEntity();
       string
GetPropertyName(string dbName);
       string
GetDbPropertyName(string propertyName);
}
Интерфейс практически классический, за исключением некоторых выборок,
которые сделаны для удобства. Также немного странным может показаться выборка с
функцией SelectedBy, которая принимает в себя перечисление FilterType. Это
перечисление выглядит следующим образом:
public enum FilterType
{
       Contains,
       Equals
}
Дело в том, что данный интерфейс затачивался под конкретную реализацию
репозитория и спроектирован для удобства под BLToolkit. Для
своей реализации вы можете избавиться от такого подхода, используя только Linq to SQL для выборки значений. Как вариант, можно воспользоваться
подходом с использованием выражений (expressions). 
Изменяйте реализацию базового репозитория под ваши нужды. Давайте
посмотрим, как будет выглядеть реализация базового репозитория и имплементация
приведенного выше интерфейса. 
/// <summary>
/// Базовый
класс репозиториев
/// </summary>
public class Repository<T> : IRepository<T>, IRepository
       where T : EntityBase
{
       protected DbManager GetDbManager()
       {
             return new DbManager();
       }
       public Table<T> GetTable(DbManager
dbManager)
       {
             return
GetTableCore(dbManager, false);
       }
       private Table<T> GetTableCore(DbManager
dbManager, bool closeConnection)
       {
             return
dbManager.GetTable<T>(closeConnection);
       }
       public Table<T> GetTable()
       {
             try
             {
                    using (var db =
GetDbManager())
                    {
                           return
GetTableCore(db, true);
                    }
             }
             catch (Exception ex)
             {
                    throw new Exception(string.Format("Get
table for {0} error", typeof(T).Name), ex);
             }
       }
       public virtual T
SelectById(long id)
       {
             try
             {
                    return (from item in
GetTable()
                                  where item.Id
== id
                                  select
item).FirstOrDefault();
             }
             catch (Exception ex)
             {
                    throw new Exception(string.Format("Select
by id {0} error", typeof(T).Name), ex);
             }
       }
       public long Insert(object entity)
       {
             return
Insert((T)entity);
       }
       public long Insert(DbManager db, object entity)
       {
             return
Insert(db, (T)entity);
       }
       public long InsertWithIdentity(object entity)
       {
             return
InsertWithIdentity((T)entity);
       }
       public long
InsertWithIdentity(DbManager db, object entity)
       {
             return
InsertWithIdentity(db, (T)entity);
       }
       public long Save(object entity)
       {
             return
Save((T)entity);
       }
       public long Save(DbManager db, object entity)
       {
             return
Save(db, (T)entity);
       }
       public long Update(object entity)
       {
             return
Update((T)entity);
       }
       public long Update(DbManager db, object entity)
       {
             return
Update(db, (T)entity);
       }
       public void Delete(object entity)
       {
             Delete((T)entity);
       }
       public void Delete(DbManager db, object entity)
       {
             Delete(db,
(T)entity);
       }
       public virtual void Delete(IEnumerable<T> entity)
       {
             using (var db =
GetDbManager())
             {
                    Delete(db,
entity);
             }
       }
       public virtual long
Insert(T entity)
       {
             using (var db =
GetDbManager())
             {
                    var result
= Insert(db, entity);
                    return result;
             }
       }
       public virtual long Insert(DbManager db, T entity)
       {
             entity.Validate();
             int result;
             try
             {
                    result
= db.Insert(entity);
             }
             catch (Exception ex)
             {
                    throw new Exception(string.Format("Insert
{0} error", typeof(T).Name),
ex);
             }
             entity.AcceptChanges();
             return result;
       }
       public virtual long
InsertWithIdentity(T entity)
       {
             using (var db = GetDbManager())
             {
                    var result
= InsertWithIdentity(db, entity);
                    return result;
             }
       }
       public virtual long
InsertWithIdentity(DbManager db, T entity)
       {
             entity.Validate();
             long result;
             try
             {
                    result
= Convert.ToInt64(db.InsertWithIdentity(entity));
                    entity.Id
= result;
             }
             catch (Exception ex)
             {
                    throw new Exception(string.Format("Insert
with identity {0} error", typeof(T).Name), ex);
             }
             entity.AcceptChanges();
             return result;
       }
       public long Save(T
entity)
       {
             return entity.IsNew
                                        ?
InsertWithIdentity(entity)
                                        :
Update(entity);
       }
       public long Save(DbManager db, T entity)
       {
             return
entity.IsNew
                                        ?
InsertWithIdentity(db, entity)
                                        :
Update(db, entity);
       }
       public long
UpdateField<TV>(long id, Expression<Func<T, TV>> extract, TV value)
       {
             using (var db =
GetDbManager())
             {
                    var result
= UpdateField(db, id, extract, value);
                    return result;
             }
       }
       public long
UpdateField<TV>(DbManager db, long id, Expression<Func<T, TV>> extract, TV value)
       {
             var query =
db.GetTable<T>()
                                        .Where(e
=> e.Id == id)
                                        .Set(extract,
value);
             return
query.Update();
       }
       public virtual long
Update(T entity)
       {
             using (var db =
GetDbManager())
             {
                    var result
= Update(db, entity);
                    return result;
             }
       }
       public virtual long Update(DbManager db, T entity)
       {
             entity.Validate();
             int result;
             try
             {
                    result
= db.Update(entity);
             }
             catch (Exception ex)
             {
                    throw new Exception(string.Format("Update
{0} error", typeof(T).Name),
ex);
             }
             entity.AcceptChanges();
             return result;
       }
       public virtual void
Delete(T entity)
       {
             using (var db =
GetDbManager())
             {
                    Delete(db,
entity);
             }
       }
       public virtual void Delete(DbManager db, T entity)
       {
             try
             {
                    db.Delete(entity);
              }
             catch (Exception ex)
             {
                    throw new Exception(string.Format("Delete
{0} error", typeof(T).Name),
ex);
             }
       }
       public virtual void Delete(DbManager db, IEnumerable<T> entity)
       {
             try
             {
                    db.Delete(entity);
             }
             catch (Exception ex)
             {
                    throw new Exception(string.Format("Delete
{0} error", typeof(T).Name),
ex);
             }
       }
       object IRepository.SelectById(long id)
       {
             return
SelectById(id);
       }
       public void
DeleteById(long id)
       {
             using (var db =
GetDbManager())
             {
                    DeleteById(db,
id);
             }
       }
       public void
DeleteById(DbManager db, long id)
       {
             try
             {
                    var entity
= CreateNewEntity();
                    entity.Id
= id;
                    db.Delete(entity);
             }
             catch (Exception ex)
             {
                    throw new Exception(string.Format("Delete
by id {0} error", typeof(T).Name), ex);
             }
       }
       object IRepository.CreateNewEntity()
       {
             return
CreateNewEntity();
       }      
       IQueryable IRepository.SelectBy(FilterType filterType, IEnumerable<KeyValuePair<string, object>> parameters)
       {
             return
SelectBy(filterType, parameters);
       }
       IQueryable IRepository.SelectBy(DbManager db, FilterType filterType, IEnumerable<KeyValuePair<string, object>> parameters)
       {
             return
SelectBy(db, filterType, parameters);
       }
       public IQueryable Select()
       {
             return
GetTable();
       }
       public IQueryable<T> SelectBy(FilterType
filterType, IEnumerable<KeyValuePair<string, object>> parameters)
       {
             using (var db =
GetDbManager())
             {
                    var result
= SelectBy(db, filterType, parameters);
                    return result;
             }
       }
       public IQueryable<T> SelectBy(DbManager db, FilterType filterType, IEnumerable<KeyValuePair<string, object>> parameters)
       {
             try
             {
                    var type =
GetTypeEntity();
                    var fields
= GetFields();
                    var query =
GetTableCore(db, true) as IQueryable<T>;
                    foreach (var param in
parameters)
                    {
                           if (!fields.Contains(param.Key))
                                  throw new Exception(string.Format("Сущность {0} не содержит поле {1}",
                                                                                                            GetType().Name,
                                                                                                            param.Key), null);
                           var
propertyName = GetPropertyName(param.Key);
                           var
property = type.GetProperty(propertyName);
                           var
propertyType = property.PropertyType;
                           var
expressionParam = Expression.Parameter(type, type.Name);
                           var right =
Expression.Constant(Convert.ChangeType(param.Value, propertyType),
propertyType);
                           var left = Expression.Property(expressionParam, property);
                           LambdaExpression predicate;
                           switch
(filterType)
                           {
                                  case FilterType.Contains:
                                        {
                                               var
filterContains = Expression.Call(left, MethodInfoHelpers.MethodContains,
new Expression[] { right });
                                               predicate = Expression.Lambda(filterContains, expressionParam);
                                        }
                                        break;
                                  default:
                                        {
                                               var expr = Expression.Equal(left, right);
                                               predicate
= Expression.Lambda(expr, expressionParam);
                                        }
                                        break;
                           }
                           var
expression = Expression.Call(typeof(Queryable), "Where", new[] { query.ElementType },
                                               query.Expression,
                                               predicate);
                           query
= query.Provider.CreateQuery<T>(expression);
                    }
                    return query;
             }
             catch (Exception ex)
             {
                    throw new Exception(string.Format("Select
by {0} error", typeof(T).Name), ex);
             }
       }
       public T
CreateNewEntity()
       {
             var entity
= TypeAccessor<T>.CreateInstanceEx();
             entity.Id
= -1;
             return entity;
       }
       public Type GetTypeEntity()
       {
             return typeof(T);
       }
       /// <summary>
       /// получение свойства сущности отвечающего за поле в бд
       /// </summary>
       /// <param name="dbName">имя поля в бд</param>
       /// <returns></returns>
       public string
GetPropertyName(string dbName)
       {
             if (string.IsNullOrEmpty(dbName))
                    return dbName;
             var
properties = GetTypeEntity()
                                               .GetProperties(BindingFlags.Instance | BindingFlags.Public);
             var
property = (from p in properties
                                        let
mapFieldAttr =
                                                      (MapFieldAttribute)Attribute.GetCustomAttribute(p, typeof(MapFieldAttribute))
                                        where
mapFieldAttr != null
                                                      &&
mapFieldAttr.MapName.Equals(dbName, StringComparison.OrdinalIgnoreCase)
                                        select p)
                                        .FirstOrDefault();
             return
property == null
                                  ?
dbName
                                  :
property.Name;
       }
       private static string GetDbPropertyName(PropertyInfo property)
       {
             var
mapFieldAttribute = property.GetCustomAttributes(typeof(MapFieldAttribute), false)
                                                                                 .OfType<MapFieldAttribute>()
                                                                                 .FirstOrDefault();
             return
mapFieldAttribute != null
                           ?
string.IsNullOrEmpty(mapFieldAttribute.MapName)
                                  ?
property.Name
                                  :
mapFieldAttribute.MapName
                           :
property.Name;
       }
       /// <summary>
       /// получение имени поля в бд
       /// </summary>
       /// <param name="propertyName">имя свойства сущности</param>
       /// <returns></returns>
       public string
GetDbPropertyName(string propertyName)
       {
             if (string.IsNullOrEmpty(propertyName))
                    return
propertyName;
             var
propertyInfo = GetTypeEntity().GetProperty(propertyName);
             return
GetDbPropertyName(propertyInfo);
       }
       private string[]
GetFields()
       {
             var
objMapper = Map.GetObjectMapper(GetTypeEntity());
             return
objMapper.FieldNames;
       }
}
В приведенном примере больше всего места занимает реализация SelectedBy. Вы
можете переписать реализацию следующим образом. Добавить в интерфейс IRepository<T> два
метода:
IQueryable<T>
Select(DbManager db, Expression<Func<T, bool>> predicate);
IQueryable<T> Select(Expression<Func<T, bool>> predicate);
После этого переходим в наш класс Repository<T> и реализуем логику для этих функций. 
public IQueryable<T> Select(DbManager db, Expression<Func<T, bool>> predicate)
{
       return
GetTableCore(db, true).Where(predicate).Select(x => x);
}
public IQueryable<T> Select(Expression<Func<T, bool>> predicate)
{
       using (var db =
GetDbManager())
       {
             return
Select(db, predicate);
       }
}
С чистой совестью мы можем удалить метод SelectedBy и
оставить только что написанный. Напоследок остался  финальный штрих: как
это использовать. Для этого перейдем в наш базовый консольный проект UsingGenericRepository
и добавим в него ссылку на наш созданный репозиторий и модель данных. Для того
чтобы продемонстрировать работу с базой данных, я создал базу данных в MS SQL Server 2008 и
добавил в нее табличку AlcoProduct и Unit.
Пример
создания такой таблицы с помощью SQL скрипта:
CREATE
TABLE [dbo].[AlcoProduct](
      [Id] [int] IDENTITY(1,1) NOT NULL,
      [Code] [int] NULL,
      [Name] [nvarchar](255) NULL,
 CONSTRAINT
[aaaaaAlcoProduct_PK] PRIMARY KEY NONCLUSTERED 
(
      [Id] ASC
)WITH (PAD_INDEX 
= OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS 
= ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON
[PRIMARY]
После этого добавим классы для наших моделей в проект UsingGenericRepository.Model.
Структура проекта приведена на рисунке ниже.
Реализация
класса AlcoProductEntity имеет следующий
вид: 
[Description("Спиртосодержащая
продукция")]
[TableName("AlcoProduct")]
[Serializable]
public abstract class AlcoProductEntity : EntityBase
{
       [Required]
       [MapField("Code")]
       [Description("Код")]
       public abstract int? Code {
get; set; }
       [MaxLength(255), Required]
       [MapField("Name")]
       [Description("Название")]
       public abstract string Name { get; set; }
}
Реализация
класса UnitDataEntity приведена ниже.
[Description("Единицы
измерения")]
[TableName("Unit")]
[Serializable]
public abstract class UnitDataEntity : EntityBase
{
       [MaxLength(3)]
       [MapField("Code")]
       [Description("Код")]
       public abstract string Code { get; set; }
       [MaxLength(100), Required]
       [MapField("ShortName")]
       [Description("Условное
обозначение")]
       public abstract string
ShortName { get; set; }
       [MaxLength(255), Required]
       [MapField("Name")]
       [Description("Название")]
       public abstract string Name { get; set; }
}
Осталось посмотреть, как это работает на практике. Для этого перейдем в наш
основной проект и в функцию Main добавим следующий код: 
class Program
{
       static void Main(string[] args)
       {
             try
             {
                    var
unitRepository = new Repository<UnitDataEntity>();
                    var names =
unitRepository.GetTable().Select(x => x.Name).ToList();
                    var
alcoRepository = new Repository<AlcoProductEntity>();
                    var unit =
alcoRepository.Select(x => x.Code == 2).First();
                    var
unitEntity = unitRepository.CreateNewEntity();
                    unitEntity.Code
= "999";
                    unitEntity.Name
= "test2345";
                    unitEntity.ShortName
= "test2345";
                    unitRepository.Insert(unitEntity);
                    var
findEntity = unitRepository.GetTable().First(x => x.Code == "999");
                    Console.WriteLine("EntityName
= {0}", findEntity.Name);
             }
             catch (Exception ex)
             {
                    Console.WriteLine(ex.ToString());
             }
             Console.ReadLine();
       }
}
Запуск приложения показан ниже на экране. 
У
многих читателей могут возникнуть вполне закономерные вопросы, на часть из которых постараюсь ответить в следующей части, посвященной паттерну репозиторий. 




 
No comments:
Post a Comment