Здравствуйте, Геннадий Васильев, Вы писали:
IT>>Бизнес логике в БД вообще нечего делать. По многим причинам. ГВ>Угу. Есть только одна веская причина, по которой БЛ имеет смысл перетащить в БД: производительность на больших массивах. Но она находится в жёстком противоречии с гибкостью.
Абсолютно верно. Но в этом случае мы точно знаем за что платим.
... << RSDN@Home 1.1.4 beta 7 rev. 447>>
Если нам не помогут, то мы тоже никого не пощадим.
Наконец руки дошли.
Что-то мне это не сильно нравится все это. Кому-то надо, кому-то не надо. Если нельзя объединять усилия по достижению общей цели, то менеджеров к стенке надо ставить. (или в сортире мочить как учит одно небезызвестное лицо) . Унифицировать интерфейсы(и состояние бизнес объектов) архиважная задача на пути к коммунизму и снижения издержек при буржуазном строе. Решить это политически значительно дешевле чем реализацию(с постановкой и тестированием). Это по количеству данных(состоянию) в возвращаемом объекте. Что касается самих объектов, то тот кому нужны адреса, должен заказывать адреса. Тому кому нужно companies тот заказывает companies. Это нормально.
Теперь по решению.
Самое простое решение описанное во все книжках. Каждый заказывает то что ему нужно. То бишь:
Каждый берет столько сколько нужно. Правда маленький недостаток. Оверхед количества вызовов. Проверено на практике, что кроме криминальных случаев, некоторый оверхед по объему дешевле чем оверхед по количеству вызовов. Поэтому дополнительно предлагаю следующий вариант.
Клиент:
Finder finderCompany=new Finder();
finderCompany.ObjectName="Company";//имя заказываемого объекта
finder.AddEqualPredicate("id", 1000);//получить те объекты у которого поле id=1000
Finder finderAddress=new Finder();
finderAddress.ObjectName="Address";
finderCompany.AddJoin(finderAddress, "AddressId", "Id");//добавляем связку показывающую что также нужны адреса.
server.GetCompanies(finder);
На сервере. Тут нужно всегда знать что если работаешь c layer'ами, изволь ими пользоваться. Поэтому тут задача передать заказ из внешнего слоя в нижний решается с помощью сервиса(если конечно это не Domain Model), допустим это объект ObjectGetter.
public DTO MyInterface.GetObjects(Finder finder)
{
IList companies=ObjectGetter.GetByFinders(finder);
return FormatDTO.FormatDTOFromObjects(companies);//форматируем в формат протокола пересылки бизнес объектов
}
//здесь у нас описывается запрос уже в терминах SQL.public struct SelectDescription
{
string _selectFields;//строка для селектовstring _from;//строка для from (но без joins)string _predicates; // строка для where
Join[] _joins;//описываем с кем связаныstring _alias;//алиас таблицы в запросе для данного маппера
IMapper _mapper; //ссылка на того, кто будет собирать результаты из него
}
//отдельно описываем джоиныpublic struct Join
{
string _predicate;//предикат связываения
EnumTypeJoin _typeJoin;//по какому joinу (внешний, внутренний)
SelectDescription _joinedDescription;//кого приджоиним
}
//само получение данныхpublic IList ObjectGetter.GetByFinder(Finder finder)
{
SelectDescription selectDescriptor=GenerateDescriptor(finder);
//из полученных данных генерируем запросstring sqlSelect=GenerateCommandString(selectDescription);
ArrayList result=new ArrayList();
//естественно, если нужна независимость от базы данных, или транзакции то операции БД в отдельные классыusing (SqlConnection connection=new SqlConnection(connectionString))
{
myConnection.Open();
using(SqlDataReader myReader = new SqlCommand(sqlSelect, myConnection).ExecuteReader(CommandBehavior.CloseConnection))
{
while(myReader.Read())
{
ReadFromReader(result, myReader, selectDescription);
}
myReader.Close();
}
}
return result;
}
//заполняем структуру SelectDescription уже SQL данными public SelectDescription ObjectGetter.GenerateDesciptor()
{
//обязательно пытаемся делать через маппер для конретного типа.
//Тогда мы можем управлять поведением заполнения и языка запросов
//в случае наличия универсального хранилища метаданных(и следовательно управления языком запросов)
// это можно сделать через один объект
IMapper mapper=GetMapperByObjectName(finder.ObjectName);
//создаем дескриптор для построение select
//второй параметр важен. алиас должен быть уникальным.
SelectDescription descriptor=mapper.GetSelectDescription(mapper, GenerateAlias());
//рекурсивно делаем joinsforeach(Finder finderJoined in finder.Joins)
{
SelectDescription joinedDescriptor=ObjectGetter.GenerateDescriptor(finder, GenerateAlias());
AppendJoin(desciption, joinedDescription);//заполняем структуры Join
}
return descriptor;
}
//чтение объекта из строкиpublic void ReadFromReader(ArrayList arrayResult, SqlDataReader myReader, SelectDescription descriptor)
{
//создают и заполняют объекты безусловно мапперы
descriptor.Mapper.LoadObjects(arrayResult, description, myReader);
//заполняем по рекурсии joinsforeach (Join join in descriptor.Joins)
ReadFromReader(arrayResult, myReader, join.SelectDescriptor);
return arrayResult;
}
Код написан только примерно и сходу. Поэтому там должно быть множество ошибок (я так думаю). Просто описано достаточно, чтобы понять само решение. Result может быть (а обычно бывает) не просто ArrayList, а более сложным.
Очень интересно, что такая длинная полемика по одному вопросу
Не смог удержаться от комментария.
Собственно, мой подход в принципе — это упрощенная модель БО — без
знания про источник данных(БД)
Например ситуация: при SOA(service oriented architecture) уже никак
нельзя хранить в БО объекты доступа к источнику даных, тк сборка со всеми
БО должна быть отдельной для использования на:
— серверной стороне наряду с функциональностью доступа к БД;
— и на клиенте(отображение), чтобы не плодить лишних сущностей.
Еще замечание про ПРОКСИ. Всем известно, что проксииспользуется для:
— lazy loading,
— code security,
— remote communication.
Но, его использование — требует кодирования, при чем однотипного
Сразу оговорюсь, я давно уже это не пису, ибо EMIT как раз мне и помогает,
конечно не сырой, а в прикольной реализации пришлось...
Собственно, всякие такие штуки должны использоваться в контексте системы
генерации кода, если даная ситуация происходит часто и решается через
копирование и подправление. А кто потом менять в 100 местах будет, уж если
поменять захочется.
Еще, есть отличные ОРМ помощнички, которые неплохо работают. В том, что
я использую ВСЕГДА есть 2 режима генерации
— SelfServicing — объект сам отвечает за вычитку себя из базы (активный можно так сказать)
— Adapter — работа через адаптер (пасивный)
И напоследок, для меня самым важным моментом проектирования является разбиение на модули и
ПРАВИЛЬНАЯ их ссылочнотсь друг на друга — такой себе граф (конечно без зацикливаний).
Рисунок очень наглядноотражает основные подходы: low coupling & high cohesion, а сдесь и
переносимость, и и универсальность, и еще куча всего приятного
Здравствуйте, Damat, Вы писали:
D>И напоследок, для меня самым важным моментом проектирования является разбиение на модули и D>ПРАВИЛЬНАЯ их ссылочнотсь друг на друга — такой себе граф (конечно без зацикливаний). D>Рисунок очень наглядноотражает основные подходы: low coupling & high cohesion, а сдесь и D>переносимость, и и универсальность, и еще куча всего приятного
А каким ORM средством ты пользуешся
Здравствуйте, IDL, Вы писали:
IDL>Здравствуйте, Damat, Вы писали:
D>>И напоследок, для меня самым важным моментом проектирования является разбиение на модули и D>>ПРАВИЛЬНАЯ их ссылочнотсь друг на друга — такой себе граф (конечно без зацикливаний). D>>Рисунок очень наглядноотражает основные подходы: low coupling & high cohesion, а сдесь и D>>переносимость, и и универсальность, и еще куча всего приятного IDL>А каким ORM средством ты пользуешся