9 октября был выпущен NHibernate 3 beta 1. Решил посмотреть что в нем нового. Для этого быстренько набросал модель:
Для маппинга я буду использовать ConfOrm. Его настройки:
public class DomainMapper { public HbmMapping GenerateMappigs() { IEnumerable<Type> domainEntities = this.GetDomainEntities(); var relationalMapper = new ObjectRelationalMapper(); relationalMapper.TablePerConcreteClass(domainEntities); relationalMapper.Patterns.PoidStrategies.Add(new NativePoidPattern()); relationalMapper.Cascade<Category, Product>(Cascade.Persist); relationalMapper.ManyToMany<Category, Product>(); relationalMapper.Cascade<Order, Product>(Cascade.Persist); var mapper = new Mapper(relationalMapper); mapper.PatternsAppliers.RootClass.Add(new TableNamesApplier()); mapper.Class<User>(x => x.Property(y => y.VeryLongProperty, map => map.Lazy(true))); HbmMapping mapping = mapper.CompileMappingFor(domainEntities); return mapping; } /// <summary> /// Gets all objects that are inherited from <see cref="BaseEntity"/>. /// </summary> private IEnumerable<Type> GetDomainEntities() { Assembly domainAssembly = typeof(BaseEntity).Assembly; IEnumerable<Type> domainEntities = from t in domainAssembly.GetTypes() where t.BaseType == typeof(BaseEntity) && !t.IsGenericType select t; return domainEntities; } }
В предыдущих постах говорили что стоит добавлять и схему базы данных. Скажу честно, я её не создавал, а попросил это сделать NHibernate. Поэтому я не буду её тут приводить
Чтобы сгенерировать базу данных можно использовать SchemaExport:
new SchemaExport(cfg).Execute(true, true, false);
Дальше по порядку:
1. И конечно самое ожидаемое – Linq Provider
Чтобы попробовать его я решил попробовать запросы, которе прошлый провайдер (основанный на ICriteria) не мог выполнить.
-
(from u in session.Query<User>() where u.Orders.Count > 5 select u).Count()
Выполняет следующий sql:select cast(count(*) as INT) as col_0_0_ from Users user0_ where ( select cast(count(*) as INT) from Orders orders1_ where user0_.Id=orders1_.OrderUser )>@p0; @p0 = 5
- Интересно было так же посмотреть как выбираются анонимные объекты, запрос:
var firstName = (from u in session.Query<User>() select new { u.FirstName }).FirstOrDefault();
Выполняетselect TOP (@p0) user0_.FirstName as col_0_0_ from Users user0_; @p0 = 1 [Type: Int32 (0)]
Я думаю комментарии тут не нужны, тем более если еще вспомнить с каким количеством баз данных умеет работать NHibernate и насколько это круто - Но вот такой запрос уже выполнить не получилось:
var rows = (from u in session.Query<User>() let order = u.Orders.FirstOrDefault() where u.Orders.Count > 5 && order != null && order.Id == 4 select u).ToList();
2. Fluent синтаксис для конфигурации SessionFactory
Для этого примера мне потребовалась следующая кофигурация:
DomainMapper mapper = new DomainMapper(); HbmMapping generatedMappigs = mapper.GenerateMappigs(); var cfg = new Configuration(); cfg.SessionFactory() .Proxy.Through<ProxyFactoryFactory>() .Integrate .Using<MsSql2008Dialect>() .AutoQuoteKeywords() .Connected .By<SqlClientDriver>() .ByAppConfing("connectionString") .CreateCommands .ConvertingExceptionsThrough<SQLStateConverter>(); cfg.SetProperty("show_sql", "true"); // I haven't found how to configure them cfg.SetProperty("format_sql", "true"); cfg.AddDeserializedMapping(generatedMappigs, "WhatsNew");
чтобы использовать такой синтаксис необходимо подключить неймспейс NHibernate.Cfg.Loquacious. Конечно название они выбрали... Но дословно гугл переводит как "словоохотливый". Может оно и правильно, но я раньше нигде не встречал. Наиболее полный пример всех настроек я смог найти у Fabio Maulo.
Так же конфигурировать можно с помощью лямбда выражений. Но в примере я их не использовал. Подробнее можно почитать тут.
3. QueryOver синтаксис
Это API позволяет упросить написание ICriteria запросов. Во первых больше нет необходимости использовать строки (раньше это решалось с помощью библиотеки NHLambdaExtensions), во вторых теперь возможны сложные условия внутри одного вызова через &&, ||, в третьих упрощена работа с алиасами (можете посмотреть насколько это было не просто в предыдущей версии), в четвертых работа с проекциями теперь гораздо более читабельна, и в пятых мне наверно стоило сделать из этого абзаца n-тых список c номерами .
Приведу простой примерчик как теперь можно делать запросы:
var query = session.QueryOver<User>() .Select(x => x.FirstName, x => x.LastName) .WhereRestrictionOn(x => x.FirstName).IsLike("first", MatchMode.Anywhere) .WhereRestrictionOn(x => x.LastName).IsLike("last") .List<object[]>() .Select(properties => new { FirstName = properties[0], LastName = properties[1] });
Это выполнит запрос:
SELECT this_.FirstName as y0_, this_.LastName as y1_ FROM Users this_ WHERE this_.FirstName like @p0 and this_.LastName like @p1; @p0 = '%first%' [Type: String (4000)], @p1 = 'last' [Type: String (4000)]
Ну и обращаться к результатам можно как и к обычным анонимным объектам:
foreach (var user in query) { Console.WriteLine(user.FirstName); }
Хорошая документация о том как использовать этот синтаксис можно найти тут.
4. Lazy Property
Не знаю почему эта фича была реализована только сейчас, но теперь она работает, так если у сущности есть какое то поле, которое не хотелось бы загружать каждый раз, можно пометить его как Lazy. Но с этой штукой надо быть осторожно, потому как если из базы данных достается список таких объектов, то вероятнее всего весь этот список будет перебираться (иначе зачем его было доставать) это может стать причиной Select N+1. Ну демонстрация очень простая :
var user = session.Get<User>(1); string veryLongProperty = user.VeryLongProperty;
И запросы:
/* 1 */ SELECT user0_.Id as Id4_0_, user0_.FirstName as FirstName4_0_, user0_.LastName as LastName4_0_ FROM Users user0_ WHERE user0_.Id=@p0; @p0 = 1 [Type: Int32 (0)] /* 2 */ SELECT user_.VeryLongProperty as VeryLong4_4_ FROM Users user_ WHERE user_.Id=@p0; @p0 = 1 [Type: Int32 (0)]
Примечательно то, что благодаря этой фиче смогли реализовать Lazy Load one-to-one связи которой раньше не было.
5. Нет зависимости от log4net
Последняя версия log4net вышла в 2003 году… Видимо поэтому многие сейчас отказываются от него. В общем теперь вам нет необходимости таскать log4net за собой, его даже нет в архиве со всеми файлами NHibernate. Вот пример как настраивать для работы с другими логгерами.
6. Остальное
В releasenotes еще длиннющий список повакшенных багов и некоторых импрувментов. Но для меня наверно они не так понятны потому как никогда на них не натыкался. Ну либо вот такие фичи:
- [NH-2309] - Add support for Future() with the new Linq provider
- [NH-626] - Adding XmlDoc to NH types
- [NH-2135] - Compatible with Mono
- [NH-2256] - Add support for user-provided extensions to the Linq provider
В общем прогресс проекта виден. Я боялся что EF задавит хибер за счет майкросовской рекламы, но видимо нет, слишком уж он хорош .
Почему не работает LINQ запрос с let? Не доделали провайдер? Как думаешь стоит нам заюзать в продакшне?
ReplyDeleteДумаю в продакшене юзать вполне стоит. Почему не работает... выкидывает какое то не понятное исключение даже не генерируя SQL. Хиберовские беты в общем то всегда были стабильны
ReplyDelete