Oswajanie Fluent NHibernate
O FluentNHibernate napisano już całkiem sporo, jednak niestety duża część z informacji krążących po Sieci jest już nieaktualna z powodu zmian w API. Postanowiłem więc podzielić się z Wami wnioskami z moich wczorajszych zmagań z FNH. Zanim jednak przejdę do konkretów, jeśli ktoś nigdy nie używał tej biblioteki, prawdopodobnie powinien zacząć o tych postów Procenta.
Enumy
Jak Procent zauważył, mapowanie enumów za pomocą właściwego im typu całkowitoliczbowego realizuje się za pomocą klauzuli CustomType:
Map(x => x.Gender).CustomType<Gender>();
Jako leniwy programista chciałbym jednak, aby FluentNHibernate zrobił to za mnie. Nauka FNH odbywa się za pomocą konwencji. Jedyne, co należy stworzyć to własną konwencję mapowania typów enum. Oto ona:
public class EnumAsIntConvention : IUserTypeConvention
{
public void Accept(IAcceptanceCriteria<IPropertyInspector> criteria)
{
criteria.Expect(x => x.Property.PropertyType.IsEnum);
}
public void Apply(IPropertyInstance target)
{
target.CustomType(target.Property.PropertyType);
}
}
Nie muszę chyba tłumaczyć jak działa, ponieważ wszystko widać na pierwszy rzut oka. Klasę tą znalazłem gdzieś na StackOverflow.
varchar a nvarchar
Nie zamierzam się wdawać w filozoficzne dywagacje na temat przewagi jednego nad drugim (ani drugiego nad pierwszym). Po prostu chciałem, aby moje napisy były przechowywane w bazie jako varchar. W Sieci znalazłem wiele rozwiązań, ale żadne mnie nie satysfakcjonowało, ponieważ nie można go było zautomatyzować za pomocą konwencji. Wtedy, przeglądając listę typów wspieranych przez NHibernate natrafiłem na AnsiString. Pomyślałem, że spróbuję. Tak zrodziła się konwencja
public class StringAsVarcharConvention : IUserTypeConvention
{
public void Accept(IAcceptanceCriteria<IPropertyInspector> criteria)
{
criteria.Expect(x => x.Property.PropertyType == typeof(string));
}
public void Apply(IPropertyInstance target)
{
target.CustomType("AnsiString");
}
}
która uczy FNH, aby wszystkie napisy mapował do kolumn varchar o odpowiedniej długości.
varchar(max)
A propos długości, pewnie wiecie, ale na wszelki wypadek: zarówno przy użyciu klasycznych plików hbm.xml, jak i płynnych mapowań, aby wymusić na NHibernate zastosowanie typu varchar(max) (i analogicznego — varbinary(max)) należy podać długość pola większą niż 8000. Ja stosuje 8001 wraz z odpowiednim komentarzem.
Decimal
Kolejna kwestia to standaryzacja reprezentacji kwot w projekcie. W moim wypadku zdecydowaliśmy się na zastosowanie decimal(18,2) na poziomie bazy danych. Poniższa konwencja uczy FluentNHibernate respektowania naszej decyzji:
public class DecimalConvention : IUserTypeConvention
{
public void Accept(IAcceptanceCriteria<IPropertyInspector> criteria)
{
criteria.Expect(x => x.Property.PropertyType == typeof(decimal));
}
public void Apply(IPropertyInstance target)
{
target.Scale(2);
target.Precision(18);
}
}
Stereotypy
W przypadku inżynierii oprogramowania są niewątpliwie dobrym zjawiskiem. Jeśli 90% moich pól tekstowych reprezentuje jedną z dwóch kategorii, mogę pokusić się o zdefiniowanie, za pomocą metod rozszerzających, odpowiednich stereotypów.
Map(x => x.SendersReference).AsReference(); Map(x => x.CustomerSpecifiedReference).AsReference(); Map(x => x.BeneficiaryInformation).AsTextualInfo(); Map(x => x.RemittanceInformation).AsTextualInfo();
Ich implementacja wygląda następująco:
public static PropertyPart AsReference(this PropertyPart propertyPart)
{
return propertyPart.Length(16).Not.Nullable();
}
public static PropertyPart AsTextualInfo(this PropertyPart propertyPart)
{
return propertyPart.Length(144).Not.Nullable();
}
Dodatkową korzyścią ze stosowania takich rozszerzeń jest fakt, że nadajemy naszemu mapowaniu sens biznesowy. Zarówno Reference, jak i TextualInfo są pojęciami pochodzącymi z domeny problemu, które są elementem naszego wszędobylskiego języka (ubiquitous language).
Z drugiej (technicznej) strony, na podobnej zasadzie można stworzyć rozszerzenie AsVarcharMax(), które przypisze tę nieszczęsną długość 8001 i ukryje ten kod w jednym miejscu.
Konwencje, konwencje
W żadnym razie nie narzucam Wam moich konwencji. Jeśli Wam odpowiadają, bierzcie i stosujcie. Jeśli nie — stwórzcie własne. Miejcie jednak jakieś. Naprawdę, warto mieć konwencje.




about 4 months ago
Podoba mi się pomysł ze stereotypami, cool.