Hi all.
[Version:
VB Csla 2.1]
This is
basically more a question of class design then Csla. Anyway I would like to ask
if someone has figured out a ‘best practice’ using the Csla Framework.
In Brief:
Certain
Properties (all Strings) of a BO must be available in different languages as I
cannot foresee how much languages are handled by the users of my application I will
not offer the BO an individual property in all languages. So I intend to spend
the BO a List (Collection) of child objects containing exactly the same Data,
but in the individual languages required. For instance a BO ‘Product’ with a ‘product
description’ in three languages (English, German, French). I do look for a good design
to provide consumers of this ProductBO access to the descriptions in the
individual varying languages.
Details:
I do have a
BO ‘Product’ with the following properties
BO: Product
(Inherits BusinessBase)
==============================
Public
ReadOnly Property ID as Guid
Public
Property ProductGroup as Integer
Public
Property Description as String
Within the
application the Product.Description is processed in different languages
(typically English, German, French), however I would like to have the option to
add more languages in future. Therefore I abstained from adding additional
Properties for each Language (e.g. DescriptionInEnglish, DescriptionInGerman…) in
the Product BO, but I did add a ‘Languages’ property of type LanguageList
inherited from BusinessListBase.
This
ChildBOs of LanguageList are ProductLanguageInfos with the following Properties
BO: ProductLanguageInfo
(Inherits BusinessBase)
====================================
Public
ReadOnly Property ProductID as Guid
(Foreign Key from BO ‘Product’)
Public
Property LanguageID as Integer
Public
Property Description as String
The Product
BO gets an additional Property…
BO :
Product (Inherits BusinessBase)
==============================
Public
ReadOnly Property ID as Guid
Public
Property ProductGroup as Integer
Public
Property Description as String
Public
Property Languages as LanguageList
The UI
currently processes 3 Languages English, German, and French.
The
question is how to access the Description in the individual language,
considering that the user should edit the Description in all languages together
in one form/user control
The GUI
shows 3 Textboxes:
Description
(EN)
Description
(DE)
Description
(FR)
My idea is
to provide an overloaded parameterized Description property in the Product BO…
BO :
Product (Inherits BusinessBase)
==============================
Public
ReadOnly Property ID as Guid
Public
Property ProductGroup as Integer
Public
Property Description as String
Public
Property Description (LanguageID as
Integer) as String
Public
Property Languages as LanguageList
The assessors
of this overloaded Description property interact with the ProductLanguageInfo
objects of the LaguagesList to get and set the Description.
Each
Textbox is bound to a corresponding Description.
My Questions:
Is that a
‘legal’ and/or recommended design?
Does
somebody have figured out a better design?
As the
ProductLanguageList / ProductLanguageInfo are only handled together with the
Product BO, does it make sense to nest those classes in the Product BO?
As my
application has this ‘language requirements’ for certain properties in quite a
lot of different BOs I wonder if there is a easier design pattern available (to
avoid to code 3 classes for each BO (BO, LanguageList, LanguageInfo).
IMO, you're on the right track. However, a couple of key things to note...
Consider placing your Product Descriptions in a separate table in your DB with a foreign key reference to the Product and a column for the language (which you may already be doing). The language identifier should be the standard used by .NET (e.g. en, en-US, fr and so on).
Then, when you instantiate your business object, Product, have your data access code pass the current language identifier to the stored procedure. Your stored procedure can perform a join on the Products and ProductDescriptions table and use the language identifier to filter the rows in ProductDescriptions to only the one corresponding to the current language.
For your UI, you can still accomodate this by having a drop-down list with supported languages and only a single textbox for the Description populated with the appropriate text based on the language selected in the drop-down box. This would allow you to unify/tie-in any other fields that you may need multi-lingual support for such as the product's name or pricing information (which you may want to localize).
Using this approach, you only have one business object, Product, with the necessary logic to handle multiple languages.
Another approach you may want to consider if the description is the only multi-lingual field you are dealing with is to have your Product object define an internal (private) dictionary of descriptions keyed on the language. Then, you can load them all into the Product and in your description property accessor, select the item from the collection that matches the language. So, you would have code such as:
public System.String Description
{
get
{
return _descriptions[System.Globalization.CultureInfo.CurrentUICulture.Name];
}
}
This approach works well if you only have a small number of multi-lingual fields because you don't have to incur a round-trip to the DB and instantiate a new BO each time you change languages. On the other hand, if you have a lot of multi-lingual fields and are not frequently switching languages on-the-fly, there is quite a bit more overhead loading all of the lists with this approach versus the first where you simply return the appropriate string right from the DB.
You can still maintain a list & lookup table of supported languages to help drive your UI. Then, when a new Product is created, you either insert a (blank?) record into the ProductDescriptions table for each supported language or build-in some logic to traverse the culture chain to a default in the event the string for the selected language is not present. For instance:
public System.String Description
{
get
{
if (_descriptions.Contains[System.Globalization.CultureInfo.CurrentUICulture.Name)
return _descriptions[System.Globalization.CultureInfo.CurrentUICulture.Name];
else if (_descriptions.Contains[System.Globalization.CultureInfo.CurrentUICulture.TwoLetterISOLanguageName)
return _descriptions[System.Globalization.CultureInfo.CurrentUICulture.TwoLetterISOLanguageName];
else
return _descriptions["en"];
}
}
(Just an example).
Hope that helps.
I agree with Andy. You've got two use cases that call for two different approaches. Since you want to be able to edit all of the languages on the same UI, you will want a BO with a child collection containing the descriptions in all of the languages. But, your OrderPrinterBO should only have a Description property and use the language to select the right string from the database when retrieving the information.
I would also agree that a grid is a better UI for your situation - especially since you want to be able to add more languages in the future. Databinding the grid to the collection will allow you to add them easily without having to make changes to your UI as you would if you tried to have a separate textbox for each (unless you created them dynamically in a loop during rendering - ouch!).
Hope that helps.
I have had a similary dream. I hope the CustomTypeProvider is also useful for your problem.
In short.
If you use the CustomTypeProvider you can craft at run time your own pseudo -properties that grap each of your object fields or fields embedded in lists, thus allowing those information to be displayed and edited with "normal " Databinding.
Please visit the following link for details :
Sounds promising!
I will go
through this article a.s.a.p. Anyway if you have already used this approach to follow
up a similarly idea like mine (exposing properties of the items in a child
collection to the root BO which contains this collection) I would very much
appreciate if you could share the code with me.
Thanks
Frank
Dear Frank,
when I will be back im my home office at the weekend I will look for an example and share it with you.
Josef Eißing
Germany
Copyright (c) Marimer LLC