Question

I have the need to create a generic method to retrieve a specific kind of class that derives from System.Data.Services.Client.DataServiceContext, I got some ideas how to deal with this but so far I'm stuck in the concept of generic co-variance, does this concept applies to this case at all? this is how my current code looks like :

public abstract class BaseODataProvider<T> where T : DataServiceContext, new()
{
    public IApiSettings ApiSettings { get; set; }

    public BaseODataProvider(IApiSettings ApiSettings = null)
    {
        this.ApiSettings = ApiSettings ?? new DefaultApiSettings();
    }

    public T GetContext()
    {
        var context = new T(new Uri(ApiSettings.ODataUrl));
        context.IgnoreMissingProperties = true;
        context.IgnoreResourceNotFoundException = true;
        var credentials = Convert.ToBase64String(Encoding.ASCII.GetBytes(ApiSettings.LoginName + ":" + ApiSettings.Password));
        context.SendingRequest +=
                (object s, SendingRequestEventArgs e) =>
                        e.RequestHeaders.Add("Authorization", "Basic " + credentials);
        return context;
    }
}

As you can see, there is no way I can instantiate T with optional parameters, it says "cannot provide parameters when creating an instance of a type parameter 'T'", however I would like to find out a way to resolve what URI is needed to create the oDataContext based on the type of T, so my expected instances would be something like this:

GetContext<MyContext1>().Table1.Where(x=>x.Id == 1);
GetContext<MyContext2>().Table2.FirstOrDefault();

Any help works!

Was it helpful?

Solution

You can instantiate an object of a generic type with parameters using reflection.

public T GetContext()
{
    Uri myUri = new Uri(ApiSettings.ODataUrl);

    Type contextType = typeof(T);
    DataServiceContext context = (T)Activator.CreateInstance(contextType, myUri);

    // Do whatever

    return context;
}

The second argument of Activator.CreateInstance is an array of type object, so you can pass in whatever number or type of arguments the constructor requires. To make the above code work, all of the T types you use have to have a constructor with the same signature, like this:

public class MyContext1 : DataServiceContext
{
    public MyContext1(Uri uri)
    {
        // Do whatever
    }
}

public class MyContext2 : DataServiceContext
{
    public MyContext2(Uri uri)
    {
        // Do whatever
    }
}

You're not limited to a single parameter either - this works for constructors with any number of parameters. Just make sure that you're using the same number and type of arguments for each class that you instantiate this way.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top