Making global resources public

Regarding the previous post you may want to use strings from a global resources .resx file (App_GlobalResources) as error messages or descriptions in your attributes. This won’t work because global resource files are by default marked as internal and so you can’t access them from your controller or your model. Also the little “Access Modifier” dropdown menu in the resource editor is grayed out, so you can’t change the access level without further ado.

However there’s a way to change the access rights without editing the designer file by hand.

You just have to open the file properties of the resource file and change the “Custom Tool” from “GlobalResourceProxyGenerator” to “PublicResXFileCodeGenerator”, which is the default Tool for local resource files. Next you have to change the “Build Action” to “Embedded Resource”. You may also want to assign a proper Custom Tool Namespace like “Resources” in order to access the file properly, but this isn’t necessary.

Now rebuild the project and you should notice that the resource access is now public and you can access its contents from anywhere in your project.

E.g. like that:


[Required(ErrorMessageResourceType = typeof(Resources.Resource1), ErrorMessageResourceName = "Comment_Text")]
public string Myproperty { get; set; }

Advertisements

Localizing DisplayNameAttribute

When trying to put a localized string from a resource file (.resx) into the DisplayName Attribute you will get a the following build error:

An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type

An easy way to solve this is to create your own custom DisplayName Attribute. To do that we will create a new class DisplayNameLocalizedAttribute and derive it from the original DisplayNameAttribute and then override the base DisplayName property to return the resource object we want.


public class DisplayNameLocalizedAttribute : DisplayNameAttribute
{
    private readonly string m_ResourceName;
    private readonly string m_ClassName;
    public DisplayNameLocalizedAttribute(string className, string resourceName)
     {
         m_ResourceName = resourceName;
         m_ClassName = className;
     }

     public override string DisplayName
     {
         get
         {
             // get and return the resource object
             return HttpContext.GetGlobalResourceObject(
                    m_ClassName,
                    m_ResourceName,
                    Thread.CurrentThread.CurrentCulture).ToString();
         }
     }
}

Use:


    [DisplayNameLocalized("MyResource", "MyString")]
    public string MyProperty { get; set; }

As you can see, we are now able to pass in a resource name (className) and a resource key (resourceName) and will get the correct string returned. The same procedure should work with similar Attributes such as the DescriptionAttribute.

Update 1: Another way would be by using reflection.

public class DisplayNameLocalizedAttribute : DisplayNameAttribute
{
    private string _displayName;

    public DisplayNameLocalizedAttribute(string resourceKey, Type resourceType)
    {
        PropertyInfo propInfo = resourceType.GetProperty(resourceKey,
                System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static);
        _displayName = (string)propInfo.GetValue(propInfo.DeclaringType, null);
    }

    public override string DisplayName
    {
        get
        {
            return _displayName;
        }
    }
}

Use:

[DisplayNameLocalized("MyString", typeof(MyResource))]
public string MyProperty { get; set; }

Update 2: In .NET 4.0 this procedure seems to be obsolete as you can just use the new [Display]-Attribute and specify the “ResourceType” and “Name” Properties:

[Display(ResourceType = typeof(MyResource), Name = "MyString")]
public string MyProperty { get; set; }

Unit testing LINQ to SQL in ASP.NET MVC Web Applications

If you want to unit test LINQ to SQL statements in an ASP.NET MVC application you most likely will get a NullReference Exception that is thrown when trying to instantiate the DataContext. That’s because the standard constructor of the DataContext is trying to get the database connection string from the current .config file.

To solve that problem, without changing the code and specifying the connection string explicitly in the DataContext’ constructor, you just have to add an App.config file to your test project and copy the contents (or at least the configuration -> connection strings section) from the Web.config to it.

Second you have to adjust the AttachDbFilename property in the connection string to point again to your actual .mdf file. Since you cannot use relative paths in the connection string, you have to put in the full path. Here’s an interesting excerpt from MSDN:

When DataDirectory is used, the resulting file path cannot be higher in the directory structure than the directory pointed to by the substitution string. For example, if the fully expanded DataDirectory is C:\AppDirectory\app_data, then the sample connection string shown above works because it is below c:\AppDirectory. However, attempting to specify DataDirectory as |DataDirectory|\..\data will result in an error because \data is not a subdirectory of \AppDirectory.

However we have the option to change where the |DataDirectory| shall point to. So you don’t have to touch the connection string directly. To do that you just have to call the AppDomain.SetData Method in the test project.


AppDomain.CurrentDomain.SetData("DataDirectory", @"C:\Projects\MyProject\MyProject\App_Data");

%d bloggers like this: