Где лучше расположить код?
От: Eldar9x  
Дата: 07.10.12 15:01
Оценка:
Одна библиотека BLL.dll содержит бизнес-логику приложения.
Другая библиотека DAL.dll представляет собой слой доступа к данным.
В библиотеке DAL.dll есть вот такой код (используется Entity Framework — Code First):

namespace Med.Server.DAL
{
    public static class Environment
    {
        internal static readonly DB DB = new DB(); // DB - это наследник DbContext

        public static IQueryable<SheduleUnit> QueryUpdateUnits(DateTime fromDateTime)
        {            
            return from shedule in DB.Shedules
                   join unit in DB.Units on shedule.Unit.Id equals unit.Id
                   where fromDateTime > shedule.StartDateTime
                   select new SheduleUnit { Shedule = shedule, Unit = unit }; 
        }

        public static List<string> GetUpdatableUnitsAndUpdateShedule() 
        {           
            List<string> units = new List<string>();

            DateTime dateTimeNow = DateTime.Now;

            var query = QueryUpdateUnits(dateTimeNow);
            foreach (var su in query)
            {
                units.Add(su.Unit.Name);
                su.Shedule.StartDateTime = dateTimeNow + TimeSpan.FromSeconds(su.Shedule.Period);
            }

            if (units.Any())
                DB.SaveChanges();

            return units;
        }
    }
}


Правильно ли так будет вообще? Ведь метод GetUpdatableUnitsAndUpdateShedule, вроде как, содержит некую бизнесс-логику, следовательно, ему тут не место?
Может быть будет более правильно сделать класс DB public, и операции в BLL.dll производить непосредственно с ним, избавшись заодно и от класса Environment (это фасад библиотеки DAL) и сделав POCO классы тоже public?
int i;
i = (i++)+(i++);
cout << i;
Re: Где лучше расположить код?
От: Doc Россия http://andrey.moveax.ru
Дата: 07.10.12 16:15
Оценка: 4 (1)
Здравствуйте, Eldar9x, Вы писали:

E>Правильно ли так будет вообще? Ведь метод GetUpdatableUnitsAndUpdateShedule, вроде как, содержит некую бизнесс-логику, следовательно, ему тут не место?


Как вариант — сделать в DAL c репозиторием+спецификации (или QueryObject), а логику вынести туда где им место — в BL.
Re: Где лучше расположить код?
От: GlebZ Россия  
Дата: 07.10.12 16:53
Оценка: 4 (1)
Здравствуйте, Eldar9x, Вы писали:

Основная проблема — это то что ты в dal ввел контекст транзакции. Лучше всего уже на уровне бизнес-сценариев открывать и закрывать контекст с сохранением/ролебэком как только это возможно. Если ты введешь класс контекста транзакции в бизнес логику, тогда все станет проще. А в случае многопоточности, такой статик db вообще не подходит.
Можно сделать класс типа:

public class DbContext:IDisposable
{
   DB db;
   DAL MyDal{get;set;}
   public DbContext(MyDal dal) 
   {
      db=new DB();
       CallContext.SetData("DbContext", this);
       ....
   }
   public static MyDal{get{ return ((DbContext)CallContext.GetData("DbContext")).MyDal;}}
   public static int Commit(){((DbContext)CallContext.GetData("DbContext")).db.SaveChanged();}
   public void Dispose(){ db.Dispose(); CallContext.SetData("DbContext", null);}
}

В этом случае использование примерно:

public void bbbInBLL()
{
   using (new DBContext())
   { 
       var b=DbContext.MyDal.GetB();
       DbContext.MyDal.SetB(b);
       DbContext.Commit();
   }
}

Это просто штрихи.
Re: Где лучше расположить код?
От: Ziaw Россия  
Дата: 08.10.12 03:50
Оценка: +1
Здравствуйте, Eldar9x, Вы писали:

E>Одна библиотека BLL.dll содержит бизнес-логику приложения.

E>Другая библиотека DAL.dll представляет собой слой доступа к данным.
E>В библиотеке DAL.dll есть вот такой код (используется Entity Framework — Code First):

А я давно уже не выделяю DAL. Считаю, что ORM вроде EF вполне достаточный DAL.
Re[2]: Где лучше расположить код?
От: Eldar9x  
Дата: 08.10.12 14:17
Оценка:
Здравствуйте, Doc, Вы писали:

Doc>Как вариант — сделать в DAL c репозиторием+спецификации (или QueryObject), а логику вынести туда где им место — в BL.


Doc, а можете чуть поподробнее, хотя бы небольшие фрагменты кода, как должен выглядеть репозитарий и спецификации в DAL?

Здравствуйте, GlebZ, Вы писали:

Правильно ли я понимаю, что в случае MS SQL Server при каждом вызове будет использоваться пул соединений к базе?

using(DB db = new DB()) // где DB - наследник DbContext из EF
{
   // ...
}


В случае MS SQL Server CE приходится каждый раз ждать, пока контекст сформируется, и поэтому я сохраняю DbContext в статик поле, и переиспользую его для каждого вызова. Но обращение происходит из нескольких потоков, как тогда быть? С одной стороны, создание нового контекста на каждый запрос к базе начинает тормозить приложение, а с другой стороны,в случае реюза одного статического DbContext, возникает проблема многопоточности...

Здравствуйте, Ziaw, Вы писали:

Z>А я давно уже не выделяю DAL. Считаю, что ORM вроде EF вполне достаточный DAL.


Ну, не знаю... Мне кажется, мешать в кучу бизнес-логику и объекты БД не очень хорошая идея.
int i;
i = (i++)+(i++);
cout << i;
Re[3]: Где лучше расположить код?
От: matumba  
Дата: 08.10.12 16:50
Оценка:
Здравствуйте, Eldar9x, Вы писали:

Z>>А я давно уже не выделяю DAL. Считаю, что ORM вроде EF вполне достаточный DAL.


+1, только юзаю BLToolkit.

E>Ну, не знаю... Мне кажется, мешать в кучу бизнес-логику и объекты БД не очень хорошая идея.


Чем конкретно плоха эта идея?
Re[4]: Где лучше расположить код?
От: Eldar9x  
Дата: 08.10.12 18:13
Оценка:
Здравствуйте, matumba, Вы писали:

M>Чем конкретно плоха эта идея?


Ну вот представьте себе проект в Visual Studio, в котором перемешаны POKO классы с классами БЛ
int i;
i = (i++)+(i++);
cout << i;
Re: Где лучше расположить код?
От: MozgC США http://nightcoder.livejournal.com
Дата: 08.10.12 21:42
Оценка:
Во-первых, мне не нравится название класса — Environment. У меня классы в DAL называются типа UnitAccessor, ScheduleAccessor (Schedule, кстати, пишется через "c") и т.д.
Во-вторых, да, я склоняюсь что логике из GetUpdatableUnitsAndUpdateShedule() место в слое бизнес-логики.
Чего-то более конкретного подсказать не могу, т.к. с Entity Framework не работал.

У меня методы из бизнес-логики могут выглядеть так:

public void DoSomething()
{
  IList<ScheduleUnit> scheduleUnits = ScheduleUnitAccessor.GetUnitsForUpdate(...);
  
  foreach(ScheduleUnit su in scheduleUnits)
  {
    // do something according to business logic
  }

  ScheduleAccessor.Save(scheduleUnits);
}


Если вызывается несколько методов из DAL, которые должны выполняться в транзакции, то просто в методе DoSomething() все будет обернуто в using(var transactionScope = new TransactionScope) { }
Re[3]: Где лучше расположить код?
От: MozgC США http://nightcoder.livejournal.com
Дата: 08.10.12 21:56
Оценка: +1
Здравствуйте, Eldar9x, Вы писали:

Z>>А я давно уже не выделяю DAL. Считаю, что ORM вроде EF вполне достаточный DAL.

E>Ну, не знаю... Мне кажется, мешать в кучу бизнес-логику и объекты БД не очень хорошая идея.

Это тема очень холиварная. Но я все-таки за отдельный DAL. Основных причин 2:

1) Повторное использование методов из DAL
2) Структурирование кода/проекта (код начинает выглядеть проще и понятнее, DAL-методы можно отдельно протестировать)
Re[5]: Где лучше расположить код?
От: Doc Россия http://andrey.moveax.ru
Дата: 09.10.12 01:09
Оценка:
Здравствуйте, Eldar9x, Вы писали:

E>Ну вот представьте себе проект в Visual Studio, в котором перемешаны POKO классы с классами БЛ


Вы POCO c DTO не путаете?
Re[3]: Где лучше расположить код?
От: Doc Россия http://andrey.moveax.ru
Дата: 09.10.12 01:22
Оценка: 4 (1)
Здравствуйте, Eldar9x, Вы писали:

E>Doc, а можете чуть поподробнее, хотя бы небольшие фрагменты кода, как должен выглядеть репозитарий и спецификации в DAL?


Вот тут раскрыто:
http://www.techdays.ru/videos/3596.html

Про QueryObject можно почитать тут
http://blog.byndyu.ru/2011/08/repository.html
Re[4]: Где лучше расположить код?
От: HowardLovekraft  
Дата: 09.10.12 06:45
Оценка: 5 (1)
Здравствуйте, MozgC, Вы писали:

MC>Это тема очень холиварная. Но я все-таки за отдельный DAL. Основных причин 2:

"The world is changed. I feel it in the water. I feel it in the earth. I smell it in the air."
(C)

IMHO, когда не было LINQ, и ORM не были так широко распостранены, то выделение отдельного слоя для доступа к данным было оправданно.

Сейчас часть работы "классического" DAL берет на себя ORM (объекты->SQL и обратно), часть — LINQ-провайдер этого же ORM. По сути, от DAL остается только выставленный наружу IQueryable (чтоб можно было написать заглушку для целей тестирования BLL), методы Add/Remove/SaveChanges, да может быть, ряд действительно сложных и действительно переиспользуемых запросов через этот же IQueryable, оформленных в виде отдельных методов.

То, что осталось, конечно, можно называть DAL, только по содержанию это несколько иное.

Наверное, бывают клинические случаи, когда у вас в базе хранятся одни объекты, а бизнес-логика рулит другими объектами, отличающимися настолько, что ORM не сможет это переварить самостоятельно, и вы вынуждены писать мэппинг руками. Но, кмк, таких случаев все-таки меньшинство.
Re[6]: Где лучше расположить код?
От: Eldar9x  
Дата: 09.10.12 07:30
Оценка:
Здравствуйте, Doc, Вы писали:

Doc>Вы POCO c DTO не путаете?


У меня DTO объекты находятся в слое бизнесс-логики. А POKO объекты трансформируются в DTO.

Здравствуйте, Ziaw, Вы писали:

Z>А я давно уже не выделяю DAL. Считаю, что ORM вроде EF вполне достаточный DAL.


Вообщем, я почти к этому и пришел, с небольшим отличием.
Библиотеку DAL оставляем, но держим в ней только голые POKO объекты без какой-либо бизнес-логики.
Все linq запросы, различные операции с POKO объектами выносим в библиотеку BLL.

Есть небольшая проблема в этой схеме. Поначалу я думал, что слой БЛ не должен ничего знать о EF. То есть код БЛ должен обращаться к слою DAL только через операции с обычными CLR типами без обращения к типам EF. Точно так же как библиотека BLL понятия не имеет о том, кто ее будет использовать (у меня это сервис WCF). Поэтому и был сделан класс Environment.
То есть другими словами имеем такую цепочку связей:

WCF->BLL->DAL->EF


Плюс такой схемы в том, что в слой BLL добавляется только ссылка на библотеку DAL, и не добавляются ссылки на нэймспейсы и сборки из EF.
Если же мы экспортируем из DAL POKO объекты EF, позволяя работать с ними напрямую (позволяя использовать тот же linq, например), то приходится ссылки на EF добавлять не только в библиотеку DAL, но и в BLL. Но я с этим смирился.

Здравствуйте, MozgC, Вы писали:

MC>Во-первых, мне не нравится название класса — Environment.


Вот от него я тоже избавился. Экспортируем все POKO объекты наружу и всего делов. И linq запросы в коде бизнесс-логики выглядят совсем даже неплохо

MC>У меня методы из бизнес-логики могут выглядеть так:

MC> IList<ScheduleUnit> scheduleUnits = ScheduleUnitAccessor.GetUnitsForUpdate(...);

Название класса ScheduleUnitAccessor уже попахивает бизнесс-логикой
int i;
i = (i++)+(i++);
cout << i;
Re[7]: Где лучше расположить код?
От: Doc Россия http://andrey.moveax.ru
Дата: 09.10.12 12:01
Оценка:
Здравствуйте, Eldar9x, Вы писали:

Doc>>Вы POCO c DTO не путаете?

E>У меня DTO объекты находятся в слое бизнесс-логики. А POKO объекты трансформируются в DTO.

Тогда вопрос — как вы определяете POCO и DTO?
Re[3]: Где лучше расположить код?
От: GlebZ Россия  
Дата: 09.10.12 12:58
Оценка:
Здравствуйте, Eldar9x, Вы писали:

E>В случае MS SQL Server CE приходится каждый раз ждать, пока контекст сформируется, и поэтому я сохраняю DbContext в статик поле, и переиспользую его для каждого вызова. Но обращение происходит из нескольких потоков, как тогда быть? С одной стороны, создание нового контекста на каждый запрос к базе начинает тормозить приложение, а с другой стороны,в случае реюза одного статического DbContext, возникает проблема многопоточности...

1. Пул — это не свойство MSSQL. Это свойство Entities и провайдера. Очень сомневаюсь, что оно вырублено для ce.
2. Думается мне, что формирование коннекта к локальной базе, ни разу не заметно на фоне запросов к данным.
3. В случае многопоточности — да.
Re[4]: Где лучше расположить код?
От: Eldar9x  
Дата: 11.10.12 05:15
Оценка:
Здравствуйте, Doc, Вы писали:

Doc>Тогда вопрос — как вы определяете POCO и DTO?


POKO — обычные классы без родителей, с указанием аттрибутов из DataAnnotations. Находятся в библиотеке DAL.
DTO — классы, с указанием аттрибутов DataContract и DataMember.Находятся в библиотеке WCF сервиса.
Промежуточные классы, находятся в библиотеке BLL. C помощью них происходит трансформация POKO классов в DTO классы.
WCF сервис видит только промежуточные классы и на их основе строит классы DTO, а библиотека BLL видит только классы из DAL.
Получается тройное дублирование, но я просто я не знаю, как по другому разделить слои в соответствии с цепочкой WCF->BLL->DAL...

Здравствуйте, GlebZ, Вы писали:

GZ>Здравствуйте, Eldar9x, Вы писали:


E>>В случае MS SQL Server CE приходится каждый раз ждать, пока контекст сформируется, и поэтому я сохраняю DbContext в статик поле, и переиспользую его для каждого вызова. Но обращение происходит из нескольких потоков, как тогда быть? С одной стороны, создание нового контекста на каждый запрос к базе начинает тормозить приложение, а с другой стороны,в случае реюза одного статического DbContext, возникает проблема многопоточности...

GZ>1. Пул — это не свойство MSSQL. Это свойство Entities и провайдера. Очень сомневаюсь, что оно вырублено для ce.
GZ>2. Думается мне, что формирование коннекта к локальной базе, ни разу не заметно на фоне запросов к данным.
GZ>3. В случае многопоточности — да.

Как оказалось, во всяком случае, для MS SQL Server, тормозит самая первая выборка, это что-то связанное с начальной загрузкой модели базы данных в память EF. Другими словами, необходимо либо "прогревать" EF перед использованием, либо смириться с тем, что первый вызов будет медленный. Таким образом, для MS SQL Server я использую создание отдельного контекста на каждое обращение к БД. Насчет MS SQL CE — не проверял, скорее всего, причина в том же. У меня сейчас архитектура приложения немного изменилась, я просто скачиваю все настройки в память клиента с сервера, поэтому, скорее всего, локальная база на клиенте и не понадобится.
int i;
i = (i++)+(i++);
cout << i;
Re[4]: Где лучше расположить код?
От: HowardLovekraft  
Дата: 11.10.12 05:38
Оценка:
Здравствуйте, GlebZ, Вы писали:

GZ>1. Пул — это не свойство MSSQL. Это свойство Entities и провайдера. Очень сомневаюсь, что оно вырублено для ce.

Зря сомневаетесь
Автор: HowardLovekraft
Дата: 27.09.12
.
Re[5]: Где лучше расположить код?
От: HowardLovekraft  
Дата: 11.10.12 05:59
Оценка:
Здравствуйте, Eldar9x, Вы писали:

E>Как оказалось, во всяком случае, для MS SQL Server, тормозит самая первая выборка

E>Другими словами, необходимо либо "прогревать" EF перед использованием, либо смириться с тем, что первый вызов будет медленный.
В случае использования EF, это характерно для любого провайдера.
Существуют способы это оптимизировать, если торможение критично:
  • для "классического" EF (database/model first)
  • для code first: вариант 1, вариант 2.
    Ну и, конечно, не нужно создавать, модели из нескольких десятков сущностей и более — ваш сервис, который будет работать с моделью EF, не должен отвечать за все.

    E>Насчет MS SQL CE — не проверял, скорее всего, причина в том же.

    Немножко не так. Для CE, помимо тормозов при создании модели EF, накладываются тормоза, связанные с тем, что сам движок CE выгружается из памяти при закрытии последнего соединения. Т.о., если вы не держите где-то соединение-пустышку (статический экземпляр, не используемый нигде в вашем приложении), то вы при каждом поключении загружаете CE заново.
  • Re: Где лучше расположить код?
    От: sertstyle Россия http://sert-style.ru/
    Дата: 11.10.12 06:14
    Оценка:
    Здравствуйте, Eldar9x, Вы писали:

    E>Одна библиотека BLL.dll содержит бизнес-логику приложения.

    E>Другая библиотека DAL.dll представляет собой слой доступа к данным.
    E>В библиотеке DAL.dll есть вот такой код (используется Entity Framework — Code First):

    E>
    E>namespace Med.Server.DAL
    E>{
    E>    public static class Environment
    E>    {
    E>        internal static readonly DB DB = new DB(); // DB - это наследник DbContext
    
    E>        public static IQueryable<SheduleUnit> QueryUpdateUnits(DateTime fromDateTime)
    E>        {            
    E>            return from shedule in DB.Shedules
    E>                   join unit in DB.Units on shedule.Unit.Id equals unit.Id
    E>                   where fromDateTime > shedule.StartDateTime
    E>                   select new SheduleUnit { Shedule = shedule, Unit = unit }; 
    E>        }
    
    E>        public static List<string> GetUpdatableUnitsAndUpdateShedule() 
    E>        {           
    E>            List<string> units = new List<string>();
    
    E>            DateTime dateTimeNow = DateTime.Now;
    
    E>            var query = QueryUpdateUnits(dateTimeNow);
    E>            foreach (var su in query)
    E>            {
    E>                units.Add(su.Unit.Name);
    E>                su.Shedule.StartDateTime = dateTimeNow + TimeSpan.FromSeconds(su.Shedule.Period);
    E>            }
    
    E>            if (units.Any())
    E>                DB.SaveChanges();
    
    E>            return units;
    E>        }
    E>    }
    E>}
    E>


    E>Правильно ли так будет вообще? Ведь метод GetUpdatableUnitsAndUpdateShedule, вроде как, содержит некую бизнесс-логику, следовательно, ему тут не место?

    E>Может быть будет более правильно сделать класс DB public, и операции в BLL.dll производить непосредственно с ним, избавшись заодно и от класса Environment (это фасад библиотеки DAL) и сделав POCO классы тоже public?

    +
    Re[5]: Где лучше расположить код?
    От: Sharov Россия  
    Дата: 11.10.12 07:05
    Оценка:
    Здравствуйте, Eldar9x, Вы писали:


    E>POKO — обычные классы без родителей, с указанием аттрибутов из DataAnnotations. Находятся в библиотеке DAL.

    E>DTO — классы, с указанием аттрибутов DataContract и DataMember.Находятся в библиотеке WCF сервиса.
    E>Промежуточные классы, находятся в библиотеке BLL. C помощью них происходит трансформация POKO классов в DTO классы.
    E>WCF сервис видит только промежуточные классы и на их основе строит классы DTO, а библиотека BLL видит только классы из DAL.
    E>Получается тройное дублирование, но я просто я не знаю, как по другому разделить слои в соответствии с цепочкой WCF->BLL->DAL...

    Вот тут спорят за POCO vs DTO. Вкратце, POCO — обычные классы некоторого языка (C#) с пропертями, и, возможно, с некоторыми поведением,
    валидацией, например, или какой-либо аггрегирующей функцией. А DTO вообще опрделяют как паттерн...
    Кстати, у меня DTO объекты находятся в DAL, т.к. многие методы в DAL уже возвращают DTO объекты,т.е. готовые к отрпавлению через WCF сервис. Хотя, возможно, это и не правильно.
    Надо будет поправить.
    Кодом людям нужно помогать!
    Re[6]: Где лучше расположить код?
    От: Eldar9x  
    Дата: 11.10.12 08:57
    Оценка:
    Здравствуйте, Sharov, Вы писали:

    S>Вот тут спорят за POCO vs DTO. Вкратце, POCO — обычные классы некоторого языка (C#) с пропертями, и, возможно, с некоторыми поведением,

    S>валидацией, например, или какой-либо аггрегирующей функцией. А DTO вообще опрделяют как паттерн...
    S>Кстати, у меня DTO объекты находятся в DAL, т.к. многие методы в DAL уже возвращают DTO объекты,т.е. готовые к отрпавлению через WCF сервис. Хотя, возможно, это и не правильно.
    S>Надо будет поправить.

    У вас WCF сервис встроен в библиотеку с BL, то есть вы не создавали отдельную специальную библиотеку для сетевого слоя?
    int i;
    i = (i++)+(i++);
    cout << i;
    Re[5]: Где лучше расположить код?
    От: GlebZ Россия  
    Дата: 11.10.12 09:18
    Оценка:
    Здравствуйте, Eldar9x, Вы писали:

    E>Как оказалось, во всяком случае, для MS SQL Server, тормозит самая первая выборка, это что-то связанное с начальной загрузкой модели базы данных в память EF. Другими словами, необходимо либо "прогревать" EF перед использованием, либо смириться с тем, что первый вызов будет медленный.

    По любому первый вызов тормозит. Но:

    Once in each application domain.

    http://msdn.microsoft.com/en-us/library/cc853327.aspx
    Re[7]: Где лучше расположить код?
    От: Sharov Россия  
    Дата: 11.10.12 10:00
    Оценка:
    Здравствуйте, Eldar9x, Вы писали:

    E>У вас WCF сервис встроен в библиотеку с BL, то есть вы не создавали отдельную специальную библиотеку для сетевого слоя?


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


    
    [ServiceBehavior(Namespace = "http://ns", InstanceContextMode = InstanceContextMode.PerSession, ConcurrencyMode = ConcurrencyMode.Single)]
        public sealed partial class SomeManager : IManager, IDisposable
    {
    ...  
    
    public IEnumerable<UserDto> Users()
       {... }
    ...
    }


    То есть данные от BL уровня, как я его понимаю, сразу отправляются в сеть.
    Т.е. если таки я правильно понял Ваш вопрос, сервисный уровень у меня встроен в BL.
    Кодом людям нужно помогать!
     
    Подождите ...
    Wait...
    Пока на собственное сообщение не было ответов, его можно удалить.