tags:

views:

196

answers:

5

I need to create base class like the following code.

public class ResourceBase
{
    protected static IDictionary<string, XDocument> resources;

    protected static IDictionary<string, XDocument> Resources
    {
        get
        {
            if (resources == null)
            {
                // cache XDocument instance in resources variable seperate by culture name.
                // load resx file to XDocument
            }

            return resources;                
        }
    }

    protected static string GetString(string resourceKey)
    {
        return GetString(resourceKey, System.Threading.Thread.CurrentThread.CurrentUICulture.Name);
    }

    protected static string GetString(string resourceKey, string cultureName)
    {
        // get data from XDocument instance
        var result = (
                        from rs in Resources
                        let node = rs.Value.Root.Elements(XName.Get("data")).SingleOrDefault<XElement>(x => x.Attribute(XName.Get("name")).Value == resourceKey)
                        where
                                (rs.Key == DEFAULT_CULTUREKEY || cultureName == rs.Key) &&
                                node != null
                        orderby cultureName == rs.Key descending
                        select node.Element(XName.Get("value"))
                     ).FirstOrDefault<XElement>();

        return result.Value;
    }
}

Next, I create child class like the following code.

public class MainResource : ResourceBase
{
    public static string AppName
    {
        return GetString("AppName");
    }
}

public class OtherResource : ResourceBase
{
    public static string OtherName
    {
        return GetString("OtherName");
    }
}

I have some problem because resource variable in base class. All child classes use some Resource variable. So, they always use same cached XDocument instance. Do you have any idea for fixing my sourcecode?

PS. I found some Attribute like ContextStaticAttribute which indicates that the value of a static field is unique for a particular context. I think a particular context should be difference thread. So, I can't use it for solving this question.

Thanks,

+3  A: 

Make the base class abstract and do not provide an implementation for Resources. Then the derived class can provide it's own resource to the base class GetString method.

Robert
It doesn't work! Because it still cache XDocument of the first called child class.
Soul_Master
you moved the resources, but not the getter. You have to move the getter to the child class
Robert
It doesn't my requirement. Because I need to share logic in base class only and derived classes have only properties for get resource value.
Soul_Master
+3  A: 

This seems to be calling for a Singleton implementation. The reason for your base class is to share logic, correct? Instead of making everything static, implement the Singleton pattern so that each one of your child classes will have their own instance, all sharing the logic from your base class.

This will allow them to have independent caching while sharing behaviour logic.

Jay S
But it's impossible to get resource value from child class. Moreover, I can't use "abstract" keyword for any static field or property.
Soul_Master
You should do a quick google search on Singleton, or just search this site. Unless you have some requirement to use static methods to do everything, then you should be good.
Jay S
A: 

Seems like you need to cache each time for your child classes. When you cache XDocument, you need to provide a key that is different across your child classes. You could add this to your Base class

abstract string ResourceName { get; };
protected static IDictionary<string, XDocument> Resources
{
    get
    {
        if (resources == null)
        {
            //Cache the XDocument with a key based on ResourceName.
        }

        return resources;                
    }
}

And then your MainResource has

string ResourceName { get { return "AppName"; } }

public static string AppName
{
    return GetString(ResourceName);
}
s_hewitt
But I have a lot of resource name and it doesn't work. Because it still use same resource instance.
Soul_Master
You'll have to post your code that does the caching.
s_hewitt
A: 

1.Solution for solving this problem by using static resources variable for caching all of resource depend on Type of child class.

public class ResourceBase
{
    protected static IDictionary<Type, IDictionary<string, XDocument>> resources;
    protected static IDictionary<string, XDocument> Resources
    {
        get
        {
            if (resources == null)
            {
                // cache XDocument instance in resources variable seperate by type and culture name.
                // load resx file to XDocument
            }

            return resources;
        }
    }
}

By the way, this solution require Child class Type every time by using the following code when I get resource value. Moreover, I tested this solution about 10k - 100k rounds and It can retrieves data about 6,000 operations/sec.. So, I can't use this solution for real world application because every web request must retrieves more than 50 operations.

public static Type GetCallerType(Type baseClass, int skipFrames)
{
    StackTrace trace = new StackTrace(skipFrames + 1, false);
    Type callerType = null;

    for (int i = 0; i < trace.FrameCount; ++i)
    {
        StackFrame frame = trace.GetFrame(i);
        Type type = frame.GetMethod().DeclaringType;

        if (type == baseClass || IsInheritFrom(type, baseClass))
        {
            callerType = type;
        }
        else
        {
            break;
        }
     }

     if (callerType != baseClass)
        return callerType;
     else
        return null;
}
Soul_Master
A: 

2.The easier solution for solving this problem by using method extension only.

Strongly-typed resource file

[ResourceInfo("~/Views/Home", "Index")]
public class IndexView
{
    protected static IDictionary<string, XDocument> resources = new Dictionary<string, XDocument>();

    public static string Title
    {
        get
        {
            return resources.GetString("Title");
        }
    }
}

Resource Helper file

public static class ResourceHelper
{
    const string RESOURCE_EXTENSION = "resx";
    const string DEFAULT_CULTUREKEY = "(default)";

    public static string GetString(this IDictionary<string, XDocument> resource, string resourceKey)
    {
        return resource.GetString(resourceKey, System.Threading.Thread.CurrentThread.CurrentUICulture.Name);
    }

    public static string GetString(this IDictionary<string, XDocument> resource, string resourceKey, string cultureName)
    {
        if (resource.Count == 0)
            resource.LoadResource();

        // retrieve data
    }

    public static void LoadResource(this IDictionary<string, XDocument> resource)
    {
        // logic to load resource
    }

I tested this logic with same test case like #solution1. But I found very interesting result. It can retrieves more than 120,000 operations/sec. So, It's my final answer because it's simply, clean and fast.

Thanks,

Soul_Master