Как говорится we are proud to announce the very first fully functional Linq provider over WCF. Первый в мире полноценный Linq провайдер работающий через WCF.
Критика, замечания, пожелания, особенно специалистов по WCF и защите, приветствуются.
Запустить пример в одном процессе можно примерно так:
using (var host = new ServiceHost(new LinqService(), new Uri("net.tcp://localhost:1234")))
{
host.Description.Behaviors.Add(new ServiceMetadataBehavior());
host.Description.Behaviors.Find<ServiceDebugBehavior>().IncludeExceptionDetailInFaults = true;
host.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexTcpBinding(), "mex");
host.AddServiceEndpoint(
typeof(ILinqService),
new NetTcpBinding(SecurityMode.None)
{
MaxReceivedMessageSize = 10000000,
MaxBufferPoolSize = 10000000,
MaxBufferSize = 10000000,
CloseTimeout = new TimeSpan(00, 01, 00),
OpenTimeout = new TimeSpan(00, 01, 00),
ReceiveTimeout = new TimeSpan(00, 10, 00),
SendTimeout = new TimeSpan(00, 10, 00),
},
"LinqOverWCF");
host.Open();
var ctx = new ServiceModelDataContext(
new NetTcpBinding(SecurityMode.None)
{
MaxReceivedMessageSize = 10000000,
MaxBufferPoolSize = 10000000,
MaxBufferSize = 10000000,
CloseTimeout = new TimeSpan(00, 01, 00),
OpenTimeout = new TimeSpan(00, 01, 00),
ReceiveTimeout = new TimeSpan(00, 10, 00),
SendTimeout = new TimeSpan(00, 10, 00),
},
new EndpointAddress("net.tcp://localhost:1234/LinqOverWCF"));
var list = ctx.GetTable<Person>().ToList();
host.Close();
return list;
}
Вкратце как это работает.
BLT Linq провайдер общается с контекстом данных через интерфейс IDataContext, которому передаётся распарсенный Linq запрос в виде структуры SqlQuery. SqlQuery фактически представляет собой SQL AST. ServiceModelDataContext сериализует эту структуру и передаёт LinqService, который в свою очередь формирует SQL, выполняет запрос и возвращает данные клиенту. Всё просто.
Самый больной вопрос в данном подходе, конечно же, безопасность, если она актуальна. LinqService содержит метод ValidateQuery, который можно использовать для валидации запроса. По умолчанию, LinqService всего лишь не позволяет использовать DML операции, но, в принципе, нет никаких проблем сделать более интеллектуальную проверку структуры запроса. Например, на использование несанкционированных таблиц или полей. Собственно говоря, на эту тему хотелось бы побольше поговорить и услышать мнения насчёт типовых сценариев защиты, которые можно было бы реализовать прямо в библиотеке, т.к. хотя SqlQuery и можно анализировать, но в языках не поддерживающих patten matching это занятие не из весёлых.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, Alex Krasov, Вы писали:
AK>В пользу [QueryInterceptor] — уже известный и задокументированный способ описания.
Мне не нравится, что он возвращает Func<Entity,bool>. Таким способом нельзя задать joins. Нужно возвращать полноценный IQueriable<Entity>, тогда в нём можно будет задать любой сложности join.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
IT>Самый больной вопрос в данном подходе, конечно же, безопасность, если она актуальна.
Очень актуальна, особенно учитывая возможности BLToolkit over WCF! Насколько я понял, LinqService позволяет исполнить любой select запрос (ExecuteReader) к любой таблице в базе данных, даже если эта таблица не представлена в виде класса модели данных на клиенте. Конечно, такая задача потребует понимания механизма сериализации LinqServiceQuery, но для потенциального "злоумышленника" больших проблем не составит, так как код всегда под рукой.
>LinqService содержит метод ValidateQuery, который можно использовать для валидации запроса.
Такой подход, конечно, позволяет произвольно манипулировать клиентским запросом на сервере, но по моему стоит поддерживать минимальный набор проверок на уровне библиотеки.
>Например, на использование несанкционированных таблиц или полей. Собственно говоря, на эту тему хотелось бы побольше поговорить и услышать мнения насчёт типовых сценариев защиты, которые можно было бы реализовать прямо в библиотеке, т.к. хотя SqlQuery и можно анализировать, но в языках не поддерживающих patten matching это занятие не из весёлых.
На мой взгляд стоит реализовать basic securty на подобии WCF Data Services где можно задавать права доступа (Read, Write в вариациях — EntitySetRights Enumeration) на необходимые таблицы.
Так как модель данных в BLT over WCF реализации находится на клиенте, а сервер получает созданный на клиенте LinqServiceQuery, то также необходим контроль за списком полей, которыми клиент может манипулировать (не только запрашивать, но и использовать в Where). Наверное стоит реализовать возможность указывать список полей доступных для клиента для каждой из таблиц. По умолчанию можно отдавать все, но иметь возможность указать 'запрещенные' поля. Такого в WCF Data Services нет, так как там модель данных создается на сервере, соответственно клиент гарантировано получит только те поля, которые предоставляются сервисом.
Остается вопрос rowlevel security.
Задача примерно следующая:
— в зависимости от текущего service security context (user) необходимо вернуть набор записей, доступных этому пользователю.
В WCF Data Services решение такое:
— для каждой из таблиц можно задать optional QueryInterceptor который возвращает predicate, обязательный для этой таблицы.
Это дает возможность фильтровать возвращаемые данные на сервере в дополнение к запросу, созданному на клиенте.
В BLT можно было бы (наверное?) дополнять where необходимым фильтром на сервере.
Реализуя такие возможности out of the box, BLT позволит легко решать большинство задач связанных с security без дополнительного кода (код для объявления permissions — не в счет). В свою очередь это даст возможность грамотно и безопасно использовать BLT в WCF\Silverlight проектах большинству разработчиков.
Вообще реализация BLT over WCF — вещь шикарная — спору нет . Но именно из-за предоставляемой гибкости требует обязательной реализации ValidateQuery метода на сервере (на сегодняшний день).
Большое человеческое СПАСИБО за отличную реализацию. Однозначно будем продолжать использовать BLT в наших проектах.
Здравствуйте, Кэр, Вы писали:
IT>>Как говорится we are proud to announce the very first fully functional Linq provider over WCF. Первый в мире полноценный Linq провайдер работающий через WCF.
Кэр>Я только мельком глянул — но здесь вроде описывается похожий провайдер: Кэр>http://www.hanselman.com/blog/CreatingAnODataAPIForStackOverflowIncludingXMLAndJSONIn30Minutes.aspx
Кэр>Там запросы описанны с помощью OData/Rest (параметры запроса).
Кэр>Это аналог? Если да — то буду благодарен, если кто опубликует сравнительные характеристики. Если нет — то что я пропустил?
WCF Data Services хотя и содержат в своём названии WCF, но на самом деле, как ты совершенно справедливо заметил, это скорее Linq over OData со всеми вытекающими последствиями.
Принципиальное отличие BLT в том, что ты можешь конфигурировать Linq отдельно, WCF отдельно, а потом просто сказать этим парням — поехали.
ЗЫ. Ещё к родственным технологиям надо бы добавить RIA Services. Но они только для SL.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, adontz, Вы писали:
A>На уровне ТЗ, например. На том же уровне он может быть НЕ фиксирован, а наоборот весьма динамическим. То есть если у тебя Admins, Users, Anonymous, как на большинстве сайтов, то роли фиксированы. Но в других случаях список ролей модет менятся после установки и инициализации приложения.
Ну и что? По любому для того, чтобы роли как-то влияли на ход выполнения приложения у тебя должен быть код, который это поддерживает. То ли это User.InRole, то ли компонент, читающий конфиг или метаданные из базы. Такой код должен быть. Или я опять чего-то не понимаю?
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, koandrew, Вы писали:
K>Обе проблемы элементарно решаются переопределением индексёра с добавлением желаемого поведения. K>Короче, если вам "ехать", а не шашечки, то больших проблем я не наблюдаю...
Это понятно, только затычек с этим SL и так получается не мало. Зачем нам лишняя грязь, которую в принципе можно вычистить.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, adontz, Вы писали:
A>Ещё я тут подумал. Что делать с правами доступа проверяемыми не на уровне БД? Например, logon hours.
Что хочешь, то и делай. Одно другому не мешает.
A>Что делать с кешированием, журналированием (я хочу журналировать не только запросы)?
Кешировать вполне можно и даже лучше на клиенте. А что такого особенного ты хочешь журналировать?
A>ИМХО очень нерасширяемое решение получилось. Катит только как QuickStart. И преимущество у QuickStart тогда должно быть только одно — с него должно быть можно быстро и безболезненно соскочить.
Никто никого не заставляет использовать только это решение. И тем более никто не отбирает у тебя твои существующие инструменты. Считай, что это просто ещё один молоток в твоей мастерской.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, adontz, Вы писали:
A>Возможно, имеетс мысл создать два уровня фильтраци: на уровне БД, который должен съекономить чтение с диска и на уровне сервера приложений, который уже под чистую всё отфильтрует.
Видишь ли, BLT решает строго определенную, довольно узкую задачу. Не надо из него делать всемогутера, реализация безопасности не его задача. Все что нужно в данном случае от BLT — обеспечить набор рукояток для проверки и коррекции запроса. А уж сами алгоритмыт проверки и коррекции должны быть вне BLT (ну разве что за исключением совсем типовых и универсальных, типа контроля доступа на уровне метаданных).
A> Читать с БД в полтора-два раза больше чем надо и дофильтровывать уже потом не такое уж и плохое решение если учесть конечную скорость разработки.
Потом не получится в общем случае. Потому что есть джойны, подзапросы и т.п. А не в общем — задача становится специфичной для конкретного приложения.
... << RSDN@Home 1.2.0 alpha 4 rev. 1476 on Windows 7 6.1.7600.0>>
IT>Как говорится we are proud to announce the very first fully functional Linq provider over WCF. Первый в мире полноценный Linq провайдер работающий через WCF.
Круто! Вот только хотелось бы использовать Linq over WCF из Silverlight
Здравствуйте, Andy77, Вы писали:
IT>>Как говорится we are proud to announce the very first fully functional Linq provider over WCF. Первый в мире полноценный Linq провайдер работающий через WCF.
A>Круто! Вот только хотелось бы использовать Linq over WCF из Silverlight
Над этим работаем. К сожалению, API SL в плане совместимости с FW вещь далеко не идеальная, поэтому придётся много поменять в булките, например, заменить все Hashtable на Dictionary и т.п.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
IT>Над этим работаем. К сожалению, API SL в плане совместимости с FW вещь далеко не идеальная, поэтому придётся много поменять в булките, например, заменить все Hashtable на Dictionary и т.п.
public class Hashtable : Dictionary<object, object> {}
Здравствуйте, IT, Вы писали:
IT>Нет. Hashtable отличается от Dictionary:
IT>- способом добавления элемента в словать,
Hashtable.Add(object, object)
Dictionary<TKey.TValue>.Add(TKey key, TValue value) == Dictionary<object, object>.Add(object, object)
=> одинаково IT>- способом проверки существования элемента в словаре,
Оба класса юзают метод ContainsKey IT>- поддержкой многопоточности.
Тут есть небольшые различия, это да. Но проблема в принципе решаемая.
Здравствуйте, AndrewVK, Вы писали:
AVK>Знаешь почему у Hashtable нет метода TryGetValue?
Потому что хэштаблица выросла из первого фреймворка и там этого метода не было? И дженериков там тоже не было для избежания боксинга?
Этот метод — комбинация метода ContainsKey и индексёра. Оба исполняются за константное время. Цитирую MSDN:
This method combines the functionality of the ContainsKey method and the Item property.
If the key is not found, then the value parameter gets the appropriate default value for the value type TValue; for example, 0 (zero) for integer types, false for Boolean types, and nullNothingnullptra null reference (Nothing in Visual Basic) for reference types.
Use the TryGetValue method if your code frequently attempts to access keys that are not in the dictionary. Using this method is more efficient than catching the KeyNotFoundException thrown by the Item property.
This method approaches an O(1) operation.
Так что проблемы тут нет.
AVK>Различия весьма серьезные.
multiread-singlewrite у хэштаблицы против multiread у словаря. При необходимости допиливается через перекрытие методов после наследования от словаря.
AVK>Простым способом — нет. А сложным — проще отрефакторить.
Мой поинт в том, что может оказаться проще сваять класс Hashtable с нужным поведением (да хоть тупо выдернув его код из "большого" фреймворка рефлектором и собрав под Silverlight), чем выкорчёвывать его из текущей либы, заменяя за словари и втыкая локи...
Здравствуйте, koandrew, Вы писали:
K>Потому что хэштаблица выросла из первого фреймворка и там этого метода не было? И дженериков там тоже не было для избежания боксинга?
Не угадал. Дело в поведении того и другого класса при отсутствии значения по указанному ключу.
AVK>>Различия весьма серьезные. K>multiread-singlewrite у хэштаблицы против multiread у словаря. При необходимости допиливается через перекрытие методов после наследования от словаря.
Допиливать такие вещи квалифицированно весьма непросто. Кроме того, констренты хештаблицы меньше, это значит придется втыкать автоматические блокировки во всех читающих и пишущих методах, что весьма неслабый объем работы.
AVK>>Простым способом — нет. А сложным — проще отрефакторить. K>Мой поинт в том, что может оказаться проще сваять класс Hashtable с нужным поведением
Не может, уж поверь.
K> (да хоть тупо выдернув его код из "большого" фреймворка рефлектором
Нарушает лицензию. Можно дернуть из Моно, но перформанс куцего джита в SL под вопросом, а родной Dictionary наверняка оптимизирован хардкодно.
... << RSDN@Home 1.2.0 alpha 4 rev. 1476 on Windows 7 6.1.7600.0>>
Здравствуйте, AndrewVK, Вы писали:
AVK>Не угадал. Дело в поведении того и другого класса при отсутствии значения по указанному ключу.
И? В чём проблема изобразить это поведение?
AVK>Допиливать такие вещи квалифицированно весьма непросто. Кроме того, констренты хештаблицы меньше, это значит придется втыкать автоматические блокировки во всех читающих и пишущих методах, что весьма неслабый объем работы.
В целом одном методе (индексёре)? Ну это несерьёзно
AVK>Не может, уж поверь.
В моём случае оказалось проще...
AVK>Нарушает лицензию. Можно дернуть из Моно, но перформанс куцего джита в SL под вопросом, а родной Dictionary наверняка оптимизирован хардкодно.
У меня проблем с перфомансом под SL не было... Незачем пытаться оптимизировать то, что ещё не факт, что окажется источником проблем.
Собственно, если посмотреть на проблему с практической точки зрения, то может оказаться так (и в моём случае так оказалось), что от всего класса хэштаблицы нам нужно реализовать меньше десятка методов (добавление, удаление, выборка по ключу, проверка существования ключа), что не составляет большой проблемы...
Здравствуйте, koandrew, Вы писали:
IT>>- способом добавления элемента в словать, K>Hashtable.Add(object, object) K>Dictionary<TKey.TValue>.Add(TKey key, TValue value) == Dictionary<object, object>.Add(object, object) K>=> одинаково
hashTable[key] = value;
Если записи с key в таблице нет, то она будет добавлена. На этом можно строить логику.
IT>>- способом проверки существования элемента в словаре, K>Оба класса юзают метод ContainsKey
var value = hashTable[key];
Если ключа нет в таблице, то вернётся null. Для Dictionary будет исключение.
IT>>- поддержкой многопоточности. K>Тут есть небольшые различия, это да. Но проблема в принципе решаемая.
Все проблемы решаемые, но для этого нужно повозиться.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
IT>hashTable[key] = value;
IT>Если записи с key в таблице нет, то она будет добавлена. На этом можно строить логику.
IT>var value = hashTable[key];
IT>Если ключа нет в таблице, то вернётся null. Для Dictionary будет исключение.
Обе проблемы элементарно решаются переопределением индексёра с добавлением желаемого поведения.
Короче, если вам "ехать", а не шашечки, то больших проблем я не наблюдаю...
Здравствуйте, IT, Вы писали:
IT>Это понятно, только затычек с этим SL и так получается не мало. Зачем нам лишняя грязь, которую в принципе можно вычистить.
Если можно — то супер. Я говорю о ситуации, когда "вычистка" чревата переписыванием половины либы...
Здравствуйте, koandrew, Вы писали:
AVK>>Не угадал. Дело в поведении того и другого класса при отсутствии значения по указанному ключу. K>И? В чём проблема изобразить это поведение?
В лишенй работе.
AVK>>Допиливать такие вещи квалифицированно весьма непросто. Кроме того, констренты хештаблицы меньше, это значит придется втыкать автоматические блокировки во всех читающих и пишущих методах, что весьма неслабый объем работы. K>В целом одном методе (индексёре)? Ну это несерьёзно
В каком одном? Почти во всех.
K>У меня проблем с перфомансом под SL не было...
Мало ли чего у тебя не было.
K> Незачем пытаться оптимизировать то, что ещё не факт, что окажется источником проблем.
В случае библиотек это не всегда работает.
K>Собственно, если посмотреть на проблему с практической точки зрения, то может оказаться так (и в моём случае так оказалось), что от всего класса хэштаблицы нам нужно реализовать меньше десятка методов (добавление, удаление, выборка по ключу, проверка существования ключа), что не составляет большой проблемы...
Как не составляет особо большой проблемы просто заменить хештаблицу на словарь без костылей.
... << RSDN@Home 1.2.0 alpha 4 rev. 1476 on Windows 7 6.1.7600.0>>
Здравствуйте, IT, Вы писали:
IT>Как говорится we are proud to announce the very first fully functional Linq provider over WCF. Первый в мире полноценный Linq провайдер работающий через WCF.
Здравствуйте, cadet354, Вы писали:
IT>>Как говорится we are proud to announce the very first fully functional Linq provider over WCF. Первый в мире полноценный Linq провайдер работающий через WCF. C>уже есть другой, но платный
Это полноценный over WCF или поддержка WCF RIA Services?
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
IT>Как говорится we are proud to announce the very first fully functional Linq provider over WCF. Первый в мире полноценный Linq провайдер работающий через WCF.
Здравствуйте, IT, Вы писали:
IT>WCF Data Services хотя и содержат в своём названии WCF, но на самом деле, как ты совершенно справедливо заметил, это скорее Linq over OData со всеми вытекающими последствиями.
IT>Принципиальное отличие BLT в том, что ты можешь конфигурировать Linq отдельно, WCF отдельно, а потом просто сказать этим парням — поехали.
Понял. Они там утверждают, что гибкость все равно осталась. Но она уже не такого уровня это точно.
Но с другой стороны в случае Linq over OData — я получаю удаленные Linq запросы и поддержку целый толпы клиентов — правильно? Сейчас в эпоху всех этих silverlight/objective-C/java это начинает таки иметь значение.
Здравствуйте, adontz, Вы писали:
A>А можно в двух словах в чём принципиальное отличие от двузвенки?
Запрос контролируется сервером со всеми вытекающими последствиями. К тому же, бич двухзвенки — это рост количества соединений к серверу БД пропорционально количеству пользователей. При тысячах пользователей сервера БД просто дают дуба.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
A>>А можно в двух словах в чём принципиальное отличие от двузвенки? IT>Запрос контролируется сервером со всеми вытекающими последствиями.
Зависит от уровня контроля. Насколько легко организовать row level security по группе таблиц?
Здравствуйте, adontz, Вы писали:
A>>>А можно в двух словах в чём принципиальное отличие от двузвенки? IT>>Запрос контролируется сервером со всеми вытекающими последствиями.
A>Зависит от уровня контроля. Насколько легко организовать row level security по группе таблиц?
Этот вопрос как раз сейчас решается. При этом интересно прежде всего как это должно выглядеть. Как бы ты хотел, чтобы это выглядело?
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
A>>Зависит от уровня контроля. Насколько легко организовать row level security по группе таблиц? IT>Этот вопрос как раз сейчас решается. При этом интересно прежде всего как это должно выглядеть. Как бы ты хотел, чтобы это выглядело?
Ну у меня вообще говоря задачи весьма специфические, но если это чем-то поможет расскажу.
Права доступа могу даваться как на класс объектов (можно/нельзя кушать булки вообще), так и на конкретные объекты (можно/нельзя кушать конкретную булку).
Задаются они весьма развесисто, но благодаря триггерам и такой-то матери всё это выливается в табличку Employee GUID, Target GUID, Permissions INT, где к качестве Target может выступать как GUID объекта, так и GUID класса. Соответсвенно любой запрос предваряется проверкой можно ли оперировать с объектами необходимых классов. Если можно, то обрабатываются все объекты (строки) для которых операция не запрещена (LEFT OUTER JOIN, ISNULL), иначе все те для которых разрешена. Выглядит примено так
CREATE PROCEDURE [dbo].[ImageTag::ReadAllInstances](
@SecurityContext UNIQUEIDENTIFIER)
AS
BEGIN
DECLARE @SecurityResult INT;
DECLARE @DefaultPermissions INT;
EXECUTE @SecurityResult = [~EmployeePermissions::Check] @SecurityContext, '37caf5e2-4128-415f-9dbb-01322ec06f0a', NULL, 2048;
IF @SecurityResult = 0
BEGIN
SET @DefaultPermissions = 0
END
ELSE
BEGIN
SET @DefaultPermissions = 2048
END
SELECT
[ID],
[Format],
[Family],
[Data],
[Target/Type],
[Target/ID]
FROM
[ImageTag]
WHERE
(@DefaultPermissions = 2048)
END
CREATE PROCEDURE [dbo].[Account::ReadAllInstances](
@SecurityContext UNIQUEIDENTIFIER)
AS
BEGIN
DECLARE @SecurityResult INT;
DECLARE @DefaultPermissions INT;
EXECUTE @SecurityResult = [~EmployeePermissions::Check] @SecurityContext, '0e490d07-556d-41c4-86aa-0689dee1c98a', NULL, 2048;
IF @SecurityResult = 0
BEGIN
SET @DefaultPermissions = 0
END
ELSE
BEGIN
SET @DefaultPermissions = 2048
END
SELECT
[ID],
[HierarchyID],
[HierarchyLevel],
[HierarchyParent],
[Code],
[Name],
[Employee],
[Family],
[Organization]
FROM
[Account]
LEFT OUTER JOIN
[~EmployeePermissions] ON [~EmployeePermissions].[~Target] = [Account].[ID]
WHERE
(ISNULL([~EmployeePermissions].[~Permissions], @DefaultPermissions) & 2048) <> 0
END
Здравствуйте, adontz, Вы писали:
IT>>Этот вопрос как раз сейчас решается. При этом интересно прежде всего как это должно выглядеть. Как бы ты хотел, чтобы это выглядело? A>Ну у меня вообще говоря задачи весьма специфические, но если это чем-то поможет расскажу.
Это всё очень интересно, но хотелось бы прикинуть как должен выглядеть API управления настройками LinqService, который потом будет по этим правилам перетряхивать запросы.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
IT>Это всё очень интересно, но хотелось бы прикинуть как должен выглядеть API управления настройками LinqService, который потом будет по этим правилам перетряхивать запросы.
Ну я так далеко заглядывать не буду, так как BLToolkit пользуюсь скорее случайно, чем специально.
Могу только резюмировать: row-level security надо реализовывать на уровне БД. Выкачивать зиллион строк в Application Server чтобы в конце выдать access denied идея далёкая от хорошей. Следовательно, надо уметь как-то сообщать SQL запросу в контексте какого пользователя приложения он выполняется. Видимо, надо внедрять служебный параметр. Но откуда его брать? Нужно как-то интегрироваться с аутентификацией. Для авторизации доступа мало ковырять WHERE, нужен JOIN. В моём примере всё симметрично, у всех таблиц есть идентификатор GUID и все права доступа в одной таблице. Вообще говоря это может быть не так. И всё это, видимо, должно хорошо работать для подзапросов.
Здравствуйте, pr0ff, Вы писали:
P>+ за аналог QueryInterceptor P>ИМХО самый простой и эффективный метод. Только, конечно, надо будет не забыть применять их ко всем связям
QueryInterceptor — это хорошо. Вот только модель наша — на клиенте, а без модели Expression по человечески не построишь... Отсюда вывод — генерить модель и для сервиса На сервисе она нам, скорее всего, все равно пригодится, а для построения Expression — необходима.
Есть идеи как еще можно задать этот самый Interceptor?
Здравствуйте, IT, Вы писали:
IT>Это всё очень интересно, но хотелось бы прикинуть как должен выглядеть API управления настройками LinqService, который потом будет по этим правилам перетряхивать запросы.
Ещё я тут подумал. Что делать с правами доступа проверяемыми не на уровне БД? Например, logon hours. Что делать с кешированием, журналированием (я хочу журналировать не только запросы)? ИМХО очень нерасширяемое решение получилось. Катит только как QuickStart. И преимущество у QuickStart тогда должно быть только одно — с него должно быть можно быстро и безболезненно соскочить.
Здравствуйте, IT, Вы писали:
A>>Что делать с кешированием, журналированием (я хочу журналировать не только запросы)? IT>Кешировать вполне можно и даже лучше на клиенте. А что такого особенного ты хочешь журналировать?
Сильно зависит от модели данных. Аналитику лучше кешировать на сервере. Журналировать я собрался всё подряд, потому что, как показывает мой личный опыт, логов много не бывает и пока не тормозят систему пусть пишутся.
A>>ИМХО очень нерасширяемое решение получилось. Катит только как QuickStart. И преимущество у QuickStart тогда должно быть только одно — с него должно быть можно быстро и безболезненно соскочить. IT>Никто никого не заставляет использовать только это решение. И тем более никто не отбирает у тебя твои существующие инструменты. Считай, что это просто ещё один молоток в твоей мастерской.
Не, я не то чтобы говорю о его бесполезности. Оно может быть очень полезно как минимум на уровне прототипирования. Вопрос только в быстром и безболезненном соскакивании.
Вопрос:
— откуда вызывать DemandFieldPermission и DemandTablePermission?
Может непосредственно из QueryDeserializer.Parse() примерно в таком виде:
case (int)QueryElementType.SqlTable :
{
var sourceID = ReadInt();
var name = ReadString();
var alias = ReadString();
var database = ReadString();
var owner = ReadString();
var physicalName = ReadString();
var objectType = Read<Type>();
var sequenceAttributes = null as SequenceNameAttribute[];
#if !SILVERLIGHT
if (!LinqService.DemandTablePermission(name, demandingPermission))
throw new SecurityAccessDeniedException(string.Format("{0} access denied for {1}", demandingPermission, alias));
#endif
В этом случае удастся избежать повторного обхода десериализованного запроса, но я не уверен или можно вычислить demandingPermission из текущего контекста непосредственно в процессе десериализации.
Вопрос с удобным описанием RowLevel security пока остается открытым. Не имя модели данных на сервере можно пытаться манипулировать конструкциями вида SqlQuery.Predicate.ExprExpr(expr1, @operator, expr2), но это не удобно... Может у автора есть идеи как можно наиболее простым и интуитивно-понятным способом описать предикат не имея под рукой модели данных...?
Если предположить, что модель данных таки доступна для сервера и можно создать человеческий Expression<Func<T, bool>>, как его вставить в уже собранный BLT Query? Помнится после долгих танцев с бубном в EF 4 мне нечто подобное так и не удалось...
Здравствуйте, Alex Krasov, Вы писали:
AK>Вопрос с удобным описанием RowLevel security пока остается открытым.
Вопрос с RLS вообще отдельный, к WCF отношения почти не имеющий. И решить этот вопрос универсально невозможно, не стоит даже пытаться. Универсальная схема в современных БД тормозит очень сильно, поэтому на практике обычно используют всякие эвристики.
AK>Может у автора есть идеи как можно наиболее простым и интуитивно-понятным способом описать предикат не имея под рукой модели данных...?
А почему не имея модели?
AK>Если предположить, что модель данных таки доступна для сервера
А как иначе?
AK> и можно создать человеческий Expression<Func<T, bool>>, как его вставить в уже собранный BLT Query?
Вот про это IT и спрашивает. Уже сейчас есть ряд механик, позволяющих в определенной мере доконструировать запрос, но для секурности они не очень удобны. Поэтому нужно придумать, как такое описать, чтобы в большинстве случаев это было красиво и удобно.
... << RSDN@Home 1.2.0 alpha 4 rev. 1476 on Windows 7 6.1.7600.0>>
Здравствуйте, AndrewVK, Вы писали:
AVK>Вопрос с RLS вообще отдельный, к WCF отношения почти не имеющий. И решить этот вопрос универсально невозможно, не стоит даже пытаться. Универсальная схема в современных БД тормозит очень сильно, поэтому на практике обычно используют всякие эвристики.
Универсально не получится — согласен, но думалось сделать некий helper в дополнение к ValidateQuery который упростил бы создание предиката для фильтра записей по необходимому условию. Вообще, хотя данный вопрос и был поднят в контексте securiy, думается было бы полезно попытаться реализовать простой(упрощенный?) механизм для манипулирования LinqServiceQuery на сервере. AVK>А почему не имея модели? AK>>Если предположить, что модель данных таки доступна для сервера
AVK>А как иначе?
Так ведь для выполнения LinqServiceQuery не нужна она... Я, конечно, может чего и не досмотрел (просьба ткнуть носом в пропущенное), но для выполнения LinqServiceQuery на сервере модель не требуется. Кроме того, не вижу как можно было бы модифицировать полученный от клиента LinqServiceQuery используя серверную модель данных (предположив что мы ее таки создали).
AK>> и можно создать человеческий Expression<Func<T, bool>>, как его вставить в уже собранный BLT Query?
AVK>Вот про это IT и спрашивает. Уже сейчас есть ряд механик, позволяющих в определенной мере доконструировать запрос, но для секурности они не очень удобны.
А можно какой-нить пример такой деконструкции (я еще лучше — конструкции)? Кроме как использования классов из семейства SqlQuery.Predicate.* для описания нужного предиката на стороне сервера ничего на ум не приходит...
AVK>Поэтому нужно придумать, как такое описать, чтобы в большинстве случаев это было красиво и удобно.
Что до большинства случаев — так для них как раз и подошло бы описание вида Expression<Func<T, bool>> — это просто, но позволит описать security filter для небольших задач.
Здравствуйте, Alex Krasov, Вы писали:
AK>Универсально не получится — согласен, но думалось сделать некий helper в дополнение к ValidateQuery который упростил бы создание предиката для фильтра записей по необходимому условию.
Для этого нужно понять, какие вообще бывают подобные предикаты. Именно в этом суть исходного вопроса и состояла.
AVK>>А как иначе? AK>Так ведь для выполнения LinqServiceQuery не нужна она...
Не совсем так. Откидывание на клиента структуры БД не есть разумный вариант. В идеале на клиенте должна быть чистая модель, а модель с маппингом уже на сервере. Маппинг, как ты сам понимаешь, без модели быть не может. А маппинг на клиенте годится только для игрушечных приложений.
AK>А можно какой-нить пример такой деконструкции (я еще лучше — конструкции)?
Например то, как собственные sql-функции добавляются. Или как создаются кастомные подзапросы для методов.
... << RSDN@Home 1.2.0 alpha 4 rev. 1476 on Windows 7 6.1.7600.0>>
Здравствуйте, AndrewVK, Вы писали:
AK>>Универсально не получится — согласен, но думалось сделать некий helper в дополнение к ValidateQuery который упростил бы создание предиката для фильтра записей по необходимому условию. AVK>Для этого нужно понять, какие вообще бывают подобные предикаты. Именно в этом суть исходного вопроса и состояла.
Возможно, имеетс мысл создать два уровня фильтраци: на уровне БД, который должен съекономить чтение с диска и на уровне сервера приложений, который уже под чистую всё отфильтрует. Читать с БД в полтора-два раза больше чем надо и дофильтровывать уже потом не такое уж и плохое решение если учесть конечную скорость разработки.
AVK>Не совсем так. Откидывание на клиента структуры БД не есть разумный вариант. В идеале на клиенте должна быть чистая модель, а модель с маппингом уже на сервере. Маппинг, как ты сам понимаешь, без модели быть не может. А маппинг на клиенте годится только для игрушечных приложений.
Что-то я упускаю — даже после повторного прохода по коду не вижу где бы можно было вклинится с маппингом серверной модели данных. На сколько я разобрался, вся информация для запроса (включая весь mapping) подготавливается на стороне клиента, а на сервере производится построение описанного в SqlQuery (собирается из LinqServiceQuery) SQL выражения с помощью конкретного SqlQueryProvider. В каком слое происходит (может произойти?) маппинг с помощью сервеной модели? То, что можно руками допилить собранный SqlQuery на сервере — понятно. Для того нам ValidataQuery и был дан 'сверху' Я пытаюсь понять или есть возможность использовать на сервере стандартный model mapping подход, применяемый в BLT, к запросу построенному на клиенте. В качестве стандарта я подразумеваю описание POCO модели со всеми необходимыми mapping attributes.
Здравствуйте, Alex Krasov, Вы писали:
AK>Я пытаюсь понять или есть возможность использовать на сервере стандартный model mapping подход, применяемый в BLT, к запросу построенному на клиенте. В качестве стандарта я подразумеваю описание POCO модели со всеми необходимыми mapping attributes.
Использовать без проблем. Вопрос — как?
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, Alex Krasov, Вы писали:
AK>Что-то я упускаю — даже после повторного прохода по коду не вижу где бы можно было вклинится с маппингом серверной модели данных.
Я текущую реализацию пока не разглядывал подробно. Но если информация о меппинге висит в клиентской модели, значит пока реализация не годится для промышленного применения.
... << RSDN@Home 1.2.0 alpha 4 rev. 1476 on Windows 7 6.1.7600.0>>
Модель можно добавить либо по таблично, либо сразу весь DataContext скормить. С этим проблем не будет. Дальше вот что подумалось.
1. Для задания разрешений на уровне таблиц и полей можно ввести дополнительный атрибут, например, AccessPermissions с соответствующими флагами.
2. Для задания фильтров для таблиц, можно придумать какой-нибудь механизм регистрации Expressions, из которых мы будем добывать SQL, а потом подставлять его в приходящие запросы.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
IT>Модель можно добавить либо по таблично, либо сразу весь DataContext скормить. С этим проблем не будет.
что-то я никак не могу сообразить как это будет выглядеть на сервере Заклинило видать...
IT>Дальше вот что подумалось. IT>1. Для задания разрешений на уровне таблиц и полей можно ввести дополнительный атрибут, например, AccessPermissions с соответствующими флагами.
Можно и через атрибуты, но хотелось бы иметь какой-нить вариант DemandPermission(string source, Rights rights) с возможностью его перекрывать в своем коде. Таким образом можно было бы реализовывать всякие интересности типа разрешений работать только с теми таблицами\колонками, которые доступны вызывающему пользователю.
IT>2. Для задания фильтров для таблиц, можно придумать какой-нибудь механизм регистрации Expressions, из которых мы будем добывать SQL, а потом подставлять его в приходящие запросы.
мда... Это все мое косноязычие наверное, но именно это я и имел в виду, предлагая реализовать аналог [QueryInterceptor("DataSourceName")] который, собсно, и должен возвращать этот самый Expression для указанного DataSource.
Здравствуйте, IT, Вы писали:
IT>2. Для задания фильтров для таблиц, можно придумать какой-нибудь механизм регистрации Expressions, из которых мы будем добывать SQL, а потом подставлять его в приходящие запросы.
Хочу заметить, что этот механизм должен уметь грамотно работать с FK.
Здравствуйте, adontz, Вы писали:
A>Здравствуйте, IT, Вы писали:
IT>>2. Для задания фильтров для таблиц, можно придумать какой-нибудь механизм регистрации Expressions, из которых мы будем добывать SQL, а потом подставлять его в приходящие запросы.
A>Хочу заметить, что этот механизм должен уметь грамотно работать с FK.
Если мы задаем Expression для определенного DataSource (к примеру — таблице), то всякий раз при обращении к этой таблице должен быть применен зарегистрированный Expression. Какого вида получится запрос — вопрос интересный...
Попробуем на примере:
обычного вида запрос:
select o.OrderDate, od.ProductID
from [Order Details] od
join Orders o on od.OrderID = o.OrderID
where o.CustomerID='ALFKI'
с помощью выше предложенного решения конечный запрос может выглядеть как то так:
select o.OrderDate, od.ProductID
from [Order Details] od
join Orders o on od.OrderID = o.OrderID
where-- Security predicate for [Order Details]
(od.OrderID in (select so.OrderID from Orders so where so.CustomerID='ALFKI'))
and-- Security predicate for [Orders]
(o.CustomerID = 'ALFKI')
или даже так:
select o.OrderDate, od.ProductID
from-- Security predicate for [Order Details]
(select * from [Order Details] od where od.OrderID in (select so.OrderID from Orders so where so.CustomerID='ALFKI')) od
join--Security predicate for [Orders]
(select * from Orders o where (o.CustomerID = 'ALFKI')) o
on od.OrderID = o.OrderID
Вернут ли эти запросы одинаковый набор записей — да;
Оптимален ли запрос — нет;
Можно ли это анализировать и избегать дубликатов — не знаю;
Второй и третий запросы в, конечном счете, будут иметь одинаковый Execution plan, но первый, ессно, наиболее эффективен.
При сложных join в конечно итоге получится монстра, но тут самое время будет задуматься о подходящем view (или еще какой оптимизации), который упростил бы задачу.
Здравствуйте, Alex Krasov, Вы писали:
AK>Попробуем на примере:
Плохой пример. Хороший пример — перемещение денег между счетами или товара между складами в бухгалтерской номенклатуре. То есть на строки таблицы Transactions явных прав доступа вообще не существует.
А вот на строки таблиц Accounts и Currencies права доступа есть.
Минимальная задача может формулироваться как: Показать пользователю движения только по счетам к которым он имеет доступ, только для валют к которым он имеет доступ.
SELECT
ID,
DebitAccount, -- FOREIGN KEY REFERENCES Accounts(ID)
DebitCurrency, -- FOREIGN KEY REFERENCES Currencies(ID)
DebitAmount,
CreditAccount, -- FOREIGN KEY REFERENCES Accounts(ID)
CreditCurrency, -- FOREIGN KEY REFERENCES Currencies(ID)
CreditAmount
FROM
Transactions
INNER JOIN
Accounts AS DebitAccounts ON DebitAccounts.ID = DebitAccount
INNER JOIN
Accounts AS CreditAccounts ON CreditAccounts.ID = CreditAccount
INNER JOIN
Currencies AS DebitCurrenciesON DebitCurrencies.ID = DebitCurrency
INNER JOIN
Currencies AS CreditCurrenciesON CreditCurrencies.ID = CreditCurrency
-- Тут ещё 4 INNER JOIN на таблицы с правами доступа.WHERE-- Куча страшных условий.
Более сложная может формулироваться как: Показать пользователю движения только по счетам к которым он имеет доступ, только для валют к которым он имеет доступ, только для сумм меньше миллиона в национальной валюте по текущему курсу.
Здравствуйте, adontz, Вы писали: A> То есть на строки таблицы Transactions явных прав доступа вообще не существует.
А можно пример условия для проверки прав доступа для таблиц, в которых они явно определенны? И как в твоем случае ты хотел бы ограничить прямой доступ к таблице Transactions? К примеру какой-нить умник решит сделать:
Здравствуйте, Alex Krasov, Вы писали:
IT>>Модель можно добавить либо по таблично, либо сразу весь DataContext скормить. С этим проблем не будет. AK>что-то я никак не могу сообразить как это будет выглядеть на сервере Заклинило видать...
typeof(MyDataContext)?
IT>>1. Для задания разрешений на уровне таблиц и полей можно ввести дополнительный атрибут, например, AccessPermissions с соответствующими флагами. AK>Можно и через атрибуты, но хотелось бы иметь какой-нить вариант DemandPermission(string source, Rights rights) с возможностью его перекрывать в своем коде. Таким образом можно было бы реализовывать всякие интересности типа разрешений работать только с теми таблицами\колонками, которые доступны вызывающему пользователю.
Пока не вижу интересностей болеее, чем разделение прав для разных ролей.
IT>>2. Для задания фильтров для таблиц, можно придумать какой-нибудь механизм регистрации Expressions, из которых мы будем добывать SQL, а потом подставлять его в приходящие запросы. AK>мда... Это все мое косноязычие наверное, но именно это я и имел в виду, предлагая реализовать аналог [QueryInterceptor("DataSourceName")] который, собсно, и должен возвращать этот самый Expression для указанного DataSource.
К сожалению, Expressions и атрибуты штука не совместимая. Придётся вводить какой-нибудь другой механизм.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, adontz, Вы писали:
IT>>2. Для задания фильтров для таблиц, можно придумать какой-нибудь механизм регистрации Expressions, из которых мы будем добывать SQL, а потом подставлять его в приходящие запросы.
A>Хочу заметить, что этот механизм должен уметь грамотно работать с FK.
Joins и Associations пока никто не отменял. Всё, что нам нужно, это выражение, возвращающее кортеж конкретной сущности. Далее этот запрос можно будет поджоинить ко всем соответствующим таблицам в приходящем запросе.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
AK>>мда... Это все мое косноязычие наверное, но именно это я и имел в виду, предлагая реализовать аналог [QueryInterceptor("DataSourceName")] который, собсно, и должен возвращать этот самый Expression для указанного DataSource.
IT>К сожалению, Expressions и атрибуты штука не совместимая. Придётся вводить какой-нибудь другой механизм.
Так никто же не предлагает описывать Expression в атрибуте ... Атрибутом декларируется метод, возвращающий соответствующий Expression. Пример из MSDN:
[QueryInterceptor ("Customers")]
public Expression<Func<Customer, bool>> FilterCustomers()
{
return c => c.Name == /* Current principal name. */ &&
this.CurrentDataSource.QueryRules.Contains(
rule => rule.Name == c.Name &&
rule.CustomerAllowedToQuery == true
);
}
Вообще — не суть важно, каким именно образом регистрировать Expression для data source.
В пользу [QueryInterceptor] — уже известный и задокументированный способ описания.
Здравствуйте, Alex Krasov, Вы писали:
AK>Здравствуйте, adontz, Вы писали: A>> То есть на строки таблицы Transactions явных прав доступа вообще не существует.
AK>А можно пример условия для проверки прав доступа для таблиц, в которых они явно определенны?
AK>И как в твоем случае ты хотел бы ограничить прямой доступ к таблице Transactions? К примеру какой-нить умник решит сделать:
var q = from t in Transations select t;
Ну вот надо как-то прозрачно добавлять аж четыре условия. В моём примере это 8 JOIN'ов получается и 4 условия в WHERE. Производительность конкретно в данном примере, кстати, не такая уж и плохая, так как самих счетов/складов меньше сотни как правило.
Вообще реальная логика может быть куда сложнее:
1) Фильтрация больше/меньше по дате. Нельзя писать в закрытый бухгалтерией период (год, месяц), если ты не главный бухгалтер. Нельзя менять данные за вчерашнее число, но можно за сегодняшнее утро (больше/меньше по фрагменту даты).
2) Косвенные связи. Например, некоторые агенты могут продавать клиентам из того же города, где работают они сами. Или некоторые виды товаров можно продавать только клиентом из того же города. Причём тот же агент другие виды товаров может продать клиентам и из другого города. Но при этом скидка считается по суммарным продажам. Очевидно, что на SELECT и UPDATE нужны разные права. Вариантов куча, тут только примеры и сценарии надо собирать пару месяцев.
Здравствуйте, IT, Вы писали:
IT>1. Для задания разрешений на уровне таблиц и полей можно ввести дополнительный атрибут, например, AccessPermissions с соответствующими флагами.
Атрибут на клиентской модели?
... << RSDN@Home 1.2.0 alpha 4 rev. 1476 on Windows 7 6.1.7600.0>>
Здравствуйте, AndrewVK, Вы писали:
IT>>1. Для задания разрешений на уровне таблиц и полей можно ввести дополнительный атрибут, например, AccessPermissions с соответствующими флагами.
AVK>Атрибут на клиентской модели?
На модели, используемой на сервере.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, AndrewVK, Вы писали:
IT>>На модели, используемой на сервере.
AVK>Т.е. на сервере все таки модель есть и ее наличие там обязательно? Тогда в чем проблема?
Пока не обязательна. Но для описания разрешений её вполне можно было бы использовать.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
IT>Здравствуйте, AndrewVK, Вы писали:
IT>>>На модели, используемой на сервере.
AVK>>Т.е. на сервере все таки модель есть и ее наличие там обязательно? Тогда в чем проблема?
IT>Пока не обязательна. Но для описания разрешений её вполне можно было бы использовать.
Здравствуйте, IT, Вы писали:
IT>1. Для задания разрешений на уровне таблиц и полей можно ввести дополнительный атрибут, например, AccessPermissions с соответствующими флагами.
Т.е. описывать роли, юзеров и permissions для них в коде? Что делать, если нужно их поменять? Перекомпиляция и редеплой?
Здравствуйте, olegkr, Вы писали:
O>Т.е. описывать роли, юзеров и permissions для них в коде? Что делать, если нужно их поменять? Перекомпиляция и редеплой?
В коде описывают не юзеров, а набор абстрактных ролей, а юзеру уже потом список ролей назначают. Менять роли при таком раскладе обычно не нужно. Если же нужны собираемые в рантайме роли, то тут, очевидно, декларативное решение в коде не годится.
... << RSDN@Home 1.2.0 alpha 4 rev. 1476 on Windows 7 6.1.7600.0>>
Здравствуйте, Alex Krasov, Вы писали:
IT>>Пока не обязательна. Но для описания разрешений её вполне можно было бы использовать. AK>А для описания маппинга на сервере?
Какого маппинга?
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, olegkr, Вы писали:
IT>>1. Для задания разрешений на уровне таблиц и полей можно ввести дополнительный атрибут, например, AccessPermissions с соответствующими флагами. O>Т.е. описывать роли, юзеров и permissions для них в коде? Что делать, если нужно их поменять? Перекомпиляция и редеплой?
А как добавление новой роли может отразиться на приложении без его модификации?
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
IT>Здравствуйте, AndrewVK, Вы писали:
AVK>>Т.е. решение, когда структура БД сервера доступна на клиенте считается нормальным?
IT>На клиенте вообще-то доступна модель данных сервера, а не структура БД.
То есть, с помощью BLT можно сделать следующее?:
— MyTestLinqService предоставляет клиенту service model.
— MyTestLinqServiceClient использует модель данных, сгенерированную, к примеру, через service reference\svcutil, для построения запросов на клиенте.
— BlToolkit mapping attributes доступны только в версии модели данных на сервере. На клиенте нет аттрибутов, описывающих entity mapping\relations.
— при получении MyTestLinqService сервисом запроса от клиента использовать BLT Mapping, описанный серверной моделью данных, для генерации окончательного SQL statement
???
Здравствуйте, Alex Krasov, Вы писали:
AK>То есть, с помощью BLT можно сделать следующее?: AK>- MyTestLinqService предоставляет клиенту service model.
Клиент сам себе предоставляет модель, зачем ему сервер?
AK>- MyTestLinqServiceClient использует модель данных, сгенерированную, к примеру, через service reference\svcutil, для построения запросов на клиенте.
Как угодно.
AK>- BlToolkit mapping attributes доступны только в версии модели данных на сервере. На клиенте нет аттрибутов, описывающих entity mapping\relations.
Без relations атрибутов на клиенте не будут доступны ассоциации.
AK>- при получении MyTestLinqService сервисом запроса от клиента использовать BLT Mapping, описанный серверной моделью данных, для генерации окончательного SQL statement
Модель на сервере может использоваться для валидации и модификации SqlQuery.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
IT>Без relations атрибутов на клиенте не будут доступны ассоциации.
Так relations как раз в терминах модели описываются. Куда интереснее атрибуты, задающие имена таблиц и полей. А так же sql функции, которые сейчас могут быть ассоциированы с функциями БД.
... << RSDN@Home 1.2.0 alpha 4 rev. 1476 on Windows 7 6.1.7600.0>>
Здравствуйте, AndrewVK, Вы писали:
IT>>Без relations атрибутов на клиенте не будут доступны ассоциации. AVK>Так relations как раз в терминах модели описываются. Куда интереснее атрибуты, задающие имена таблиц и полей. А так же sql функции, которые сейчас могут быть ассоциированы с функциями БД.
Этот маппинг сейчас делается на клиенте. Но нет никаких проблем перемапить всё по своему на сервере. SqlQuery — структура, с которой можно сделать всё, что угодно.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, AndrewVK, Вы писали:
IT>>Взять то без проблем. А что делать с этой ролью? AVK>В конфиге же и использовать. Вот пример из R.Server. Речь, правда, о сервисах, но от БД принципиальных отличий нет:
А конфиг деплоить надо?
AVK>Только вот в таком варианте, очевидно, никаких деклараций в коде быть не может и непонятно в чем вопрос.
Вопрос Олега был такой:
А как добавление новой роли может отразиться на приложении без его модификации?
Так вот я и пытаюсь понять как можно новую роль задействовать без модификации приложения.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, AndrewVK, Вы писали:
IT>>А конфиг деплоить надо? AVK>Зачем?
А как? Ты у себя на машине поменял, а на сервере заработало?
IT>>Так вот я и пытаюсь понять как можно новую роль задействовать без модификации приложения. AVK>Уже понял? Или еще нет?
Ещё нет.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
IT>А как? Ты у себя на машине поменял, а на сервере заработало?
Например у нас задеплоить конфиг — на порядок меньший геммор, чем деплоить dll/exe. Во втором случае надо проходить полную процедуру QA/UAT, тестить практически все. Занимает где-то полмесяца минимум. Бюрократия, конечно, но это реальный мир и нам в нем жить.
Здравствуйте, olegkr, Вы писали:
IT>>А как? Ты у себя на машине поменял, а на сервере заработало? O>Например у нас задеплоить конфиг — на порядок меньший геммор, чем деплоить dll/exe. Во втором случае надо проходить полную процедуру QA/UAT, тестить практически все. Занимает где-то полмесяца минимум. Бюрократия, конечно, но это реальный мир и нам в нем жить.
Не важно какая там процедура. Главное, что ты вносишь изменение непосредственно в приложение.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, AndrewVK, Вы писали:
IT>>А как? Ты у себя на машине поменял, а на сервере заработало? AVK>Почему у себя на машине? Конфиг лежит на сервере, его и меняют.
Т.е. в приложение вносятся изменения.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
IT>Не важно какая там процедура. Главное, что ты вносишь изменение непосредственно в приложение.
Именно. В случае изменения конфига ты изменения не вносишь.
Здравствуйте, IT, Вы писали:
IT>Вообще-то, конфиг это часть твоего приложения. Разве нет?
Нет. Совершенно разные бюрократические процедуры. Например, я могу попросить наш веб хостинг поменять пару строчек в конфиге без проблем (please add this after that), но после изменения пары строчек в dll я вынужден деплоить приложение целиком.
Я понимаю, что теоретически это равнозначно, но попробуй докажи кому-нибудь, что ты просто тупо добавил всего одну роль в атрибуте, а не изменил бизнес логику. Если бы я имел возможность делать все, что мне угодно в продакшине... но увы (или слава Богу!), не имею.
Здравствуйте, olegkr, Вы писали:
IT>>Вообще-то, конфиг это часть твоего приложения. Разве нет? O>Нет. Совершенно разные бюрократические процедуры.
Понятно. Конфиг не часть твоего приложения, потому что у вас разные бюрократические процедуры Пусть будет так. Давай вернёмся всё же к твоему изначальному вопросу:
Т.е. описывать роли, юзеров и permissions для них в коде? Что делать, если нужно их поменять? Перекомпиляция и редеплой?
Как у тебя сейчас работают роли? Что ты изменяешь в конфиге и какой эффект от этого получается?
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
IT>Как у тебя сейчас работают роли?
Что-то типа
<modules>
<module id="huh" roles="Admin, IT Support, IPO-Managers" />
</modules>
Увы, выделить пару-тройку ролей не получится. Модулей может быть куча и разные юзера могут иметь произвольную комбинацию модулей. Плюс внутри модулей может быть разделение по юзерам, типа одни только смотрят пару страниц, другие видят все, а третьи могут еще вносить изменения. Если учитывать все комбинации, то ролей получится туева туча. Плюс к тому, членство в группах управляется бизнес менеджерами групп, а не нами. Плюс к тому, роли для dev/qa/uat/prod различаются, а наш веб хостинг хренас пустит в qa версию assembly отличную от dev.
IT>Что ты изменяешь в конфиге и какой эффект от этого получается?
Например, если нужно добавить новую группу, то пишу в веб хостинг
Please update modules.config and replace
<module id="huh" roles="Admin, IT Support, IPO-Managers" />
with
<module id="huh" roles="Admin, IT Support, IPO-Managers, New Group" />
Здравствуйте, olegkr, Вы писали:
IT>>При этом у тебя есть какой-то умный компонент, который умеет разруливать твои роли в конфиге. Правильно? O>Правильно.
Ну вот. Значит ответ на твой вопрос будет примерно таким же — написать умный компонент, который будет разруливать роли в нашем случае
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
IT>Вообще-то, конфиг это часть твоего приложения. Разве нет?
Вовсе нет. Зачем делать конфиг, если его содержимое это часть приложения? Конфиг в общем случае пишет совсем другой человек, не программист, а админ. Программист разве что базовый конфиг для экономии сил может предоставить.
... << RSDN@Home 1.2.0 alpha 4 rev. 1476 on Windows 7 6.1.7600.0>>
Здравствуйте, IT, Вы писали:
A>>Это есть роли fixed и predefined. Лучше думай о ролях, как о группах пользователей в Windows. IT>Это есть просто роли. А уж как они используются — это дело десятое.
Если спсиок ролей не фиксирован, настраивать их через конфигурационный файл будет невозможно.
Здравствуйте, AndrewVK, Вы писали:
IT>>Вообще-то, конфиг это часть твоего приложения. Разве нет?
AVK>Вовсе нет. Зачем делать конфиг, если его содержимое это часть приложения?
Чтобы иметь возможность настраивать приложение без перекомпиляции.
AVK>Конфиг в общем случае пишет совсем другой человек, не программист, а админ. Программист разве что базовый конфиг для экономии сил может предоставить.
Это в теории.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, adontz, Вы писали:
A>Если спсиок ролей не фиксирован, настраивать их через конфигурационный файл будет невозможно.
А где он должен быть фиксирован?
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
A>>Если спсиок ролей не фиксирован, настраивать их через конфигурационный файл будет невозможно. IT>А где он должен быть фиксирован?
На уровне ТЗ, например. На том же уровне он может быть НЕ фиксирован, а наоборот весьма динамическим. То есть если у тебя Admins, Users, Anonymous, как на большинстве сайтов, то роли фиксированы. Но в других случаях список ролей модет менятся после установки и инициализации приложения.
Здравствуйте, IT, Вы писали:
A>>На уровне ТЗ, например. На том же уровне он может быть НЕ фиксирован, а наоборот весьма динамическим. То есть если у тебя Admins, Users, Anonymous, как на большинстве сайтов, то роли фиксированы. Но в других случаях список ролей модет менятся после установки и инициализации приложения. IT>Ну и что? По любому для того, чтобы роли как-то влияли на ход выполнения приложения у тебя должен быть код, который это поддерживает. То ли это User.InRole, то ли компонент, читающий конфиг или метаданные из базы. Такой код должен быть. Или я опять чего-то не понимаю?
Нет-нет, ты прав, конечно должна быть какая-то проверка. Просто конкретно приведённый тобой пример не расширяем и не масштабируем. Проверка выполняется в .Net, а не в SQL, коде. Это сильно связывает руки.
Здравствуйте, AndrewVK, Вы писали:
IT>>Чтобы иметь возможность настраивать приложение без перекомпиляции. AVK>Зачем? Вот никогда не понимал этой страсти вытаскивать потроха приложения во внешний, трогаемый грязными ручками админов файл.
Да мне-то пофиг где и как его настраивать. Это же вы вроде как без конфигов жить не можете
IT>>Это в теории. AVK>И на практике тоже.
Ни разу в жизни не видел. Хотя вполне допускаю такую возможность, но не думаю, что это широкораспространённая практика.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, adontz, Вы писали:
A>Просто конкретно приведённый тобой пример не расширяем и не масштабируем. Проверка выполняется в .Net, а не в SQL, коде. Это сильно связывает руки.
Конкретно мой пример можно реализовать не хуже чем проверка в SQL. Точнее это и будет проверка в SQL. Поверь мне, в своих проектах я использую секьюрити не только для доступа пользователей к определённому функционалу, но и к определённым данным тоже.
В общем, разговор у нас съехал совсем не туда. Меня сейчас интересуют типовые базовые сценарии, которые можно реализовать прямо в библиотеке. Если получится, то может быть удастся сделать их относительно легко расширяемыми.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
IT>В общем, разговор у нас съехал совсем не туда. Меня сейчас интересуют типовые базовые сценарии, которые можно реализовать прямо в библиотеке. Если получится, то может быть удастся сделать их относительно легко расширяемыми.
Ну я уже перечислил.
1) Row-level security по таблице с явными разрешениями для типов и экзмепляров.
2) Тоже самое, но с учётом разрешений на объекты ссылаемые foreign key.
3) Тоже самое, но с учётом попадания в период времени, окно со скользящими или прыгающими началом и концом.
Здравствуйте, adontz, Вы писали:
IT>>В общем, разговор у нас съехал совсем не туда. Меня сейчас интересуют типовые базовые сценарии, которые можно реализовать прямо в библиотеке. Если получится, то может быть удастся сделать их относительно легко расширяемыми.
A>Ну я уже перечислил. A>1) Row-level security по таблице с явными разрешениями для типов и экзмепляров. A>2) Тоже самое, но с учётом разрешений на объекты ссылаемые foreign key. A>3) Тоже самое, но с учётом попадания в период времени, окно со скользящими или прыгающими началом и концом.
A>Я что-то не про то говорю или про то?
Я не понимаю ни один из этих пунктов. Что такое, например, разрешение для типов и экзмепляров?
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
IT>Здравствуйте, adontz, Вы писали:
IT>>>В общем, разговор у нас съехал совсем не туда. Меня сейчас интересуют типовые базовые сценарии, которые можно реализовать прямо в библиотеке. Если получится, то может быть удастся сделать их относительно легко расширяемыми.
A>>Ну я уже перечислил. A>>1) Row-level security по таблице с явными разрешениями для типов и экзмепляров. A>>2) Тоже самое, но с учётом разрешений на объекты ссылаемые foreign key. A>>3) Тоже самое, но с учётом попадания в период времени, окно со скользящими или прыгающими началом и концом.
A>>Я что-то не про то говорю или про то?
IT> Я не понимаю ни один из этих пунктов. Что такое, например, разрешение для типов и экзмепляров?
Ладно, постараюсь разъяснить
1) Row-level security по таблице с явными разрешениями для типов и экземпляров.
Разрешение для типа — Могу работать со складами. Любыми, всеми, вообще.
Разрешение для экземпляра — Могу работать со складом 37.
Отдельно следует иметь возможность работать с обхектом и работать с объектом каким-то определённым способом.
Иногда, как в случае со складом, способы фиксированны: приход, расход, списание. Иногда список операций над объектом на которые надо раздавать права меняется пользователем. Например в IT склад можно добавить объект "картриджи", добавить операцию "заправка картриджа" и потом указывать кто и что может делать с картриджем.
2) Тоже самое, но с учётом разрешений на объекты ссылаемые foreign key.
Могу работать только со складом 37. Эта транзакция списывает товар со склада 42 и я её не вижу. Эта принимает товар на склад 37 и я её вижу.
3) Тоже самое, но с учётом попадания в период времени, окно со скользящими или прыгающими началом и концом.
Не могу оформлять перемещения товара задним числом (скользящее окно)
Не могу редактировать перемещения товара не за текущую неделю (прыгающее окно)
Здравствуйте, adontz, Вы писали:
IT>> Я не понимаю ни один из этих пунктов. Что такое, например, разрешение для типов и экзмепляров? A>Ладно, постараюсь разъяснить
Это всё фильтрация. Мы уже даже почти определились как её лучше сделать.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
IT>>> Я не понимаю ни один из этих пунктов. Что такое, например, разрешение для типов и экзмепляров? A>>Ладно, постараюсь разъяснить IT>Это всё фильтрация. Мы уже даже почти определились как её лучше сделать.
Ну мой подход в том чтобы с сервера были видны все списки объектов (какой смысл кромсать модель?), но некоторые были пустыми, если нет доступа. Задашь более конкретный вопрос, получишь более конкретный ответ.
Здравствуйте, IT, Вы писали:
IT>Запустить пример в одном процессе можно примерно так:
IT>
IT>using (var host = new ServiceHost(new LinqService(), new Uri("net.tcp://localhost:1234")))
IT>{
.....
IT> var list = ctx.GetTable<Person>().ToList();
........
IT>}
IT>
Что делать в случае, если таблица Person большая ? А тем не менее хотелось бы выбрать всю таблицу ? Может ли помочь изменение " MaxStringContentLength property on the XmlDictionaryReaderQuotas " и как до него добраться ?
У меня вываливается исключение
System.ServiceModel.Dispatcher.NetDispatcherFaultException was unhandled
Message=The formatter threw an exception while trying to deserialize the message: There was an error while trying to deserialize parameter http://tempuri.org/:ExecuteReaderResult. The InnerException message was 'There was an error deserializing the object of type BLToolkit.ServiceModel.LinqServiceResult. The maximum string content length quota (8192) has been exceeded while reading XML data. This quota may be increased by changing the MaxStringContentLength property on the XmlDictionaryReaderQuotas object used when creating the XML reader.'. Please see InnerException for more details.
Source=mscorlib
Action=http://schemas.microsoft.com/net/2005/12/windowscommunicationfoundation/dispatcher/fault
StackTrace:
Server stack trace:
at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.DeserializeParameterPart(XmlDictionaryReader reader, PartInfo part, Boolean isRequest)
at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.DeserializeParameter(XmlDictionaryReader reader, PartInfo part, Boolean isRequest)
at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.DeserializeBody(XmlDictionaryReader reader, MessageVersion version, String action, MessageDescription messageDescription, Object[] parameters, Boolean isRequest)
at System.ServiceModel.Dispatcher.OperationFormatter.DeserializeBodyContents(Message message, Object[] parameters, Boolean isRequest)
at System.ServiceModel.Dispatcher.OperationFormatter.DeserializeReply(Message message, Object[] parameters)
at System.ServiceModel.Dispatcher.ProxyOperationRuntime.AfterReply(ProxyRpc& rpc)
at System.ServiceModel.Channels.ServiceChannel.HandleReply(ProxyOperationRuntime operation, ProxyRpc& rpc)
at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)
Exception rethrown at [0]:
at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
at BLToolkit.ServiceModel.ILinqService.ExecuteReader(LinqServiceQuery query)
at BLToolkit.ServiceModel.LinqServiceClient.ExecuteReader(LinqServiceQuery query) in d:\GeneratedCode\9\Test\BLToolkit\ServiceModel\LinqServiceClient.cs:line 43
at BLToolkit.ServiceModel.ServiceModelDataContext.BLToolkit.Data.Linq.IDataContext.ExecuteReader(Object query) in d:\GeneratedCode\9\Test\BLToolkit\ServiceModel\ServiceModelDataContext.cs:line 210
at BLToolkit.Data.Linq.Query`1.<RunQuery>d__2a.MoveNext() in d:\GeneratedCode\9\Test\BLToolkit\Data\Linq\Query.cs:line 367
at BLToolkit.Data.Linq.Query`1.<Map>d__32.MoveNext() in d:\GeneratedCode\9\Test\BLToolkit\Data\Linq\Query.cs:line 383
at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
at Program.Main() in D:\GeneratedCode\9\Test\Test\Program.cs:line 106
at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
InnerException: System.Runtime.Serialization.SerializationException
Message=There was an error deserializing the object of type BLToolkit.ServiceModel.LinqServiceResult. The maximum string content length quota (8192) has been exceeded while reading XML data. This quota may be increased by changing the MaxStringContentLength property on the XmlDictionaryReaderQuotas object used when creating the XML reader.
Source=System.Runtime.Serialization
StackTrace:
at System.Runtime.Serialization.XmlObjectSerializer.ReadObjectHandleExceptions(XmlReaderDelegator reader, Boolean verifyObjectName, DataContractResolver dataContractResolver)
at System.Runtime.Serialization.DataContractSerializer.ReadObject(XmlDictionaryReader reader, Boolean verifyObjectName)
at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.DeserializeParameterPart(XmlDictionaryReader reader, PartInfo part, Boolean isRequest)
InnerException: System.Xml.XmlException
Message=The maximum string content length quota (8192) has been exceeded while reading XML data. This quota may be increased by changing the MaxStringContentLength property on the XmlDictionaryReaderQuotas object used when creating the XML reader.
Source=System.Runtime.Serialization
LineNumber=0
LinePosition=0
StackTrace:
at System.Xml.XmlExceptionHelper.ThrowXmlException(XmlDictionaryReader reader, String res, String arg1, String arg2, String arg3)
at System.Xml.XmlExceptionHelper.ThrowMaxStringContentLengthExceeded(XmlDictionaryReader reader, Int32 maxStringContentLength)
at System.Xml.XmlDictionaryReader.ReadContentAsString(Int32 maxStringContentLength)
at System.Xml.XmlBaseReader.ReadContentAsString()
at System.Xml.XmlBaseReader.ReadElementContentAsString()
at System.Xml.XmlBinaryReader.ReadElementContentAsString()
at System.Runtime.Serialization.XmlReaderDelegator.ReadElementContentAsString()
at ReadLinqServiceResultFromXml(XmlReaderDelegator , XmlObjectSerializerReadContext , XmlDictionaryString[] , XmlDictionaryString[] )
at System.Runtime.Serialization.ClassDataContract.ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context)
at System.Runtime.Serialization.XmlObjectSerializerReadContext.ReadDataContractValue(DataContract dataContract, XmlReaderDelegator reader)
at System.Runtime.Serialization.XmlObjectSerializerReadContext.InternalDeserialize(XmlReaderDelegator reader, String name, String ns, Type declaredType, DataContract& dataContract)
at System.Runtime.Serialization.XmlObjectSerializerReadContext.InternalDeserialize(XmlReaderDelegator xmlReader, Type declaredType, DataContract dataContract, String name, String ns)
at System.Runtime.Serialization.DataContractSerializer.InternalReadObject(XmlReaderDelegator xmlReader, Boolean verifyObjectName, DataContractResolver dataContractResolver)
at System.Runtime.Serialization.XmlObjectSerializer.ReadObjectHandleExceptions(XmlReaderDelegator reader, Boolean verifyObjectName, DataContractResolver dataContractResolver)
InnerException:
Здравствуйте, WaSh, Вы писали:
WS>Что делать в случае, если таблица Person большая ? А тем не менее хотелось бы выбрать всю таблицу ? Может ли помочь изменение " MaxStringContentLength property on the XmlDictionaryReaderQuotas " и как до него добраться ?
Помочь может. Как поменять — подробно описано в MSDN. Вкратце — или в конфиге ендпоинта, или в соотв. элементах описания биндинга при программном конфигурировании. Поиск в MSDN по MaxStringContentLength должен помочь.
... << RSDN@Home 1.2.0 alpha 4 rev. 1490 on Windows 7 6.1.7600.0>>
Скачал последнюю версию.
Все запросы Linq Over WCF не работают, отваливаются по таймауту.
До этого использовалась версия 4.0, все работало.
Сейчас не работает даже этот пример, см. ниже.
В чем подвох и как лечить?
using (var host = new ServiceHost(new LinqService(), new Uri("net.tcp://localhost:1234")))
{
host.Description.Behaviors.Add(new ServiceMetadataBehavior());
host.Description.Behaviors.Find<ServiceDebugBehavior>().IncludeExceptionDetailInFaults = true;
host.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexTcpBinding(), "mex");
host.AddServiceEndpoint(
typeof(ILinqService),
new NetTcpBinding(SecurityMode.None)
{
MaxReceivedMessageSize = 10000000,
MaxBufferPoolSize = 10000000,
MaxBufferSize = 10000000,
CloseTimeout = new TimeSpan(00, 01, 00),
OpenTimeout = new TimeSpan(00, 01, 00),
ReceiveTimeout = new TimeSpan(00, 10, 00),
SendTimeout = new TimeSpan(00, 10, 00),
},
"LinqOverWCF");
host.Open();
var ctx = new ServiceModelDataContext(
new NetTcpBinding(SecurityMode.None)
{
MaxReceivedMessageSize = 10000000,
MaxBufferPoolSize = 10000000,
MaxBufferSize = 10000000,
CloseTimeout = new TimeSpan(00, 01, 00),
OpenTimeout = new TimeSpan(00, 01, 00),
ReceiveTimeout = new TimeSpan(00, 10, 00),
SendTimeout = new TimeSpan(00, 10, 00),
},
new EndpointAddress("net.tcp://localhost:1234/LinqOverWCF"));
var list = ctx.GetTable<Person>().ToList();
host.Close();
return list;
}