Override a resource from standard assembly in ASP.NET - asp.net

I want to override a string from a System.ComponentModel.DataAnnotations for an ASP.NET project. Do I need to make a satellite assembly, messing with custom build tasks, al.exe etc.? Even if yes, I couldn't find how to convert .resx to .resources to feed it to al.exe. And if no, where to put the .resx. and how to name it?
UPD: To make it clear: I wanted to use a custom resource string instead of one from the default resource from the assembly. I didn't want to make changes in the every place that uses that string. After all, the resources exist just for overriding them.

Phil Haack has an excellent article Localizing ASP.Net MVC Validation which specifically guides you through overriding your strings. This article applies more to DataAnnotations than it does ASP.net MVC. So, this will help however your are using DataAnnotattions.
Below I have listed the simplest steps to add Localized Resources in Visual Studio.
Open the Project Properties dialog.
Select the Resources tab.
Click to create a new default
resources file.
This will create two files in your Properties folder.
Resources.resx
Resources.Designer.cs
When Resources.resx has
opened, change it's Access Modifier
to Public.
Add your strings.
To add additional resource files for specific cultures you will need to.
Right click your Project in the
Solution Explorer.
Select Add -> New Item -> Resource
File.
Name it Resources.en-us.resx.
(replace 'en-us' with appropriate
code)
Click Add
Drag it into the Properties folder.
Open Resources.en-us.resx and change it's Access Modifier
to Public.
Add your strings.
Repeat for each Culture you need to
support.
During the build VS will convert the .resx files to .resource files and build wrapper classes for you. You can then access via the namespace YourAssembly.Properties.Resources.
With this using statement.
using YourAssembly.Properties;
You can decorate with attributes like this:
[Required(ErrorMessageResourceType = typeof(Resources), ErrorMessageResourceName = "MyStringName")]
Note: I used the Properties folder for consistency. To use the App_GlobalResources move your .resx files there and change your using statement to match the directory name. Like this:
using YourAssembly.App_GlobalResources;
Edit: The closest that you can get to Strongly Typed resource names would be to do something like this:
public class ResourceNames
{
public const string EmailRequired = "EmailRequired";
}
You can then decorate with attributes like this.
[Required(ErrorMessageResourceType = typeof(Resources), ErrorMessageResourceName = ResourceNames.EmailRequired)]
To enable automatic client culture detection add the globalizationsection to the web.config file.
<configuration>
<system.web>
<globalization enableClientBasedCulture="true" culture="auto:en-us" uiCulture="auto:en-us"/>
</system.web>
<configuration>
Here I have enabled a client based culture and set the culture and the uiculture to "auto" with a default of "en-us".
Creating Separate Satellite Assemblies:
The MSDN Creating Satellite Assemblies article will help as well.
If you are new to satellite assemblies make sure you read Packaging and Deploying Resources.
When creating satellite assemblies in the past, I have found it useful to use VS build events. These are the steps I would take.
Create a separate Class Library project in my solution.
Create or Add my .resx files to this project.
Add a Post-Build Event to the Project Properties dialog. (Like the one below)
Sample VS Post-Build Script:
set RESGEN="C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\resgen.exe"
set LINKER="C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\al.exe"
set ASSEMBLY=$(TargetName)
set SOURCEDIR=$(ProjectDir)
Set OUTDIR=$(TargetDir)
REM Build Default Culture Resources (en)
%RESGEN% %SOURCEDIR%en\%ASSEMBLY%.en.resx %SOURCEDIR%en\%ASSEMBLY%.resources
REM Embed Default Culture
%LINKER% /t:lib /embed:%SOURCEDIR%en\%ASSEMBLY%.resources /culture:en /out:%OUTDIR%%ASSEMBLY%.resources.dll
REM Embed English Culture
IF NOT EXIST %OUTDIR%en\ MKDIR $%OUTDIR%en\
%LINKER% /t:lib /embed:%SOURCEDIR%en\%ASSEMBLY%.resources /culture:en /out:%OUTDIR%en\%ASSEMBLY%.resources.dll
REM These are just a byproduct of using the project build event to run the resource build script
IF EXIST %OUTDIR%%ASSEMBLY%.dll DEL %OUTDIR%%ASSEMBLY%.dll
IF EXIST %OUTDIR%%ASSEMBLY%.pdb DEL %OUTDIR%%ASSEMBLY%.pdb
If you would prefer not to use ResGen.exe to convert your .resx files, you could do something like this.
using System;
using System.Collections;
using System.IO;
using System.Resources;
namespace ResXConverter
{
public class ResxToResource
{
public void Convert(string resxPath, string resourcePath)
{
using (ResXResourceReader resxReader = new ResXResourceReader(resxPath))
using (IResourceWriter resWriter = new ResourceWriter(
new FileStream(resourcePath, FileMode.Create, FileAccess.Write)))
{
foreach (DictionaryEntry entry in resxReader)
{
resWriter.AddResource(entry.Key.ToString(), entry.Value);
}
resWriter.Generate();
resWriter.Close();
}
}
}
}
One of the potential draw backs to doing the conversion this way is the need to reference the System.Windows.Forms.dll. You will still need to use Assembly Linker.
Edit: As wRAR has reminded us if you are signing your assemblies your keys must match.

While this is strange, especially for people familiar with open source localization technologies, one cannot build a satellite assembly for any system assembly or even a 3rd-party signed one:
If your main assembly uses strong
naming, satellite assemblies must be
signed with the same private key as
the main assembly. If the
public/private key pair does not match
between the main and satellite
assemblies, your resources will not be
loaded.
Whether the same is possible automatically, but without a satellite assembly, is unknown, though I doubt that.

If the server doesn't have .NET language packs installed then no matter what CurrentUICulture is set to, you'll always get English in DataAnnotations validation messages. This epic hack works for us.
Go to "Microsoft .NET Framework 4.6.1 Language Pack" download page https://www.microsoft.com/en-us/download/details.aspx?id=49977
Select language and download
Extract NDP461-KB3102436-x86-x64-AllOS-{LANG}.exe with 7-Zip
Extract CAB file x64-Windows10.0-KB3102502-x64.cab with 7-Zip
Locate "msil_system.componentmod..notations.resources_...."
... in which you'll find "system.componentmodel.dataannotations.resources.dll"
Open .resources.dll with ILSpy, locate Resources and click Save button above String Table to save as System.ComponentModel.DataAnnotations.Resources.DataAnnotationsResources.{LANGUAGE}.resources
Add to your project under say a "Resources"
Ensure the files Build Action property of the resources files is set to "Embedded Resource"
Then in a PreStart method of your project you overwrite the System.ComponentModel.DataAnnotations.Resources.DataAnnotationsResources.resourceMan private static field (told you it was a hack) with the ones you have in your project.
using System;
using System.Linq;
using System.Reflection;
using System.Resources;
[assembly: WebActivator.PreApplicationStartMethod(typeof(ResourceManagerUtil), nameof(ResourceManagerUtil.PreStart))]
class ResourceManagerUtil
{
public static void PreStart()
{
initDataAnnotationsResourceManager();
}
/// <summary>
/// If the server doesn't have .NET language packs installed then no matter what CurrentUICulture is set to, you'll always get English in
/// DataAnnotations validation messages. Here we override DataAnnotationsResources to use a ResourceManager that uses language .resources
/// files embedded in this assembly.
/// </summary>
static void initDataAnnotationsResourceManager()
{
var embeddedResourceNamespace = "<YourProjectDefaultNamespace>.<FolderYouSavedResourcesFilesIn>";
var dataAnnotationsResourcesName = "System.ComponentModel.DataAnnotations.Resources.DataAnnotationsResources";
var thisAssembly = typeof(ResourceManagerUtil).Assembly;
var dataAnnotationsAssembly = typeof(System.ComponentModel.DataAnnotations.ValidationAttribute).Assembly;
var resourceManager = new ResourceManager(embeddedResourceNamespace + "." + dataAnnotationsResourcesName, thisAssembly);
// Set internal field `DataAnnotationsResources.resourceMan`
var dataAnnotationsResourcesType = dataAnnotationsAssembly.GetType(dataAnnotationsResourcesName);
var resmanProp = dataAnnotationsResourcesType.GetField("resourceMan", BindingFlags.NonPublic | BindingFlags.Static);
resmanProp.SetValue(null, resourceManager);
}
}

Assuming that you want to override the default error message strings in the validation attributes, you can do that by setting the ErrorMessageResourceName and the ErrorMessageResourceType properties like this:
[Required(ErrorMessageResourceName = "Required_Username", ErrorMessageResourceType = typeof(MyResourceFile)]
public string Username { get; set; }
You can create a resource file called MyResourceFile.resx that contains Required_Username with the error message you want.
Hope this helps.

I want to provide an answer with the same idea as by Duncan Smart, but for .NET Core 2.2 instead of .NET Framework 4.x.
Here it is.
using System;
using System.Linq;
using System.Reflection;
using System.Resources;
public static class ResourceManagerHack
{
/// <summary>
/// If the server doesn't have .NET language packs installed then no matter what CurrentUICulture is set to, you'll always get English in
/// DataAnnotations validation messages. Here we override DataAnnotationsResources to use a ResourceManager that uses language .resources
/// files embedded in this assembly.
/// </summary>
public static void OverrideComponentModelAnnotationsResourceManager()
{
EnsureAssemblyIsLoaded();
FieldInfo resourceManagerFieldInfo = GetResourceManagerFieldInfo();
ResourceManager resourceManager = GetNewResourceManager();
resourceManagerFieldInfo.SetValue(null, resourceManager);
}
private static FieldInfo GetResourceManagerFieldInfo()
{
var srAssembly = AppDomain.CurrentDomain
.GetAssemblies()
.First(assembly => assembly.FullName.StartsWith("System.ComponentModel.Annotations,"));
var srType = srAssembly.GetType("System.SR");
return srType.GetField("s_resourceManager", BindingFlags.Static | BindingFlags.NonPublic);
}
internal static ResourceManager GetNewResourceManager()
{
return new ResourceManager($"{typeof(<YourResource>).Namespace}.Strings", typeof(<YourResource>).Assembly);
}
private static void EnsureAssemblyIsLoaded()
{
var _ = typeof(System.ComponentModel.DataAnnotations.RequiredAttribute);
}
}
And I call this like so:
public static void Main(string[] args)
{
ResourceManagerHack.OverrideComponentModelAnnotationsResourceManager();
CreateWebHostBuilder(args).Build().Run();
}
Furthermore, I created a ~/Resources/<YourResource>.resx file and populated it with the default values and changed them at will. Lastly I created a public empty class <YourResource>.

Related

The resource name is not a valid identifier for devexpress gridview

I want to translate DevExpress GridView filter text from English to Persian.
I added a .resx file into asp.net project and fill it.
But I have some problem.
The resource name ASPxGridViewStringId.GroupPanel is not a valid identifier.
After added this .resx file, and run project, not happen any thing.
How can I fix this problem?
OK!!!! I do it, according to the similar question in this page, I added two ".resx" into App_GlobalResources folder.
one of them is default lang "English" and other is target lang "Persian".
DevExpress_Web_ASPxGridView_v15_1.rexs = default
DevExpress_Web_ASPxGridView_v15_1.Fa.resx = target
and translate it's values to persian.
finally change "UICULTURE" to "fa-ir".
<%# Page Language="C#" AutoEventWireup="true" CodeFile="Default2.aspx.cs" Inherits="Default2" UICulture="fa-ir" %>
Thanks God :) .
i put this answer in Devexpress
You can take a look at DevExpress Documentation topic called «Localization»:
DevExpress ASP.NET controls can be localized using one of the
following methods.
Via Satellite Resource Assemblies.This is the most universal approach, commonly used for all DevExpress products and under all
supported technologies.
Localizing ASP.NET Controls via Localizer Objects.This approach is appropriate if you are developing an application for a single culture
and you wish to translate resources into a specific language, or you
wish to change the default resources (for the English-US culture) to
their equivalents.
Satellite Resource Assemblies
You can create your translation via Localization Service:
After completing the translation you can save it, download it as the assembly and add it into your project:
To add localized resources to your application, copy satellite
assemblies from the unpacked archive to the corresponding subfolders
of your application's directory. The subfolder's name is the culture's
abbreviation. For example, to include German assemblies, copy the
folder named de from the \Bin\Framework\ directory to the Bin
directory of your application.
Localizer Objects
You can create custom localizer object for your translation and use it in your application. For GridView your object must be derived from ASPxGridViewLocalizer class or from ASPxGridViewResLocalizer class. In your descendant class you must ovveride XtraLocalizer(T).GetLocalizedString method. This method is using ASPxGridViewStringId enumeration as parameter.
Here is example:
public class CustomGridViewLocalizer : ASPxGridViewResLocalizer
{
public static void Activate()
{
var localizer = new CustomGridViewLocalizer();
var provider = new DefaultActiveLocalizerProvider<ASPxGridViewStringId>(localizer);
SetActiveLocalizerProvider(provider);
}
public override string GetLocalizedString(ASPxGridViewStringId id)
{
switch (id)
{
case ASPxGridViewStringId.AutoFilterBeginsWith:
return "شروع با";
case ASPxGridViewStringId.AutoFilterContains:
return "شامل";
case ASPxGridViewStringId.AutoFilterDoesNotContain:
return "شامل نمی شود";
case ASPxGridViewStringId.AutoFilterEndsWith:
return "به پایان می رسد با";
case ASPxGridViewStringId.AutoFilterEquals:
return "برابر";
case ASPxGridViewStringId.AutoFilterNotEqual:
return "نا برابر";
default:
return base.GetLocalizedString(id);
}
}
}
You need to activate this object in the Application_Start event handler within the Global.asax file:
protected void Application_Start(object sender, EventArgs e)
{
CustomGridViewLocalizer.Activate();
}

ASP.NET MEF Compose all class parts

// In Global asax
DirectoryCatalog catalog = new DirectoryCatalog(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin"));
CompositionContainer container = new CompositionContainer(catalog);
// do not want to compose at every instance when use Import attribute.
container.ComposeParts(this);
// add all external library (.dll)
container.ComposeParts(container);
// Index.aspx page
public partial Index: Page{
[Import]
private c1 object1;
protected void OnLoadPage
{
if (object1== null)
thrown Error("mef failed, why")
}
}
// External library Library1
[Export(typeOf(c1))]
public class c1{}
// External library Library2
[Export(typeOf(c2))]
public class c2{}
why object1 is null?
How to compose all classes from Global asax or simple to make some asp.net bootstrap lazy loading?
I do not want in every constructor to compose part. How to make all assembly or classes to be compose at the beginning in Global asax? All my [Export] are in external library classes. Main site project have reference to all .dll projects.
I am not sure the my question is correct, sorry for that.
Project is simple asp.net (no MVC) with external library with user controls.

Adding Wcf Service does not allow namespace access

I have created a wcf service called ServiceIRE. I add a service reference to my project by right clicking on th eproject in solution explorer and clicking add Service Reference. I am able to discover my created service and click ok after specifying the namespace ServiceReference1. All files seem to be generated properly.
I then go to the codebehind and try to add a using statment "using ServiceReference1;". This is unrecognized by the file even though the namespace apparently exists in the same project.
Any help would be greatly appreciated.
ServiceIRE.cs
namespace FakeIREServiceLibrary
{
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class ServiceIRE : IServiceIRE
{
public string GetData()
{
Random rnd = new Random();
var randomNumber = rnd.Next(10);
Random rndID = new Random();
var randomNumberID = rnd.Next(10000);
Thread.Sleep(randomNumber*1000);
return string.Format("Thread # {0} returned in {1} nanoseconds", randomNumberID, rnd.Next());
}
}
IServiceIRE.cs
namespace FakeIREServiceLibrary
{
[ServiceContract]
public interface IServiceIRE
{
[OperationContract]
string GetData();
}
}
Page.aspx.cs
using ServiceReference1; //THIS IS NOT RECOGNIZED
Thank you in advance :)
What's the default namespace for your project?
I believe you have to do something like "using DefaultNamespaceForProject.ServiceReference1;"
You can find the default namespace by right clicking your project.. clicking Properties, and then going to the Application tab.
You can find the namespace of the service client generated by Visual Studio by looking at the Reference.cs file within the Service Reference after you show all files for that project. The default is [Project namespace].[Namespace specified].
It's possible that it did NOT generate the file because of an error. That may be the behavior you're seeing.
I would also encourage you to NOT use Add Service Reference, but to instead use a ChannelFactory or build a reusable service client.

Calling methods in App_Code from an ASP.NET Custom Control

I am using VS2008 and have a solution file which contains 1 Website and 1 Class Library Project.
The Class Library is a Custom Control which derives from Label. The Website contains a reference to the control - it builds successfully and the compiled .dll gets added to the Website's /bin folder. I can then use it in any of the website's .aspx pages without error.
What I cannot do, however, is reference any of the Website's data access methods that are in static classes in /App_Code from within the custom control.
I don't want to repeat the website data access logic all over again within the custom control when I know it will already exist in the website - I just want to be able to call a method from a class in /App_Code . If I try and reference anything in /App_Code from within the Class Library, it fails to build and says it can't find the Namespace or that it doesn't exist in the current context.
How can I achieve this so that the Custom Control builds as a standalone control, but can make use of classes in the website it gets used in? Delegates, possibly? Was hoping it might be more straightforward than that.
EDIT: I should add that the reason the control is in a separate Class Library is so that I can include JavaScript as an embedded resource within the Control. So when it's used in a .aspx page, it adds a WebResource.axd? style link to the page instead of a load of plaintext JavaScript in the <head> section.
EDIT 2:
In the website App_Code folder, I have a static class that handles data access (snippet):
[DataObject]
public static class DBAccess
{
[DataObjectMethod(DataObjectMethodType.Select)]
public static DataTable GetSomeData(Int32 SomeParam, DateTime OtherParam)
{
SqlConnection cn = SqlLibrary.GetConnection(DBConnectionString);
DataTable _dt;
SqlLibrary.SProcFill(out _dt, cn, "usp_SomeData_Select", SomeParam, OtherParam);
return _dt;
}
}
In the Class Library's custom control (which I want to build independently of the website's existence, yet be capable of calling its methods when used as a control in an .aspx page):
namespace MyCustomControls
{
public class StatusControl : Label
{
private Int32 _someProperty = -1;
private DateTime _otherProperty = DateTime.Now;
public StatusControl()
{
//some constructor logic
}
public void FetchData()
{
//what I'd **like** to do here is:
DBAccess.GetSomeData(_someProperty, _otherProperty);
//...but DBAccess isn't "visible" to this control at build time
}
}
}

Use ASP.NET Resource strings from within javascript files

How would one get resx resource strings into javascript code stored in a .js file?
If your javascript is in a script block in the markup, you can use this syntax:
<%$Resources:Resource, FieldName %>
and it will parse the resource value in as it renders the page... Unfortunately, that will only be parsed if the javascript appears in the body of the page. In an external .js file referenced in a <script> tag, those server tags obviously never get parsed.
I don't want to have to write a ScriptService to return those resources or anything like that, since they don't change after the page is rendered so it's a waste to have something that active.
One possibility could be to write an ashx handler and point the <script> tags to that, but I'm still not sure how I would read in the .js files and parse any server tags like that before streaming the text to the client. Is there a line of code I can run that will do that task similarly to the ASP.NET parser?
Or does anyone have any other suggestions?
Here is my solution for now. I am sure I will need to make it more versatile in the future... but so far this is good.
using System.Collections;
using System.Linq;
using System.Resources;
using System.Web.Mvc;
using System.Web.Script.Serialization;
public class ResourcesController : Controller
{
private static readonly JavaScriptSerializer Serializer = new JavaScriptSerializer();
public ActionResult GetResourcesJavaScript(string resxFileName)
{
var resourceDictionary = new ResXResourceReader(Server.MapPath("~/App_GlobalResources/" + resxFileName + ".resx"))
.Cast<DictionaryEntry>()
.ToDictionary(entry => entry.Key.ToString(), entry => entry.Value.ToString());
var json = Serializer.Serialize(resourceDictionary);
var javaScript = string.Format("window.Resources = window.Resources || {{}}; window.Resources.{0} = {1};", resxFileName, json);
return JavaScript(javaScript);
}
}
// In the RegisterRoutes method in Global.asax:
routes.MapRoute("Resources", "resources/{resxFileName}.js", new { controller = "Resources", action = "GetResourcesJavaScript" });
So I can do
<script src="/resources/Foo.js"></script>
and then my scripts can reference e.g. window.Resources.Foo.Bar and get a string.
There's no native support for this.
I built a JavaScriptResourceHandler a while ago that can serve Serverside resources into the client page via objects where each property on the object represents a localization resource id and its value. You can check this out and download it from this blog post:
http://www.west-wind.com/Weblog/posts/698097.aspx
I've been using this extensively in a number of apps and it works well. The main win on this is that you can localize your resources in one place (Resx or in my case a custom ResourceProvider using a database) rather than having to have multiple localization schemes.
whereas "Common" is the name of the resource file and Msg1 is the fieldname. This also works for culture changes.
Partial Javascript...:
messages:
{
<%=txtRequiredField.UniqueID %>:{
required: "<%=Resources.Common.Msg1 %>",
maxlength: "Only 50 character allowed in required field."
}
}
In a nutshell, make ASP.NET serve javascript rather than HTML for a specific page. Cleanest if done as a custom IHttpHandler, but in a pinch a page will do, just remember to:
1) Clear out all the ASP.NET stuff and make it look like a JS file.
2) Set the content-type to "text/javascript" in the codebehind.
Once you have a script like this setup, you can then create a client-side copy of your resources that other client-side scripts can reference from your app.
If you have your resources in a separate assembly you can use the ResourceSet instead of the filename. Building on #Domenics great answer:
public class ResourcesController : Controller
{
private static readonly JavaScriptSerializer Serializer = new JavaScriptSerializer();
public ActionResult GetResourcesJavaScript()
{
// This avoids the file path dependency.
ResourceSet resourceSet = MyResource.ResourceManager.GetResourceSet(CultureInfo.CurrentUICulture, true, true);
// Create dictionary.
var resourceDictionary = resourceSet
.Cast<DictionaryEntry>()
.ToDictionary(entry => entry.Key.ToString(), entry => entry.Value.ToString());
var json = Serializer.Serialize(resourceDictionary);
var javaScript = string.Format("window.Resources = window.Resources || {{}}; window.Resources.resources = {1};", json);
return JavaScript(javaScript);
}
}
The downside is that this will not enable more than one resource-file per action. In that way #Domenics answer is more generic and reusable.
You may also consider using OutputCache, since the resource won't change a lot between requests.
[OutputCache(Duration = 3600, Location = OutputCacheLocation.ServerAndClient)]
public ActionResult GetResourcesJavaScript()
{
// Logic here...
}
http://www.asp.net/mvc/overview/older-versions-1/controllers-and-routing/improving-performance-with-output-caching-cs
I usually pass the resource string as a parameter to whatever javascript function I'm calling, that way I can continue to use the expression syntax in the HTML.
I the brown field application I'm working on we have an xslt that transforms the resx file into a javascript file as part of the build process. This works well since this is a web application. I'm not sure if the original question is a web application.
use a hidden field to hold the resource string value and then access the field value in javascript
for example :
" />
var todayString= $("input[name=TodayString][type=hidden]").val();
Add the function in the BasePage class:
protected string GetLanguageText(string _key)
{
System.Resources.ResourceManager _resourceTemp = new System.Resources.ResourceManager("Resources.Language", System.Reflection.Assembly.Load("App_GlobalResources"));
return _resourceTemp.GetString(_key);
}
Javascript:
var _resurceValue = "<%=GetLanguageText("UserName")%>";
or direct use:
var _resurceValue = "<%= Resources.Language.UserName %>";
Note:
The Language is my resouce name. Exam: Language.resx and Language.en-US.resx

Resources