mvc 4 localization in multi layer web app - asp.net-mvc-4

I am new to MVC and am trying to get a web application up and running that will be displayed in a couple of languages.
I have the localization functioning as I want in my web project but I have split my domain model into a separate class library and am wanting to perform localization on my property attributes as in the web project.
I have created a folder in my domain model project called Resources and have a Client.resx to store my fallback/default strings for property error messages and display names etc... I have also created a Client.es.resx for Spanish errors and display names however even if my culture is set to es, as below in my Global.aspx, I receive spanish on main web views but still receive english error messages on properties etc.. Can you tell me what I am doing wrong?
protected void Application_AcquireRequestState(object sender, EventArgs e)
{
//Create culture info object
CultureInfo ci = new CultureInfo("es");
Thread.CurrentThread.CurrentUICulture = ci;
Thread.CurrentThread.CurrentCulture =
CultureInfo.CreateSpecificCulture(ci.Name);
}

Dope...... I had forgot to add the named parameters to the attribute as below...... what a clown!
[Required (ErrorMessageResourceName = "Required", ErrorMessageResourceType = typeof(ModelRes.Client))]

Related

Deploying SSRS RDL files from VB.Net - Issue with shared datasources

I am currently developing a utility to help automate our report deployment process. Multiple files, in multiple folders, to multiple servers.
I am using the reportservice2010.asmx web service, and I am deploying my files to the server - so most of the way there.
My issue is that I have shared data sets and shared data sources, which are deployed to individual folders, separate to the report folders. When the deployment occurs the web service looks locally for the data source rather than in the data source folder, giving an error like:
The dataset ‘CostReduction’ refers to the shared data source ‘CostReduction’, which is not
published on the report server. The shared data source ‘CostReduction’ must be published
before this report can run.
The data source/set has been deployed and the report functions correctly but I need to suppress these error messages as they may be hiding other actual errors.
I can hard code a lookup that checks if the data source/set exists and manually filter them via that, but it seems very in-efficient. Is there any way I can tell the web service where to look for these files or another approach that other people have used?
I'm not looking at changing the reports so the data source is read from
/DataSources/DataSourceName
as there are lots of reports and that's not how our existing projects are configured.
Many thanks in advance.
I realize you are using VB, but perhaps this will give you a clue if you convert it from C# to VB, using one of the translators on the web.
Hopefully this will give you a lead in the right direction.
When All the reports in a particular folder, referred to here as the 'parent folder', all use the same Shared Data source, I use this to set all the reports to the same shared Data Source (in this case "/DataSources/Shared_New")
using GetPropertiesSample.ReportService2010;
using System.Diagnostics;
using System.Collections.Generic; //<== required for LISTS
using System.Reflection;
namespace GetPropertiesSample
{
class Program
{
static void Main(string[] args)
{
GetListOfObjectsInGivenFolder_and_ResetTheReportDataSource("0_Contacts"); //<=== This is the parent folder
}
private static void GetListOfObjectsInGivenFolder_and_ResetTheReportDataSource(string sParentFolder)
{
// Create a Web service proxy object and set credentials
ReportingService2010 rs = new ReportingService2010();
rs.Credentials = System.Net.CredentialCache.DefaultCredentials;
CatalogItem[] reportList = rs.ListChildren(#"/" + sParentFolder, true);
int iCounter = 0;
foreach (CatalogItem item in reportList)
{
iCounter += 1;
Debug.Print(iCounter.ToString() + "]#########################################");
if (item.TypeName == "Report")
{
Debug.Print("Report: " + item.Name);
ResetTheDataSource_for_a_Report(item.Path, "/DataSources/Shared_New"); //<=== This is the DataSource that I want them to use
}
}
}
private static void ResetTheDataSource_for_a_Report(string sPathAndFileNameOfTheReport, string sPathAndFileNameForDataSource)
{
//from: http://stackoverflow.com/questions/13144604/ssrs-reportingservice2010-change-embedded-datasource-to-shared-datasource
ReportingService2010 rs = new ReportingService2010();
rs.Credentials = System.Net.CredentialCache.DefaultCredentials;
string reportPathAndName = sPathAndFileNameOfTheReport;
//example of sPathAndFileNameOfTheReport "/0_Contacts/207_Practices_County_CareManager_Role_ContactInfo";
List<ReportService2010.ItemReference> itemRefs = new List<ReportService2010.ItemReference>();
ReportService2010.DataSource[] itemDataSources = rs.GetItemDataSources(reportPathAndName);
foreach (ReportService2010.DataSource itemDataSource in itemDataSources)
{
ReportService2010.ItemReference itemRef = new ReportService2010.ItemReference();
itemRef.Name = itemDataSource.Name;
//example of DataSource i.e. 'itemRef.Reference': "/DataSources/SharedDataSource_DB2_CRM";
itemRef.Reference = sPathAndFileNameForDataSource;
itemRefs.Add(itemRef);
}
rs.SetItemReferences(reportPathAndName, itemRefs.ToArray());
}
}
To Call it I use this in the 'Main' Method:
GetListOfObjectsInGivenFolder_and_ResetTheReportDataSource("0_Contacts");
In this case "0_Contacts" is the parent folder, itself located in the root directory, that contains all the reports for which I want to reset their DataSources to the new Shared DataSource. Then that Method calls the other method "ResetTheDataSource_for_a_Report" which actually sets the DataSource for the report.

MVC5 How to set language based on top level domain

I have a single MVC5 site which is accessed via several different regional URLs. In my case .co.uk (for the UK), .de (for Germany) and .fr (for France).
The site content is localised using RESX files and users can switch language via a cookie for persistence and a HttpModule which sets the asp.net thread locale based on the cookie (I used this approach).
I want the default language to be relevant to the top-level domain the user is accessing the site as. For example if a user is on .de, the default language should be de-DE. The user may choose to change the language in which case the default is overwritten, but it is very important that the default language is appropriate to the top-level domain (for users and search engines).
How can I achieve this in MVC5? The best I have got to so far is using JavaScript to check the url, set the cookie and refresh the page, but i know this is nasty and there must be a better way.
PS: Please note it is the top level domain that I want to drive this. I'm not using regional routing, for example http://whatever.com/DE or http://whatever.com/EN
PPS: I do not want to use the browser language detection feature either because that causes problems for search engines. i.e. it may cause the .de site to show in en-GB because that is what the search engine uses (or the search engine has no language so that is the default). If that happens the .de site will be treated as a duplicate of the .co.uk site which is never good for SEO
I figured out how to do this. Add this to global.asax
protected void Application_AcquireRequestState(object sender, EventArgs e)
{
if (Request.Cookies[Constants.LanguageCookieName] == null)
{
var culture = GetCultureFromHost();
Thread.CurrentThread.CurrentUICulture = culture;
Thread.CurrentThread.CurrentCulture = culture;
}
}
private CultureInfo GetCultureFromHost()
{
//set default culture of en-GB
CultureInfo ci = new CultureInfo("en-GB");
//get top level domain
string host = Request.Url.Host.ToLower();
//check for other known domains and set culture accordingly
if (host.Contains("whatever.de"))
{
ci = new CultureInfo("de-DE");
}
return ci;
}
In my case I set Persian cluture in global.asax and works well
protected void Application_BeginRequest(object sender, EventArgs e)
{
var persianCulture = new PersianCulture();
persianCulture.DateTimeFormat.ShortDatePattern = "yyyy/MM/dd";
persianCulture.DateTimeFormat.LongDatePattern = "dddd d MMMM yyyy";
persianCulture.DateTimeFormat.AMDesignator = "صبح";
persianCulture.DateTimeFormat.PMDesignator = "عصر";
Thread.CurrentThread.CurrentCulture = persianCulture;
Thread.CurrentThread.CurrentUICulture = persianCulture;
}

How do you set the Customvalidation in Metadata file, If the Metadata is in different Model project

My silverlight solution has 3 project files
Silverlight part(Client)
Web part(Server)
Entity model(I maintained the edmx along with Metadata in a seperate project)
Metadata file is a partial class with relavent dataannotation validations.
[MetadataTypeAttribute(typeof(User.UserMetadata))]
public partial class User
{
[CustomValidation(typeof(UsernameValidator), "IsUsernameAvailable")]
public string UserName { get; set; }
}
Now my question is where I need to keep this class UsernameValidator
If my Metadata class and edmx are on Server side(Web) then I know I need to create a .shared.cs class in my web project, then add the proper static method.
My IsUserAvailable method intern will call a domainservice method as part of asyc validation.
[Invoke]
public bool IsUsernameAvailable(string username)
{
return !Membership.FindUsersByName(username).Cast<MembershipUser>().Any();
}
If my metadata class is in the same project as my domain service is in then I can call domain service method from my UsernameValidator.Shared.cs class.
But here my entity models and Metadata are in seperate library.
Any idea will be appreciated
Jeff wonderfully explained the asyc validation here
http://jeffhandley.com/archive/2010/05/26/asyncvalidation-again.aspx
but that will work only when your model, metadata and Shared class, all are on server side.
There is a kind of hack to do this. It is not a clean way to do it it, but this is how it would probably work.
Because the .shared takes care of the code generation it doesn't complain about certain compile errors in the #if brackets of the code. So what you can do is create a Validator.Shared.cs in any project and just make sure it generates to the silverlight side.
Add the following code. and dont forget the namespaces.
#if SILVERLIGHT
using WebProject.Web.Services;
using System.ServiceModel.DomainServices.Client;
#endif
#if SILVERLIGHT
UserContext context = new UserContext();
InvokeOperation<bool> availability = context.DoesUserExist(username);
//code ommited. use what logic you want, maybe Jeffs post.
#endif
The compiler will ignore this code part because it does not meet the condition of the if statement. Meanwhile on the silverlight client side it tries to recompile the shared validator where it DOES meet the condition of the if-statement.
Like I said. This is NOT a clean way to do this. And you might have trouble with missing namespaces. You need to resolve them in the non-generated Validator.shared.cs to finally let it work in silverlight. If you do this right you can have the validation in silverlight with invoke operations. But not in your project with models and metadata like you would have with Jeff's post.
Edit: I found a cleaner and better way
you can create a partial class on the silverlight client side and doing the following
public partial class User
{
partial void OnUserNameChanging(string value)
{
//must be new to check for this validation rule
if(EntityState == EntityState.New)
{
var ctx = new UserContext();
ctx.IsValidUserName(value).Completed += (s, args) =>
{
InvokeOperation invop = (InvokeOperation) s;
bool isValid = (bool) invop.Value;
if(!isValid)
{
ValidationResult error = new ValidationResult(
"Username already exists",
new string[] {"UserName"});
ValidationErrors.Add(error;
}
};
}
}
}
This is a method generated by WCF RIA Services and can be easily partialled and you can add out-of-band validation like this. This is a much cleaner way to do this, but still this validation now only exists in the silverlight client side.
Hope this helps

WCF app . Structure map Objectfactory losing instance of an Object

I am trying to get WCF work with Structuremap. I took most of the code from Jimmy Bogard's blog
Integrating StructureMap with WCF
I am reusing all my objects (Domain, NHIBERNATE , Service layer ) that was created for ASP.nET MVC application.I have a separate IoC Library where I bootstrap all regisrties
I have added global.asax and in Application Start Event this is what I have :
var webServiceRegistry = new WebServiceRegistry();
var bootStrapper = new IoC.BootStrapper();
bootStrapper.Start();
WebServiceRegistry is a normal class and in its constructor I have the following:
var appUser = new AppUser
{
Role = userDto.Role,
Email = userDto.Email,
Id = userDto.Id,
CompanyName = dealerDto.Name,
Name = userDto.Name,
IsImpersonated = false,
Impersonater = Guid.Empty
};
System.Threading.Thread.CurrentPrincipal = System.Web.HttpContext.Current.User = appUser;
ObjectFactory.Configure(x => x.For<IAppUser>().Use(appUser));
Everything goes fine - no errors
Things start to fail when my my wcf service class is being instantiated which has a Domainservice object (IDomainServiceObject domainServiceObject) in constructor with 202 error.
The failure is in DomainService Registry where it fails to get default instance of IAppUser
Edit
Initial problem was in that I had error in constructor of StructureMapServiceHost.
Now I dont get any error but I am not getting the instance of IAppUser that I inject. In place of that it creates a new instance of AppUser.
Do anyone have a clue as to why this happens?
I have tried adding HttpContextScope , HybridHttpOrThreadLocalScoped and still same result
I have conformed that at the time all registries are called in bootstrapper IAppUser instance is the once that was provided with values in it (and not created by StructureMap)
In Application Start it does create the whole graph .
As soon as it enters webmethod ILMSUser is a new instance.
Thank you
Since you want the same object back every time you can use singleton
this.For<IAppUser>().Singleton().Use(appUser);

Adding content to silver light controls from database using wcf ria services

I am trying to retrieve strings from the database and add it as content to the controls on my page before it loads(somewhat like a custom localization). I retrieve my strings from the database uing ria services as follows:
**
Web.DomainService1 context = new Web.DomainService1();
LoadOperation<Web.LocalizationTab>LoadOp=context.Load(context.GetLocalizationTabsQuery(currentCulture, moduleName));
Dictionary<string, string> localizationDictonary = new Dictionary<string, string>();
List<Web.LocalizationTab> localList = new List<Web.LocalizationTab>();
LoadOp.Completed += (s, e) =>
{
localList = LoadOp.Entities.ToList<Web.LocalizationTab>();
//System.Windows.MessageBox.Show(localList.Count.ToString());
foreach (Web.LocalizationTab item in localList)
{
// var control = this.FindName(item.Control_ID.ToString());
if (!localizationDictonary.ContainsKey(item.Control_ID))
{
localizationDictonary.Add(item.Control_ID, item.Control_Text);
}
}
};**
This piece of code is in a separate class called utilities.cs.
now in my Mainpage.xaml.cs i need to get this dictionary with values and then set the controls with the strings from the dictionary.
my problem is that when i do the following in the constructor of Mainpage.xaml.cs:
utilities.getDict(ModuleName);
button1.Content = localizationDictonary["button1"].ToString();
i get an exception as the dictionary doesnt contain values at that point of time.. The load completed event of getting data from the database gets fired only after my constructor is exited. now how do i go about setting my controls automatically in this particular scenario??
Try loading your dictionary in the Silverlight Application class StartUp event. This should ensure that your Dictionary is loaded (and could be placed in the Application Resources for retrieval elsewhere in your application).
You'll need to wait for the GetLocalizationTabsQuery to complete before instantiating your MainPage. In your App.xaml.cs file, you can start the query, add a query completed handler, and only create the MainPage when the query completed handler gets called.