Sebelumnya saya telah membuat contoh Custom ResourceManger dengan implementasi API Bing Translator pada post Multilingual ResourceManager dgn Auto Translate (Part I).
Kali ini, saya akan tune up agar performance lebih baik dan juga lebih mudah dimaintain. Adapun yg di Tune Up adalah sbb :
- Menggunakan database
ResourceManager akan menarik resource data cache terlebih dahulu, jika tidak ada menarik ke database, jika resource tidak ditemukan, maka secara automatis mentranslate dgn service Translator dan menyimpannya kedalam database dan di cache - Memudahkan Maintain
Dengan menggunakan database akan memudahkan kita memaintain data output translatenya, jika translatornya salah.
source code dapat didownload di sini
Ok, kita lanjut ...
Pertama2 membuat class untuk store database (dengan LinqToSql)
Code Snippet
- [Table(Name = "ResourceData")]
- public class ResourceData
- {
- [Column(Name = "ResourceID", CanBeNull = false, IsDbGenerated = true, IsPrimaryKey = true, AutoSync = AutoSync.OnInsert)]
- public long ResourceID { get; set; }
- [Column(Name = "ResourceType", DbType = "NVARCHAR(1000) NOT NULL", CanBeNull = false)]
- public string ResourceType { get; set; }
- [Column(Name = "ResourceKey", DbType = "NVARCHAR(MAX) NOT NULL", CanBeNull = false)]
- public string ResourceKey { get; set; }
- [Column(Name = "FromLanguageCode", DbType = "VARCHAR(20) NOT NULL", CanBeNull = false)]
- public string FromLanguageCode { get; set; }
- [Column(Name = "ToLanguageCode", DbType = "VARCHAR(20) NOT NULL", CanBeNull = false)]
- public string ToLanguageCode { get; set; }
- [Column(Name = "ResourceValue", DbType = "NVARCHAR(MAX) NOT NULL", CanBeNull = false)]
- public string ResourceValue { get; set; }
- }
Kemudian membuat DataContextnya (dengan default connectionStringName nya adalah "MyResourceManager")
Code Snippet
- public class ResourceDataContext : DataContext
- {
- private static string ConnectionString = ConfigurationManager.ConnectionStrings["MyResourceManager"].ConnectionString;
- private static AttributeMappingSource mappingSource = new AttributeMappingSource();
- public Table<ResourceData> Resources
- {
- get
- {
- return GetTable<ResourceData>();
- }
- }
- public ResourceDataContext()
- : base(ConnectionString, mappingSource)
- {
- }
- /// <summary>
- /// attribute [MethodImpl(MethodImplOptions.Synchronized)] memaksa agar fungsi ini menjadi thread-safe
- /// </summary>
- [MethodImpl(MethodImplOptions.Synchronized)]
- public ResourceData GetByResourceKey(string ResourceType, string ResourceKey, string FromLanguageCode, string ToLanguageCode)
- {
- var query = from r in Resources
- where r.ResourceType.Equals(ResourceType) && r.ResourceKey.Equals(ResourceKey) && r.FromLanguageCode.Equals(FromLanguageCode) && r.ToLanguageCode.Equals(ToLanguageCode)
- select r;
- var data = query.ToArray(); // pada sql 2005 query equal operator tidak case sensitive
- return data.FirstOrDefault(t => t.ResourceKey.Equals(ResourceKey)); // memaksa agar case sensitive
- }
- [MethodImpl(MethodImplOptions.Synchronized)]
- public ListDictionary GetByFromLanguageCodeToLanguageCode(string ResourceType, string FromLanguageCode, string ToLanguageCode)
- {
- var query = from r in Resources
- where r.ResourceType.Equals(ResourceType) && r.FromLanguageCode.Equals(FromLanguageCode) && r.ToLanguageCode.Equals(ToLanguageCode)
- select new { r.ResourceKey, r.ResourceValue };
- ListDictionary dic = new ListDictionary();
- foreach (var result in query)
- dic.Add(result.ResourceKey, result.ResourceValue);
- return dic;
- }
- }
Kemudian update coding dibawah ini pada class MyResourceProvider method GetObject
Code Snippet
- public object GetObject(string resourceKey, CultureInfo culture)
- {
- if (Disposed)
- throw new ObjectDisposedException("MyResourceProvider object is already disposed.");
- if (string.IsNullOrEmpty(resourceKey))
- throw new ArgumentNullException("resourceKey");
- if (culture == null)
- culture = CultureInfo.CurrentUICulture;
- if (string.IsNullOrEmpty(culture.Name))
- culture = System.Globalization.CultureInfo.CurrentUICulture;
- string resourceValue = null;
- Dictionary<string, string> resCacheByCulture = null;
- if (resourceCache.ContainsKey(culture.Name))
- {
- resCacheByCulture = resourceCache[culture.Name];
- if (resCacheByCulture.ContainsKey(resourceKey))
- resourceValue = resCacheByCulture[resourceKey];
- }
- if (resourceValue == null)
- {
- using (ResourceDataContext ctxt = new ResourceDataContext())
- {
- // Create Database jika tidak ada
- if (!ctxt.DatabaseExists())
- ctxt.CreateDatabase();
- string fromLanguageCode = resourceKeyLanguage.GetLanguageType().GetLanguageCode();
- string toLanguageCode = culture.GetLanguageType().GetLanguageCode();
- // Tarik data Resource
- ResourceData data = ctxt.GetByResourceKey(classKey, resourceKey, fromLanguageCode, toLanguageCode);
- if (data == null)
- {
- // jika data tidak ada, menarik service API
- resourceValue = Utils.Translate(resourceKey, resourceKeyLanguage.GetLanguageType(), culture.GetLanguageType());
- data = new ResourceData();
- data.ResourceType = classKey;
- data.ResourceKey = resourceKey;
- data.FromLanguageCode = fromLanguageCode;
- data.ToLanguageCode = toLanguageCode;
- data.ResourceValue = resourceValue;
- // Insert data
- ctxt.Resources.InsertOnSubmit(data);
- ctxt.SubmitChanges();
- }
- else
- resourceValue = data.ResourceValue;
- }
- // add this result to the cache
- // find the dictionary for this culture
- // add this key/value pair to the inner dictionary
- lock (this)
- {
- if (resCacheByCulture == null)
- {
- resCacheByCulture = new Dictionary<string, string>();
- resourceCache.Add(culture.Name, resCacheByCulture);
- }
- resCacheByCulture.Add(resourceKey, resourceValue);
- }
- }
- return resourceValue;
- }
Dan yang terakhir tambahakn ConnectionString pada web.config
Code Snippet
- <connectionStrings>
- <add name="MyResourceManager" connectionString="Data Source=.\SQL2005;Initial Catalog=MyResourceManagerDB;User ID=sa;Password=12345" providerName="System.Data.SqlCLient"/>
- </connectionStrings>
Rename DataSource, User dan password, sesuai environment kalian...
Semoga bermanfaat
Dalam pengembangan aplikasi, multi language merupakan point plus dalam aplikasi. Dalam .NET sendiri fitur tersebut sudah ada yaitu menggunakan ResourceManager, namun dalam prakteknya setiap bahasa kita membutuhkan tenaga kerja yg menguasai bahasa terkait, dan juga harus mencompile dan mendeploy kembali. Tentunya ujung2nya adalah waktu dan cost.
Source code dapat didownload di sini
Dalam postingan kali ini saya akan memberikan contoh membuat Custom ResourceManager dgn implementasi service Translator Microsoft. Contoh screennya adalah sbb :
English

Indonesian

Japanese

Korean
Cukup menarik bukan ?
Sebelum menyentuh lebih dalam, sebaiknya lihat dahulu link dibawah ini :
- ResourceManager, detail penggunanaannya dapat dilihat pada http://msdn.microsoft.com/en-us/library/aa905797.aspx.
- Contoh Implementasi Translator telah posting oleh teman kita Yugie, linknya adalah http://students.netindonesia.net/blogs/yugie/archive/2011/03/02/mudah-translator-dengan-bing-web-service.aspx
- Dan juga untuk detail API Translator dapat dilihat pada http://api.microsofttranslator.com/
Jika sudah membaca link diatas, oke kita lanjut....
Saya membuat enum LanguageType dan Extensionnya, tujuannya untuk mapping dengan API Translator dan juga mapping CultureInfonya
Code Snippet
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Globalization;
- namespace MyResourceManager
- {
- public enum LanguageType
- {
- AutoDetect,
- Arabic,
- Bulgarian,
- Catalan,
- Chinese_Simplified,
- Chinese_Traditional,
- Czech,
- Danish,
- Dutch,
- English,
- Estonian,
- Finnish,
- French,
- German,
- Greek,
- Haitian_Creole,
- Hebrew,
- Hungarian,
- Indonesian,
- Italian,
- Japanese,
- Korean,
- Latvian,
- Lithuanian,
- Norwegian,
- Polish,
- Portuguese,
- Romanian,
- Russian,
- Slovak,
- Slovenian,
- Spanish,
- Swedish,
- Thai,
- Turkish,
- Ukrainian,
- Vietnamese
- }
- public static class LanguageTypeExtension
- {
- public static string GetLanguageCode(this LanguageType value)
- {
- switch (value)
- {
- case LanguageType.AutoDetect:
- return "";
- case LanguageType.Arabic:
- return "ar";
- case LanguageType.Bulgarian:
- return "bg";
- case LanguageType.Catalan:
- return "ca";
- case LanguageType.Chinese_Simplified:
- return "zh-CHS";
- case LanguageType.Chinese_Traditional:
- return "zh-CHT";
- case LanguageType.Czech:
- return "cs";
- case LanguageType.Danish:
- return "da";
- case LanguageType.Dutch:
- return "nl";
- case LanguageType.English:
- return "en";
- case LanguageType.Estonian:
- return "et";
- case LanguageType.Finnish:
- return "fi";
- case LanguageType.French:
- return "fr";
- case LanguageType.German:
- return "de";
- case LanguageType.Greek:
- return "el";
- case LanguageType.Haitian_Creole:
- return "ht";
- case LanguageType.Hebrew:
- return "he";
- case LanguageType.Hungarian:
- return "hu";
- case LanguageType.Indonesian:
- return "id";
- case LanguageType.Italian:
- return "it";
- case LanguageType.Japanese:
- return "ja";
- case LanguageType.Korean:
- return "ko";
- case LanguageType.Latvian:
- return "lv";
- case LanguageType.Lithuanian:
- return "lt";
- case LanguageType.Norwegian:
- return "no";
- case LanguageType.Polish:
- return "pl";
- case LanguageType.Portuguese:
- return "pt";
- case LanguageType.Romanian:
- return "ro";
- case LanguageType.Russian:
- return "ru";
- case LanguageType.Slovak:
- return "sk";
- case LanguageType.Slovenian:
- return "sl";
- case LanguageType.Spanish:
- return "es";
- case LanguageType.Swedish:
- return "sv";
- case LanguageType.Thai:
- return "th";
- case LanguageType.Turkish:
- return "tr";
- case LanguageType.Ukrainian:
- return "uk";
- case LanguageType.Vietnamese:
- return "vi";
- default:
- return "";
- }
- }
- }
- public static class CultureInfoExtension
- {
- public static LanguageType GetLanguageType(this CultureInfo value)
- {
- string name = value.Name.ToLower();
- LanguageType result = LanguageType.English;
- switch (name)
- {
- case "af":
- case "af-za":
- result = LanguageType.English;
- break;
- case "sq":
- case "sq-al":
- break;
- case "ar":
- case "ar-dz":
- case "ar-bh":
- case "ar-eg":
- case "ar-iq":
- case "ar-jo":
- case "ar-kw":
- case "ar-lb":
- case "ar-ly":
- case "ar-ma":
- case "ar-om":
- case "ar-qa":
- case "ar-sa":
- case "ar-sy":
- case "ar-tn":
- case "ar-ae":
- case "ar-ye":
- result = LanguageType.Arabic;
- break;
- case "hy":
- case "hy-am":
- break;
- case "az":
- case "az-az-cyrl":
- case "az-az-latn":
- break;
- case "eu":
- case "eu-es":
- break;
- case "be":
- case "be-by":
- break;
- case "bg":
- case "bg-bg":
- result = LanguageType.Bulgarian;
- break;
- case "ca":
- case "ca-es":
- result = LanguageType.Catalan;
- break;
- case "zh-hk":
- case "zh-mo":
- case "zh-cn":
- case "zh-sg":
- case "zh-tw":
- case "zh-chs":
- result = LanguageType.Chinese_Simplified;
- break;
- case "zh-cht":
- result = LanguageType.Chinese_Traditional;
- break;
- case "hr":
- case "hr-hr":
- break;
- case "cs":
- case "cs-cz":
- result = LanguageType.Czech;
- break;
- case "da":
- case "da-dk":
- result = LanguageType.Danish;
- break;
- case "div":
- case "div-mv":
- break;
- case "nl":
- case "nl-be":
- case "nl-nl":
- result = LanguageType.Dutch;
- break;
- case "en":
- case "en-au":
- case "en-bz":
- case "en-ca":
- case "en-cb":
- case "en-ie":
- case "en-jm":
- case "en-nz":
- case "en-ph":
- case "en-za":
- case "en-tt":
- case "en-gb":
- case "en-us":
- case "en-zw":
- result = LanguageType.English;
- break;
- case "et":
- case "et-ee":
- result = LanguageType.Estonian;
- break;
- case "fo":
- case "fo-fo":
- break;
- case "fa":
- case "fa-ir":
- break;
- case "fi":
- case "fi-fi":
- result = LanguageType.Finnish;
- break;
- case "fr":
- case "fr-be":
- case "fr-ca":
- case "fr-fr":
- case "fr-lu":
- case "fr-mc":
- case "fr-ch":
- result = LanguageType.French;
- break;
- case "gl":
- case "gl-es":
- break;
- case "ka":
- case "ka-ge":
- break;
- case "de":
- case "de-at":
- case "de-de":
- case "de-li":
- case "de-lu":
- case "de-ch":
- result = LanguageType.German;
- break;
- case "el":
- case "el-gr":
- result = LanguageType.Greek;
- break;
- case "gu":
- case "gu-in":
- break;
- case "he":
- case "he-il":
- result = LanguageType.Hebrew;
- break;
- case "hi":
- case "hi-in":
- break;
- case "hu":
- case "hu-hu":
- result = LanguageType.Hungarian;
- break;
- case "is":
- case "is-is":
- break;
- case "id":
- case "id-id":
- result = LanguageType.Indonesian;
- break;
- case "it":
- case "it-it":
- case "it-ch":
- result = LanguageType.Italian;
- break;
- case "ja":
- case "ja-jp":
- result = LanguageType.Japanese;
- break;
- case "kn":
- case "kn-in":
- break;
- case "kk":
- case "kk-kz":
- break;
- case "kok":
- case "kok-in":
- break;
- case "ko":
- case "ko-kr":
- result = LanguageType.Korean;
- break;
- case "ky":
- case "ky-kz":
- break;
- case "lv":
- case "lv-lv":
- result = LanguageType.Latvian;
- break;
- case "lt":
- case "lt-lt":
- result = LanguageType.Lithuanian;
- break;
- case "mk":
- case "mk-mk":
- break;
- case "ms":
- case "ms-bn":
- case "ms-my":
- break;
- case "mr":
- case "mr-in":
- break;
- case "mn":
- case "mn-mn":
- break;
- case "no":
- result = LanguageType.Norwegian;
- break;
-
- case "nb-no":
- break;
- case "nn-no":
- break;
- case "pl": break;
- case "pl-pl":
- result = LanguageType.Polish;
- break;
- case "pt":
- case "pt-br":
- case "pt-pt":
- result = LanguageType.Portuguese;
- break;
- case "pa":
- case "pa-in":
- break;
- case "ro":
- case "ro-ro":
- result = LanguageType.Romanian;
- break;
- case "ru":
- case "ru-ru":
- result = LanguageType.Russian;
- break;
- case "sa":
- case "sa-in":
- break;
- case "sr-sp-cyrl":
- case "sr-sp-latn":
- break;
- case "sk":
- case "sk-sk":
- result = LanguageType.Slovak;
- break;
- case "sl":
- case "sl-si":
- result = LanguageType.Slovenian;
- break;
- case "es":
- case "es-ar":
- case "es-bo":
- case "es-cl":
- case "es-co":
- case "es-cr":
- case "es-do":
- case "es-ec":
- case "es-sv":
- case "es-gt":
- case "es-hn":
- case "es-mx":
- case "es-ni":
- case "es-pa":
- case "es-py":
- case "es-pe":
- case "es-pr":
- case "es-es":
- case "es-uy":
- case "es-ve":
- result = LanguageType.Spanish;
- break;
- case "sw":
- case "sw-ke":
- break;
- case "sv":
- case "sv-fi":
- case "sv-se":
- result = LanguageType.Swedish;
- break;
- case "syr":
- case "syr-sy":
- break;
- case "ta":
- case "ta-in":
- break;
- case "tt":
- case "tt-ru":
- break;
- case "te":
- case "te-in":
- break;
- case "th":
- case "th-th":
- result = LanguageType.Thai;
- break;
- case "tr":
- case "tr-tr":
- result = LanguageType.Turkish;
- break;
- case "uk":
- case "uk-ua":
- result = LanguageType.Ukrainian;
- break;
- case "ur":
- case "ur-pk":
- break;
- case "uz":
- case "uz-uz-cyrl":
- case "uz-uz-latn":
- break;
- case "vi":
- case "vi-vn":
- result = LanguageType.Vietnamese;
- break;
- default:
- result = LanguageType.AutoDetect;
- break;
- }
- return result;
- }
- }
-
- }
Kemudian saya membuat class Utility untuk memudahkan memanggil fungsi API Translatornya
Code Snippet
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Configuration;
- using System.Globalization;
- namespace MyResourceManager
- {
- public static class Utils
- {
- /// <summary>
- /// Application ID untuk kebutuhan Service API Translator, APP ID dapat dibuat dari link http://www.bing.com/developers/appids.aspx
- /// </summary>
- private static string _AppId = ConfigurationManager.AppSettings["MSTranslatorAppId"].ToString();
- /// <summary>
- /// default kode bahasa ResourceKey valuenya dapat didapat dari LanguageTypeExtension.GetLanguageCode()
- /// </summary>
- private static string _ResourceKeyLanguage = ConfigurationManager.AppSettings["ResourceKeyLanguage"].ToString();
- public static string ResourceKeyLanguage
- {
- get
- {
- return _ResourceKeyLanguage;
- }
- }
- public static string AppId
- {
- get
- {
- return _AppId;
- }
- }
- public static string Translate(string resourceKey, LanguageType from, LanguageType to)
- {
- if (from.Equals(to)) // Skip jika translate ke bahasa yg sama;
- return resourceKey;
- string result = string.Empty;
- using (MSTranslator.LanguageServiceClient client = new MSTranslator.LanguageServiceClient())
- {
- result = client.Translate(_AppId, resourceKey, from.GetLanguageCode(), to.GetLanguageCode(), "text/plain", "general");
- }
- return result;
- }
- public static string Translate(string resourceKey, CultureInfo from, CultureInfo to)
- {
- LanguageType fromLang = from.GetLanguageType();
- LanguageType toLang = to.GetLanguageType();
- return Translate(resourceKey, fromLang, toLang);
- }
- }
- }
Kemudian implementasi ResourceManagernya
(disini saya tidak menjelaskan lebih detail, beberapa code saya ambil dari contoh http://msdn.microsoft.com/en-us/library/aa905797.aspx.
Code Snippet
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Resources;
- using System.Globalization;
- using System.Web.Compilation;
- using System.Collections.Specialized;
- namespace MyResourceManager
- {
- public class DisposableBaseType : IDisposable
- {
- private bool m_Disposed;
- protected bool Disposed
- {
- get
- {
- lock (this)
- {
- return m_Disposed;
- }
- }
- }
- #region IDisposable Members
- public void Dispose()
- {
- lock (this)
- {
- if (m_Disposed == false)
- {
- Cleanup();
- m_Disposed = true;
- GC.SuppressFinalize(this);
- }
- }
- }
- #endregion
- protected virtual void Cleanup()
- {
- // override to provide cleanup
- }
- ~DisposableBaseType()
- {
- Cleanup();
- }
- }
- public class MyResourceReader : DisposableBaseType, IResourceReader, IEnumerable<KeyValuePair<string, object>>
- {
- private ListDictionary _listDic;
- public MyResourceReader(ListDictionary dic)
- {
- this._listDic = dic;
- }
- #region IResourceReader Members
- public void Close()
- {
- this.Dispose();
- }
- public System.Collections.IDictionaryEnumerator GetEnumerator()
- {
- if (Disposed)
- throw new ObjectDisposedException("MyResourceReader object is already disposed.");
- return this._listDic.GetEnumerator();
- }
- #endregion
- #region IEnumerable Members
- System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
- {
- if (Disposed)
- throw new ObjectDisposedException("MyResourceReader object is already disposed.");
- return this._listDic.GetEnumerator();
- }
- #endregion
- protected override void Cleanup()
- {
- try
- {
- this._listDic = null;
- }
- finally
- {
- base.Cleanup();
- }
- }
- #region IEnumerable<KeyValuePair<string,object>> Members
- IEnumerator<KeyValuePair<string, object>> IEnumerable<KeyValuePair<string, object>>.GetEnumerator()
- {
- if (Disposed)
- throw new ObjectDisposedException("MyResourceReader object is already disposed.");
- return this._listDic.GetEnumerator() as IEnumerator<KeyValuePair<string, object>>;
- }
- #endregion
- }
- public class MyResourceProvider : DisposableBaseType, IResourceProvider
- {
- private string classKey;
- private Dictionary<string, Dictionary<string, string>> resourceCache = new Dictionary<string, Dictionary<string, string>>();
- private CultureInfo resourceKeyLanguage = new CultureInfo(Utils.ResourceKeyLanguage);
- public MyResourceProvider(string classKey)
- {
- this.classKey = classKey;
- }
- #region IResourceProvider Members
- public object GetObject(string resourceKey, CultureInfo culture)
- {
- if (Disposed)
- throw new ObjectDisposedException("MyResourceProvider object is already disposed.");
- if (string.IsNullOrEmpty(resourceKey))
- throw new ArgumentNullException("resourceKey");
- if (culture == null)
- culture = CultureInfo.CurrentUICulture;
- if (string.IsNullOrEmpty(culture.Name))
- culture = System.Globalization.CultureInfo.CurrentUICulture;
- string resourceValue = null;
- Dictionary<string, string> resCacheByCulture = null;
- if (resourceCache.ContainsKey(culture.Name))
- {
- resCacheByCulture = resourceCache[culture.Name];
- if (resCacheByCulture.ContainsKey(resourceKey))
- resourceValue = resCacheByCulture[resourceKey];
- }
- if (resourceValue == null)
- {
- resourceValue = Utils.Translate(resourceKey, resourceKeyLanguage, culture);
- // add this result to the cache
- // find the dictionary for this culture
- // add this key/value pair to the inner dictionary
- lock (this)
- {
- if (resCacheByCulture == null)
- {
- resCacheByCulture = new Dictionary<string, string>();
- resourceCache.Add(culture.Name, resCacheByCulture);
- }
- resCacheByCulture.Add(resourceKey, resourceValue);
- }
- }
- return resourceValue;
- }
- public IResourceReader ResourceReader
- {
- get
- {
- if (Disposed)
- throw new ObjectDisposedException("MyResourceProvider object is already disposed.");
- // this is required for implicit resources
- // this is also used for the expression editor sheet
- ListDictionary resourceDictionary = new ListDictionary();
- return new MyResourceReader(resourceDictionary);
- }
- }
- #endregion
- protected override void Cleanup()
- {
- try
- {
- this.resourceCache.Clear();
- }
- finally
- {
- base.Cleanup();
- }
- }
- }
- public class MyResourceProviderFactory : ResourceProviderFactory
- {
- public override IResourceProvider CreateGlobalResourceProvider(string classKey)
- {
- return new MyResourceProvider(classKey);
- }
- public override IResourceProvider CreateLocalResourceProvider(string virtualPath)
- {
- string classKey = virtualPath;
- if (!string.IsNullOrEmpty(virtualPath))
- {
- virtualPath = virtualPath.Remove(0, 1);
- classKey = virtualPath.Remove(0, virtualPath.IndexOf('/') + 1);
- }
- return new MyResourceProvider(classKey);
- }
- }
- }
Kemudian tambahan web.config nya
Code Snippet
- .
- .
- .
- .
- <appSettings>
- <!-- default kode bahasa ResourceKey valuenya dapat didapat dari LanguageTypeExtension.GetLanguageCode() -->
- <add key="ResourceKeyLanguage" value="en" />
- <!-- Application ID untuk kebutuhan Service API Translator, APP ID dapat dibuat dari link http://www.bing.com/developers/appids.aspx -->
- <add key="MSTranslatorAppId" value="2FF28BD209C6653904A8307F0BA50E1D97E862B0"/>
- </appSettings>
- <system.web>
- <!-- uiCulture untuk default tampilan bahsa di UInya -->
- <globalization culture="en-US" uiCulture="en-US" resourceProviderFactoryType="MyResourceManager.MyResourceProviderFactory, MyResourceManager" />
- .
- .
- .
- .
Pada contoh ini ada beberapa configurasi yg perlu kita ketahui
- Pada AppSetting ResourceKeyLanguage ini digunakan untuk default bahasa ResourceKey nya. Setiap kita membuat resourceKey, musti dalam bahasa yg diset pada ResourceKeyLanguage.
- Pada AppSetting MSTranslatorAppId, Application ID harus diset untuk mendapatkannya harus register terlebih dahulu pada link http://www.bing.com/developers/appids.aspx
Berhubung waktu terbatas, sepertinya cukup untuk sekian dulu... 
Dengar nama Recursive, sering menjadi bottle neck dalam aplikasi. Banyak cara untuk meningkatkan performance agar metode recursive tidak menjadi berat.
Source code dapat di download di sini
Contoh Struktur Table Part
Contoh Isi Data Part
Result
Class Part
Code Snippet
- [Table(Name = "Part")]
- public partial class Part
- {
- [Column(DbType = "INT NOT NULL IDENTITY", IsPrimaryKey = true, IsDbGenerated = true, AutoSync = AutoSync.OnInsert, Name = "PartID")]
- public int PartID { get; set; }
- [Column(DbType="INT NULL", CanBeNull=true, Name="ParentPartID")]
- public int? ParentPartID { get; set; }
- [Column(DbType = "VARCHAR(255) NOT NULL", CanBeNull = false, Name = "PartName")]
- public string PartName { get; set; }
- [Column(DbType = "DECIMAL NOT NULL", CanBeNull = false, Name = "Price")]
- public Decimal Price { get; set; }
- [Column(DbType = "INT NOT NULL", CanBeNull = false, Name = "Qty")]
- public int Qty { get; set; }
- }
Addional Property class Part (untuk memudahkan perhitungan Recursive)
Code Snippet
- public partial class Part
- {
- public Part Parent { get; set; }
- private List<Part> _childs = new List<Part>();
- public List<Part> Childs
- {
- get { return _childs; }
- set { _childs = value; }
- }
- public Decimal Total
- {
- get
- {
- return (Price * Qty);
- }
- }
- public Decimal SubTotal
- {
- get
- {
- return _childs.Sum(t => t.SubTotal) + Total;
- }
- }
- public int TotalPart
- {
- get
- {
- return _childs.Sum(t => t.TotalPart) + Qty;
- }
- }
- public override string ToString()
- {
- string LabelPrice = string.Empty;
- if (Parent == null)
- LabelPrice = "Grand Total";
- else if (_childs.Count > 0)
- LabelPrice = "Sub Total";
- else
- LabelPrice = "Total";
- return string.Format("Part Name : {0}, Price: Rp {1}, Qty: {2}, Total Part: {3}, {4}: Rp {5}", PartName, Price.ToString("n0"), Qty, TotalPart, LabelPrice, SubTotal.ToString("n0"));
- }
- }
Part Context (untuk caching data, dan membuat struktur Tree)
Code Snippet
- public class PartContext
- {
- private static string ConnectionString = ConfigurationManager.ConnectionStrings["MyPartDB"].ConnectionString;
- private static Dictionary<int, Part> dicParts = new Dictionary<int, Part>();
- public static Part[] GetAllParts()
- {
- if (dicParts.Count == 0)
- PopulatePart();
- return dicParts.Values.ToArray();
- }
- public static Part[] GetAllParent()
- {
- if (dicParts.Count == 0)
- PopulatePart();
- return dicParts.Values.Where(t => !t.ParentPartID.HasValue).ToArray();
- }
- public static Part GetByPartID(int PartID)
- {
- if (dicParts.Count == 0)
- PopulatePart();
- if (dicParts.ContainsKey(PartID))
- return dicParts[PartID];
- else
- return null;
- }
- public static void ClearCache()
- {
- dicParts.Clear();
- }
- private static void PopulatePart()
- {
- using (DBContext ctxt = new DBContext(ConnectionString))
- {
- #region Create Database
- if (!ctxt.DatabaseExists())
- {
- // Create Database
- ctxt.CreateDatabase();
- // Create Dummy Record;
- Part PC = new Part()
- {
- ParentPartID = null,
- PartName = "PC",
- Price = 0,
- Qty = 1
- };
- ctxt.Parts.InsertOnSubmit(PC);
- ctxt.SubmitChanges();
- Part Casing = new Part()
- {
- ParentPartID = PC.PartID,
- PartName = "Casing",
- Price = 500000,
- Qty = 1
- };
- ctxt.Parts.InsertOnSubmit(Casing);
- ctxt.SubmitChanges();
- Part PowerSupply = new Part()
- {
- ParentPartID = Casing.PartID,
- PartName = "Power Supply",
- Price = 400000,
- Qty = 1
- };
- ctxt.Parts.InsertOnSubmit(PowerSupply);
- ctxt.SubmitChanges();
- Part ExtraFan = new Part()
- {
- ParentPartID = Casing.PartID,
- PartName = "Extra Fan",
- Price = 120000,
- Qty = 1
- };
- ctxt.Parts.InsertOnSubmit(ExtraFan);
- ctxt.SubmitChanges();
- Part DVD = new Part()
- {
- ParentPartID = Casing.PartID,
- PartName = "DVD ROM RW",
- Price = 400000,
- Qty = 1
- };
- ctxt.Parts.InsertOnSubmit(DVD);
- ctxt.SubmitChanges();
- Part Motherboard = new Part()
- {
- ParentPartID = Casing.PartID,
- PartName = "Motherboard",
- Price = 1200000,
- Qty = 1
- };
- ctxt.Parts.InsertOnSubmit(Motherboard);
- ctxt.SubmitChanges();
- Part Processor = new Part()
- {
- ParentPartID = Motherboard.PartID,
- PartName = "Processor",
- Price = 2500000,
- Qty = 1
- };
- ctxt.Parts.InsertOnSubmit(Processor);
- ctxt.SubmitChanges();
- Part Memory = new Part()
- {
- ParentPartID = Motherboard.PartID,
- PartName = "Memory 4GB",
- Price = 600000,
- Qty = 4
- };
- ctxt.Parts.InsertOnSubmit(Memory);
- ctxt.SubmitChanges();
- Part VGACARD = new Part()
- {
- ParentPartID = Motherboard.PartID,
- PartName = "VGA CARD",
- Price = 1200000,
- Qty = 1
- };
- ctxt.Parts.InsertOnSubmit(VGACARD);
- ctxt.SubmitChanges();
- Part SOUNDCARD = new Part()
- {
- ParentPartID = Motherboard.PartID,
- PartName = "SOUND CARD",
- Price = 600000,
- Qty = 1
- };
- ctxt.Parts.InsertOnSubmit(SOUNDCARD);
- ctxt.SubmitChanges();
- Part HardDisk = new Part()
- {
- ParentPartID = Motherboard.PartID,
- PartName = "Hard Disk 1 TB",
- Price = 1300000,
- Qty = 2
- };
- ctxt.Parts.InsertOnSubmit(HardDisk);
- ctxt.SubmitChanges();
- Part CoolingFan = new Part()
- {
- ParentPartID = Processor.PartID,
- PartName = "Super Cooler",
- Price = 350000,
- Qty = 1
- };
- ctxt.Parts.InsertOnSubmit(CoolingFan);
- ctxt.SubmitChanges();
- }
- #endregion
- #region Populate Dic
- Part[] parts = ctxt.Parts.ToArray();
- // Populate Dic
- foreach (Part part in parts)
- dicParts.Add(part.PartID, part);
- // Populate Parent & Child
- foreach (Part part in parts)
- {
- if (part.ParentPartID.HasValue)
- {
- Part parentPart = dicParts[part.ParentPartID.GetValueOrDefault()];
- part.Parent = parentPart;
- parentPart.Childs.Add(part);
- }
- }
- #endregion
- }
- }
- }
Kesimpulan :
Metode ini sangat berguna untuk menaikan kinerja aplikasi. Akan tetapi jgn lupa Clear Cache atau Partial Update pada PartContextnya jika terjadi perubahan data..
Apakah ada cara yg lebih cepat dari ini ?
Hari ini adalah hari account geeks.netindonesia saya dibuat.