webForumDet fria alternativet

Pattern för språk- och användarstöd

.NET

9 svar · 642 visningar · startad av emission

Medlem sedan dec. 19996 721 inlägg
Frågan#1

Hej vänner!

Jag arbetar just nu med ett större webbprojekt där vi ämnar att gå över till en O/R-mappad domänmodell, från att tidigare ha använt ett helt eget datalager. Två snarlika problem har uppstått.

1. Ett flertal av entiteterna (som är read-only) har språkstöd, dvs. en eller flera av sträng-egenskaperna på objekten kan variera beroende på användarsessionens valda språk. (productsView är en påhittad vy)

"SELECT ...... FROM productsView WHERE ...... AND langId=@langId"

2. Ett flertal (dock fåtal) av entiteterna har användarrättigheter.

"SELECT ...... FROM productsView WHERE ...... AND userId=@userId"

Både langId och userId är väldigt enkla att injicera i det nuvarande datalagrets SQL, genom att hämta dem från den abstrakta sessionen, men de känns inte alls lika självklara som delar i själva domänmodellen, och därmed är de ett problem för en mapper.

Vi kan tänkas ha en Order som innehåller flera Products. Order är varken språk- eller användarberoende, och kan därigenom inte tillhandahålla tillräckligt med information för att hämta upp rätt Products till sin Order.Products-collection. Den saknade informationen finns ju i sessionen. Förvisso är det egentligen korrekt, ur ett domänperspektiv, att Order.Products inte varierar med sessionen, utan att man istället ersätter denna property med en GetProducts(userId,langId)-metod, men samtidigt kan det bli lite trist om man vill åka snålskjuts på en cascade-update med t.ex. NHibernate.

Har ni haft något liknande modellproblem och hur har ni i så fall löst det?

Red: I det fiktiva exemplet är förhållandet mellan Products och userId M:N.

Medlem sedan dec. 19996 522 inlägg
#2

Vi kör i ett rätt stort projekt nhibernate och enterprise library. Lite mer javastuk på själva upplägget då vi använder DTO:er i presentationen och mellan webservices, vi har en LocalizationManager som fyller på DTO:erna med rätt språk som vi hämtar ut användarens Identity som ligger på tråden. Översättningen i LocalizationManager ordnas med EL/Generics/XML-filer. Tyckte inte heller språk hade något att göra med domänmodellen.

/Red missade användarproblematiken, vi har något liknande ska bara lyckas forumlera det som ett pattern ;)

Medlem sedan dec. 19996 721 inlägg
#3

Okej, ni använder DTO:er, vilket jag kan förstå, med tanke på att det finns en webservice med i bilden.

databas->mapper->domänobjekt->dto->webservice->konsument

..antar jag.

I vårt fall har vi inga DTO:er, men det inte omöjligt att vi kan gå en liknande väg, även om det mer blir DPO:er (Data Presentation Object) :)

Hur ser användningen av er LocalizationManager ut?

I ett annat projekt har jag byggt "lokalisering" mha av en interceptor-klass som dynamiskt översätter alla strängproperties. Det går dock inget vidare att använda en sådan metod med en mapper, eftersom de oftast tar över instantieringen och dessutom använder egna interceptorer.

Användarproblematiken är ju egentligen inte så snarlik, men den påminner, eftersom jag inte vill ha in det i domänen.

Medlem sedan dec. 19996 522 inlägg
#4

Ska kika igenom vår arkitektur lite mer på torsdag, ska flytta hela dagen i morgon så jag återkommer på torsdag :) Var ett par veckor sedan jag vara nere i de lagren i den applikationen så jag måste dubbelkolla ett par saker. Just nu blev jag osäker på vad lokaliseringen görs, det är efter vårt transformeringslager som omvandlar dto->domän och vice versa. Vi kör dtoerna både i aspx och mot webservices->konsument.

Ill be back!

Medlem sedan dec. 19996 721 inlägg
#5

Det ser jag fram emot!

Tillsvidare har jag löst det i NHibernate genom en IInterceptor som översätter vid OnLoad. Mycket funktionellt, men väldigt knutet till att en dylik interceptor-logik existerar i mappern. Problemet med språk är därmed löst, men det är ändå intressant att diskutera vidare kring en generell metod för att lösa detta.

Medlem sedan dec. 19996 522 inlägg
#6

Språkstöd är ju ofta ett mindre helsingland. När det gäller sidöversättningar och dylikt brukar vi köra på att vi laddar en collection med översättningar till sidan, loopar igenom kontrollerna och sätter texterna. På tal om språkhantering :)

När det gäller översättningen på domänobjekten har vi löst det ungefär som nedan

DOMAIN
DAL / IProductDao Interface
DAL / NHibernate
DAL / Localiszation
SERVICE INTERFACE / ProductService, DTOs

SERVICE INTERFACE

public static class ProductService
{
   private static IDaoFactory CreateDaoFactory()
   {
      return new LocalizationDaoFactory();
   }

   public static IList<ProductDto> GetProducts()
   {
      using (IProductDao dao = CreateDaoFactory().CreateProductDao())
      {
         return dao.GetProducts();
      }
    }
}

DAL NAMESPACES

public interface IProductDao : IDisposable
{
   IList<ProductDto> GetProducts();
}

public class HibernateProductDao : HibernateDaoBase<ProductDto, Guid>, IProductDao  
{
   public HibernateProductDao()
   : base(typeof(Product))
   {}

   public HibernateProductDao(ISession currentSession)
   : base(typeof(Product), currentSession)
   {}

   public IList<ProductDto> GetProducts()
   {		
      //Code to return all products
      //Here we use a Generic Transformation Method or a specific transformation metod, in this example
     //a specific. Purpose, transform domain object to DTO or reverse
     //Exists in Tranformation namespace in the DAL
    
     return ProductTransformation.TransformToProductDto(query.list());
    }
}

public class LocalizationProductDao : IProductDao
{
   private IProductDao m_concreteDao;
 
   internal LocalizationProductDao(IProductDao dao)
   {
     m_concreteDao = dao;
   }

   public IList<ProducteDto> GetProducts()
   {
      IList<ProductDto> list = m_concreteDao.GetProducts();
      //Here we use a Generic Translation Method or a specific translation method
      //in this example a specific
      LocalizationTranslateHelper.TranslateProducts(list);
      return list;
   }
}

Med reservation för eventuella missar och felstavningar :) De är ju uppdelade i egna namespaces sedan, ja du fattar :)

Vidare är jag nyfiken på hur ni löst validering av domänobjekten?

Medlem sedan dec. 19996 522 inlägg
#7

Inga kommentarer, vart försvann du emission ;)

Medlem sedan dec. 19996 721 inlägg
#8

Blev lite upptagen med synthfestarrangemang..:)

Tack för ditt exempel! Det var ett bra upplägg, som jag mycket väl kan tänka mig att använda. Det kan bli rätt mycket kod, men det är ändå hyfsat transparent att överlåta jobbet till en Dao-wrapper och Dao-fabriken. Så länge inte valet av språk kan ske mitt i en session så är det ett fungerande pattern.

Vad beträffar validering så har vi oftast klarat oss med en enkel IValidatable-metod, där domänobjekten själva bistår med valideringslogiken. Jag har har labbat en del med attributbaserad validering (deklarativ) och externa valideringsklasser, men landar ändå oftast på IValidatable.

Medlem sedan dec. 19996 522 inlägg
#9

Synhfest, då förstår jag du vart upptagen :) Nej inga patterns är perfekta men jag tycker det fungerar bra och är tillräckligt transparant.

Har också använt validering direkt på domänobjekten men blir problem då jag gärna vill validera data även direkt under GUIt, innan det skickas ner i lagren för att mindska trafik, samt att jag personligen vill validera även där. Testat om man ska ha ett eget valideringsprojekt där man har ett generellt interface och valideringsklasser för varje objekt, eller attributbaserad validering på dto:erna och validering direkt på dem, för de är ju egentligen de som ska valideras. Men inget känns optimalt. Inför nästa projekt ska jag ta mig en rejäl funderare.

Medlem sedan dec. 19996 721 inlägg
#10

Attributbaserad validering är smidigt, men fungerar bara riktigt bra för värdevalidering (stränglängd, regexp-pattern etc.), vilket i och för sig blir väldigt smidigt om man vill automatgenerera t.ex. ASP.NET-validatorer.

255 ms totalt · 4 externa anrop · v20260731065814-full.a51de22e
120 ms — deklarationer (db)
0 ms — hämta statistik (cache)
132 ms — hämta tråd, inlägg och bilagor (db)
121 ms — ändringar (db)