|
|
|
Rank: Advanced Member
Groups: Registered
Joined: 6/10/2010 Posts: 71 Location: Sweden
|
Hi folks I seem to be having some problems when trying to gather all my text resources in an easy-to-handle manner. The problem is demonstrated below: This code generates "object not set to an instance" Code:using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc;
using Everest.CmsServices.Models; using Everest.CmsServices.MvcHelper; using Everest.CmsServices.Extension.Module; using Kooboo.Module.Arloesi.MemberControls.Models;
namespace Kooboo.Module.Arloesi.MemberControls.Controllers { public class RegisterController : ModuleController { public ActionResult Index() { Tst test123 = new Tst(); Response.Write(test123.Validate()); return View(new ViewModels.MemberForm(null)); } }
public class Tst : ModuleController { public string Validate() { string baba = GetTextResource("test3224", "TEST TEXT"); return baba; } } } Whlie this code(ofcourse) works fine. Code:using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc;
using Everest.CmsServices.Models; using Everest.CmsServices.MvcHelper; using Everest.CmsServices.Extension.Module; using Kooboo.Module.Arloesi.MemberControls.Models;
namespace Kooboo.Module.Arloesi.MemberControls.Controllers { public class RegisterController : ModuleController { public ActionResult Index() { Response.Write(GetTextResource("test3224", "TEST TEXT")); return View(new ViewModels.MemberForm(null)); } } } Since im trying to lift out all textresources for custom errormessages and such, this causes a bit of a problem. Any good hint on how to get around this? Separate function in the same namespace works fine but desnt solve what im after.
|
|
|
|
Rank: Advanced Member
Groups: Registered
Joined: 2/2/2010 Posts: 122 Location: England
|
Are you deriving Tst from ModuleController so you can get access to GetTextResource()?
You can get to resource values from CmsContext, you could create your own base class for your controllers where you would put the GetReousrce() function, or you could create a library where you pass in CmsContext
I don't know if this is anythign to do with your problem though, but it doesn't look right to have a utility class derived from ModuleController to me?!?
|
|
|
|
Rank: Advanced Member
Groups: Registered
Joined: 3/4/2010 Posts: 147 Location: brazil
|
It's not a good design at all extend the controller class if you're not using it as a controller, I'm not sure if it will work but you can call initialize method in the controller before calling the validate
maybe a better design option is to create an abstract class that extend modulecontroller and them all of your controller classes extend you base controller class with all your funcionality
|
|
|
|
Rank: Advanced Member
Groups: Registered
Joined: 6/10/2010 Posts: 71 Location: Sweden
|
Luiz / Gaz: Regarding extending the controller i tought that would be subpar / bad practise as well but was a bit uncertain on how to go about it the best way so wanted just to show what im after. My goal was to keep all text resource information for the project in the same place(except the localized labeltexts etc that reside in the views). I tried a couple of things but didnt really work out(Am learning MVC at the same time im learning Koobo so a bit to take in at the same time). Alternative as i thought of after i posted was perhaps to only store only the string resources(the exception strings etc) and then assemble the ones needed via returned List in the controller to the modelerrors / gettextresources like: Code:List<InputError> Errors = arVal.ValidateMember(NewMember); foreach (var Itm in Errors) ModelState.AddModelError(Itm.ElementID, GetTextResource(Itm.ResourceID, Itm.ResourceText)); Edit: Think i will use a baseclass / text separation combo, see if that will give me a clean separation, thx for the input!
|
|
|
|
Rank: Advanced Member
Groups: Registered
Joined: 2/2/2010 Posts: 122 Location: England
|
If you're interested in how I get resources in my modules, I have a separate dll (a base class derived from ModuleController would also be fine) containing a static class : Code: public static class Resources { static Resources() { }
/// <summary> /// /// </summary> /// <param name="oController"></param> /// <param name="key"></param> /// <param name="defaultValue"></param> /// <returns></returns> static public string GetResource( Everest.CmsServices.MvcHelper.CmsContext oCmsContext, string key, string defaultValue) { key = StringExtensions.TryTrim(key); IDictionary<string, string> aTextResources = UnityManager.Resolve<TextResourceService>().GetTextResources( oCmsContext.Cms_Application.ApplicationName);
if (aTextResources.ContainsKey(key)) { return aTextResources[key]; } else { //Add the key, it doesn't exist yet. We'll need it next time UnityManager.Resolve<TextResourceService>().AddTextResource( oCmsContext.Cms_Application.ApplicationName, key, defaultValue); return defaultValue; } } }
If I remember correctly I kind of grabbed this from the Kooboo source and pushed it into my namespace. I wanted the functionality where, if the resource key you are after doesn't exist, it automatically creates it for you with the supplied default
|
|
|
|
Rank: Advanced Member
Groups: Registered
Joined: 6/10/2010 Posts: 71 Location: Sweden
|
Gaz: Thx, that seems interesting. Am gonna use a baseclass anyway because of other matters but the problem i have with base class i guess will be the same as the example i showed(that is they need to reside in the base class namespace). But your example seems to be able to help me solve the problem in conjunction with some sort of static list so will try that.
Another thought i had was how to be able to create all reasources at installation and then be able to fetch localized versions of the resources at the different region sites without manually having to translate it all when its needed. Import/export as ive seen is only whole applications? For example if one build a site template and some of the implemented sites want 2 or 3 languages that ive translated earlier i can implement them without translating all module resources by hand each time. Though this is not something ive looked into yet at all, first my plan is to get the module im building now up and running. But collecting all "codebehind/controller/model" resources in one place seems like a good thing to do if i later plan to have different versions of them.
|
|
|
|
Rank: Advanced Member
Groups: Registered
Joined: 6/10/2010 Posts: 71 Location: Sweden
|
Gaz: i built a class based on your code and it seems to work nicely! The way i found to retrieve the cmscontext where with HttpContext.Current.GetCmsContext(), dunno if thats the cleanest way to get it but it works.
Though still thinking a bit if i should like put the resourcestrings in a dictionary/list/ static const class (in wich case i actually dont need anything other then the built in GetTextResource) or i should handle the textresourses completely in an isolated class and just deliver the appropriate strings to the places their needed. ie:
getresource -> string / list ->controller modelerror or key/defaultvalue strings -> list -> addmodelerror("", gettextresoruce(key, defaultvalue)).
Im after the method creating the least overhead that is. Feels like one got it all served on a platter using resourcefiles ;) with the pros and cons that presents.
|
|
|
|
Rank: Advanced Member
Groups: Registered
Joined: 2/2/2010 Posts: 122 Location: England
|
The code I posted calls this Kooboo API function : Code:Everest.CmsServices.Services.TestResourceService.GetTextResources(string application); This function caches an IDictionary<string,string> for you so does that help you with your overhead requirements? See : Code:public IDictionary<string, string> GetTextResources(string application) { string cacheKey = string.Format("__TextResource_Application:{0}", application); IDictionary<string, string> resources = CacheManager.Get(CachedData.Template, cacheKey) as IDictionary<string, string>; if (resources == null) { IEverestCmsDataContext dataContext = EverestCmsEntities.GetDataContext(); var textResourceQuery = dataContext.QueryTextResources(application);
resources = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);
foreach (var item in textResourceQuery) { if (!resources.ContainsKey(item.ResourceKey)) { resources.Add(item.ResourceKey, item.ResourceValue); } }
CacheManager.Add(CachedData.Template, cacheKey, resources, CacheItemPriority.Normal, null, CachedData.DefaultExpirations); } return resources; }
|
|
|
|
Rank: Advanced Member
Groups: Registered
Joined: 6/10/2010 Posts: 71 Location: Sweden
|
Yes but the GetResource code you posted does approx the same as the built in GetTextResource(key, defaulttext), except that GetTextResource only generates object not set to instance when fired outside of controller (as far as ive tested anyhow, modulesettings does the same). Regarding the last example you posted it takes the kooboo resources into a cache correct? Im not really to worried about the overhead regarding the fetchtimes from kooboo though. What i wanted was: What i wanted to avoid: Code:Controller1 GetTextResource("KEY1", "STRING1");
Controller2 GetTextResource("KEY2", "STRING2");
Controller3 GetTextResource("KEY1", "STRING1"); GetTextResource("KEY2", "STRING2"); What i was after: Code:Controller1 GetTextResource(key1, string1);
Controller2 GetTextResource(key2, string2);
Controller3 GetTextResource(key1, string1); GetTextResource(key2, string2);
Some class parentfolder = "Setings."; key1 = parentfolder + "KEY1"; string1 = "STRING1"; key2 = parentfolder + "KEY2"; string2 = "STRING2";
Or that the static class contained the affected resources directly, setting them there. It could be perhaps const like above or dicionary item or other. Though your cached example would be nice to but using that as is would mean i still have to set the properties somehow thus initializing / creating the resources. SO what i was after was to never set static strings in the controllers, the key and defaultvalue in the gettextresource and your getresource functions being suchs strings. Since my plan is to reuse atleast some of the resources settings and setting the same string values on multiple resource calls seems bad and makes it hard if i where to change values or translate to other language later on for example. THOUGH with that said i think a cached dictionary containg the static strings seems like a nice option but question is if it where to make any practical difference compared to for example just placing all strings in a class, fetching them when needed.
|
|
|
|
Rank: Advanced Member
Groups: Registered
Joined: 6/10/2010 Posts: 71 Location: Sweden
|
Though one way of removing the issue would be not to use defaultvalues incode, setting them using a Generate function manually after install. And only have a const string for the parentfolder so its changeable without to much hassle. Atleast havent seen any functionality to create a list of resources automatically at install.
|
|
|
|
Rank: Advanced Member
Groups: Registered
Joined: 2/2/2010 Posts: 122 Location: England
|
You could have a module with a page that generates all the stuff you need, just browse to it once and let it do its work. You could pass the parentfolder in the module parameters
|
|
|
|
Rank: Advanced Member
Groups: Registered
Joined: 6/10/2010 Posts: 71 Location: Sweden
|
True, thats one option. Will do some coding and see where it leads ;)
|
|
|
|
Rank: Advanced Member
Groups: Registered
Joined: 6/10/2010 Posts: 71 Location: Sweden
|
Btw Gaz: You dont happen to have any samplecode for fetching modulesettings? It seems like ModuleInfo.ModuleSettings[].Value handles like gettextresource, ie cant get it outside controller action(no matter namespaces etc one uses). Am playing around to find a solution but if you happen to sit on any samplecode for that as well please post, the GetResource worked perfectly.
|
|
|
|
Rank: Advanced Member
Groups: Registered
Joined: 2/2/2010 Posts: 122 Location: England
|
This is how I do it, I have a base class for retrieving module settings : Code: public abstract class ConfigBaseClass { private ModuleSettingCollection m_aModuleSettings; public ConfigBaseClass( ModuleSettingCollection aModuleSettings) { m_aModuleSettings = aModuleSettings; } public string GetModuleSetting( string sKey) { return GetModuleSetting( sKey, ""); } public string GetModuleSetting( string sKey, string sDefault) { string sResult = sDefault; if( m_aModuleSettings != null) { ModuleSetting oSetting = m_aModuleSettings[sKey]; if( oSetting != null) { sResult = oSetting.Value; } } return sResult; } }
This is abstract as I inherit it on a per module basis where I add public properties that wrap module parameters specific for that module eg; Code: public class Config : ConfigBaseClass { private string m_sUITemplateConfigName; private TimeSpan? m_oCachePeriod; private int? m_nPageSize; private bool m_bCachePeriod_TriedToGetValue; public Config( ModuleSettingCollection aModuleSettings) : base( aModuleSettings) { m_bCachePeriod_TriedToGetValue = false; m_oCachePeriod = null; } /// <summary> /// This is the filename that is loaded in when it builds the simple admin /// interface. 100% required! /// </summary> public string UITemplateConfigFile { get { if( m_sUITemplateConfigName == null) { m_sUITemplateConfigName = GetModuleSetting( "UITemplateConfigFile", "SimpleAdmin.xml"); } return m_sUITemplateConfigName; } } public TimeSpan? CachePeriod { get { if( m_bCachePeriod_TriedToGetValue == false) { string sTemp = GetModuleSetting( "CachePeriod"); if( sTemp != null) { m_oCachePeriod = GarethElms.Kooboo.Standard.Date.ParseTimeSpan( sTemp); } m_bCachePeriod_TriedToGetValue = true; } return m_oCachePeriod; } } /// <summary> /// How many items to display in a list of content items. The list /// can be with a variety of filters from dates, category or no /// filter at all /// </summary> public int? PageSize { get { if( m_nPageSize.HasValue == false) { m_nPageSize = int.Parse( GetModuleSetting( "PageSize", "10")); } return m_nPageSize; } } }
In each module I have a controller base class derived from ModuleController. This base class has a private property : Code: public SimpleAdmin.Core.Config m_oUIConfig {get; private set;}
And a public property I can call from my action methods : Code: public SimpleAdmin.Core.Config UIConfig { get { if( m_oUIConfig == null) { m_oUIConfig = new SimpleAdmin.Core.Config( ModuleInfo.ModuleSettings); }
return m_oUIConfig; } private set {} }
Then in my action methods I can just call Code:UIConfig.CachePeriod or Code:UIConfig.GetModuleSetting( "name", "default") or something like that
|
|
|
|
Rank: Advanced Member
Groups: Registered
Joined: 6/10/2010 Posts: 71 Location: Sweden
|
Thanks a bunch for everything, will check that out!
|
|
|
|
Rank: Advanced Member
Groups: Registered
Joined: 6/10/2010 Posts: 71 Location: Sweden
|
I did some coding and ended upp with a scheme similar to yours but added some thiings and with all resurce strings collected in a single dictionary. Everything works very well but one problem still bugs me ;)
Its the scenario where you have a class that you never manually create instances of that class. I happen to have a couple of these wich are used for data annotation(set up in an abstract inherited class for the annotation classes). I know its not recommended using HttpContext, but that way i atleast can get the applicationname = text resources to work.
Though how to get the modulesettings without passing from controller actions still haunts me. Static variables should get me in trouble if trying to change settings at runtime i guess so thats not really god solution either.
SO if anyone has a good suggestion on how to grab the modulesettings(ang perhaps a better cmscontext way that httpcontext) that doesnt require being passed from controller action via constructors etc please let me know. Am checking around in the Kooboo CMS source(ModuleInfo.cs) but dont have any concrete solution yet.
|
|
|
|
Rank: Advanced Member
Groups: Registered
Joined: 2/2/2010 Posts: 122 Location: England
|
I have the same problem when I have static classes that act as helpers such as getting text resources from Kooboo. These classes are outside of the module controller so can't access CmsContext.
How about a singleton that is initialised from your base controller? Then all dependent classes outside the module controller can getInstance() of the singleton to access properties normally only accessible from the controller. Or is this obvious and you mean something better than that?!
At the moment I have two or three places where I pass CmsContext along, it feels messy and isn't scalable
|
|
|
|
Rank: Advanced Member
Groups: Registered
Joined: 6/10/2010 Posts: 71 Location: Sweden
|
I was trying to avoid singleton and statics(except the resource dictionary wich i hold as static), and if it werent for the darn annotation classes i would have gotten away with it ;) For all repositories etc i just get a single instance of my moduleconfig baseclass and pass it on. But dont really see any other way out of it atm.
|
|
|
|
Guest
|