Sunday, June 9, 2013

Solve Cannot retrieve property 'Name' because localization failed. Type 'YourResourceFile' is not public or does not contain a public static string property with the name 'YourItemName'.

Data Annotations are a great thing. They allow you to add information to your model quickly and easily. However, if you have an application that is designed for multiple cultures, you cannot add a Name to your annotation and have it display appropriately for other cultures. It is easy to use App_GlobalResources to hold your global strings, however, you may encounter the following message when using an App_GlobalResources folder in Asp.Net MVC -

        [Required(
            ErrorMessageResourceName = "Required_PleaseSelectA",
            ErrorMessageResourceType = typeof(StringResources))]
        [StringLength(100,
            ErrorMessageResourceName = "Length_YourMustBeACombination",
            ErrorMessageResourceType = typeof(StringResources),
            MinimumLength = 6)]
        [Display(
            ResourceType = typeof(LocalizedResources),
            Name = "CategoryName",
            Description = "CategoryName")]
        public string CategoryName { get; set; }
Cannot retrieve property 'Name' because localization failed.  Type 'Resources.StringResources' is not public or does not contain a public static string property with the name 'CategoryName'.

In this particular example, I set up resources for a property called CategoryName.
This error is VERY simple to solve. The resource file needs to be made public.
To do this, you will need to change the TYPE of tool used to generate the resource file.

In Asp.Net MVC 4, when you add a App_GlobalResources folder and create a resource file in it, by default the resource file is set up as internal and given the GlobalResourceProxyGenerator as a tool. That internal designation creates a problem for the DisplayAttribute annotation because it checks to see if the ResourceType is visible. In fact, it actually uses Type.IsVisible. This creates a problem because the internal class is NOT visible to it.

Now here is an interesting thing to note. The RequiredAttribute and StringAttribute both inherit from ValidationAttribute. The DisplayAttribute does NOT. The Required and String Attributes utilize a different mechanism to connect with the resource files. You can see subtle differences when you look at how the items are set up again -

        [Required(
            ErrorMessageResourceName = "Required_PleaseSelectA",
            ErrorMessageResourceType = typeof(StringResources))]
        [StringLength(100,
            ErrorMessageResourceName = "Length_YourMustBeACombination",
            ErrorMessageResourceType = typeof(StringResources),
            MinimumLength = 6)]

        [Display(
            ResourceType = typeof(StringResources),
            Name = "CategoryName",
            Description = "CategoryName")]

        public string CategoryName { get; set; }
Notice that each resource file in the photo of App_GlobalResources above has a .cs file attached to it.When you click on that resource file,  you see that all of the classes are internal. If you change the CustomTool to PublicResXFileCodeGenerator and rebuild your application, you will see the .cs file go from internal to public and your DisplayAttribute will be able to connect with the resource file now. In fact, if you click on the .cs file to open it AND change then go over to properties and change the CustomTool, all of the items in the file will automagically change from internal to public.

Smooches,

Kila Morton





Saturday, June 8, 2013

Solving To call this method, the “Membership.Provider” property must be an instance of “ExtendedMembershipProvider”

When using Asp.Net MVC 4 and hosting your website with a hosting provider like TheDomainConnection or Godaddy, you may encounter a problem that seems strange. 

When testing locally on your dev machine not on the host, you  may be able to create accounts just fine. However, after you  load your site to your host,  you may get the error 
  
To call this method, the “Membership.Provider” property must be an instance of “ExtendedMembershipProvider”

You think to yourself "Self, why am I getting this error when this worked on my dev machine not more than a few moments ago?" The answer is interesting.

The error is happening because of this code located in the WebSecurity file of the WebMatrix.WebData dll -


        private static ExtendedMembershipProvider VerifyProvider()
        {
            ExtendedMembershipProvider provider = Membership.Provider as ExtendedMembershipProvider;

What is the significance of this? That Membership.Provider line is getting the membership information from your web.config and if you have not configured your system to use a provider that extends the ExtendedMembershipProvider class, the provider will not match and your application will not work. The first step to solving this problem is to add the following line to your web.config file -

  <appSettings>
    <add key="enableSimpleMembership" value="true" />
  </appSettings>

However, in some cases, your website host settings are actually going to override that setting in your web.config! That's right - your host configuration is going to override your own. This means that the setting above will be ignored and if your host is implementing a provider that is NOT an extension of the ExtendedMembershipProvider, you are going to experience the error. This happens because NONE of the old providers used with prior versions of Asp.Net/Asp.Net MVC are extensions of ExtendedMembershipProvider.
So how do you solve this problem?

Place the following code in your web.config file of the website you are hosting.


  <system.web>
    <roleManager enabled="true" defaultProvider="SimpleRoleProvider">
      <providers>
        <clear />
        <add name="SimpleRoleProvider" type="WebMatrix.WebData.SimpleRoleProvider, WebMatrix.WebData" />
      </providers>
    </roleManager>
    <membership defaultProvider="SimpleMembershipProvider">
      <providers>
        <clear />
        <add name="SimpleMembershipProvider" type="WebMatrix.WebData.SimpleMembershipProvider, WebMatrix.WebData" />
      </providers>
    </membership>
    <sessionState mode="InProc" customProvider="DefaultSessionProvider">
      <providers>
        <add name="DefaultSessionProvider" type="System.Web.Providers.DefaultSessionStateProvider, System.Web.Providers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" connectionStringName="DefaultConnection" />
      </providers>
    </sessionState>
  </system.web>
This will override your host's settings for your website and get your website working!
Smooches, 
Kila Morton