Зачастую при работе с БД (да и не только), особенно при использовании какого-нибудь ОРМ, возникает задача упрощения тех или иных процедур, которые приходится вызывать очень часто(к примеру открытие транзакции и её коммит, запросы с NOLOCK или банальный препроцессинг данных). Конечно все можно сделать по-старинке:
void Method(sometype somevalue) { Preprocess(somevalue); OpenTransaction(); // some stuff CommitTransaction(); }
Но «Шурик, это же не наш метод!»®. Потому мы пойдем дальше и воспользуемся более гуманными средствами а именно AOP и IOC (что это такое и с чем его едят вы можете почитать в интернете, потому не вижу смысла описывать это здесь)
До некоторых пор я использовал Unity, но что-то разонравилось мне оно и я решил его(её) сменить. Альтернативных вариантов много (Spring.Net AOP, Castle, PostSharp, Aspect#, LinFu и т.д.). Мой выбор пал на LinFu уж больно часто о нем упоминается на безграничных просторах интернета. В данном экскурсе я рассмотрю простой пример реализации транзакций при использовании NHibernate, ну и если будут многочисленные просьбы трудящихся продолжу в следующих.
Итак приступим.
Для начала скачиваем необходимые библиотеки Здесь
Открываем студию, создаем проект и добавляем необходимые ссылки на наши библиотеки (NHibernate, LinFu ну и что вы там еще собираетесь использовать), создаем объекты БД и маппинги к ним и DAO. Это я думаю вы и так умеете. Для удобства я создал базовый класс для DAO:
public class NHibernateDao { ISession _session; /// <summary> /// Gets NHibernate Session /// </summary> public ISession Session { get { return _session??this.DoGetSession(); } } //some code stripped }
И соответственно наследуем все DAO от него. И вот все готово можно приступать. дабы код смотрелся симпатичнее и гуманнее мы воспользуемся атрибутами. Сперва создаем базовый атрибут для перехвата:
public abstract class InterceptionAttribute: Attribute { public abstract IInvokeWrapper GetInvokeWrapper(); //или так кому как нравится. public abstract IInterceptor GetInterceptor(); }
IInvokeWrapper и IInterceptor — это интерфейсы линфу для перехвата вызываемых методов.
NB. Выбрать надо один из методов. не надо использовать оба — дальше поймете почему.
теперь собственно атрибут перехватчик:
[AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)] public class TransactionAttribute:InterceptionAttribute { public override IInvokeWrapper GetInvokeWrapper() { return new TransactionInvokeWrapper(); } //или так кому как нравится. public override IInterceptor GetInterceptor() { return new TransactionInterceptor(); } }
ну и сами перехватчики:
public class TransactionInterceptor : IInterceptor { public object Intercept(IInvocationInfo info) { if (!(info.Target is NHibernateDao)) { throw new InvalidOperationException("Transaction Attribute must be implemeted inside NHibernateDao class or its children"); } (info.Target as NHibernateDao).Session.Transaction.Begin(); var returnValue = info.TargetMethod.Invoke(info.Target, info.Arguments); try { (info.Target as NHibernateDao).Session.Transaction.Commit(); } catch (DataException e) { (info.Target as NHibernateDao).Session.Transaction.Rollback(); throw e; } return returnValue; } }
Или вот так если вы выбрали второй метод.
public class TransactionInvokeWrapper : IInvokeWrapper { bool intercepted = true; public object DoInvoke(IInvocationInfo info) { return info.TargetMethod.Invoke(info.Target, info.Arguments); } public void BeforeInvoke(IInvocationInfo info) { (info.Target as NHibernateDao).Session.Transaction.Begin(); } public void AfterInvoke(IInvocationInfo info, object returnValue) { try { (info.Target as NHibernateDao).Session.Transaction.Commit(); } catch (DataException e) { (info.Target as NHibernateDao).Session.Transaction.Rollback(); throw e; } } }
Эти два класса были упрощены: не надо привязываться к конкретному классу, лучше сделать получении Сессии через рефлексию. Я думаю тут комментарии излишни ибо названия методов говорят за себя.
Ну вот теперь у нас готовы атрибуты и их можно расставить над нужными методами. На этом я наверное закончу первую часть ибо дальше мы уже перейдем непосредственно вглубь этого безобразия, а это заслуживает отдельной статьи. А вы пока можете выполнить вышеописанные действия.