mercredi 22 juillet 2015

Object reference not set to an instance of an object when Invoking generic reflective method

So this problem is quite confusing. And I'm its fairly complicated and abstract so I'm going to try my best to explain it.

I have 3 classes LinkedIn, Facebook, and Twitter. All of them inherit from a commit base class SocialBase which only has one property: uuid and this property is only used so we can find the actual types in the database.

So I have this one function that accepts a Profile class which contains foreign keys to all LinkedIn, Facebook, and Twitter tables, as well as an Enum value that will tell us whether to look for the LinkedIn, the Facebook, or the Twitter foreign keys

public static async Task UnlinkSocialAccountFromProfile(Profile prof, SocialNetworks provider)
    {
        //TYPE variable of LinkedIn, Facebook, or Twitter
        var handler = HandlerMapping[provider];
        //client is MobileServiceClient
        var method = client.GetType().GetMethod("GetTable", Type.EmptyTypes);
        //MobileServiceClient.GetTable<handler>()
        var generic = method.MakeGenericMethod(handler);
        //IMobileServiceTable<handler> 
        var table = generic.Invoke(client, null);
        //Profile has 3 foreign keys, LinkedinUUID, FacebookUUID, TwitterUUID, we want the <handler>UUID
        string propertyValue = prof.GetType().GetProperty(handler.Name + "UUID").GetValue(prof) as string;
        //Invoke Extension method with our generic types
        var genMethod =
            typeof (Extensions).GetMethod("FilterByNamedProperty")
                .MakeGenericMethod(table.GetType().GetGenericArguments()[0], propertyValue.GetType());
        //Get the List<handler> that results from our query
        var result = await (Task<List<Linkedin>>)(genMethod.Invoke(null, new [] {table, "uuid", propertyValue}));
        //var result = await (table as IMobileServiceTable<SocialBase>).FilterByNamedProperty("uuid", propertyValue);
        await new SocialResources().DeleteIfExists((result as IList<SocialBase>)[0]);
}

So what I'm doing here is getting the Type of the class, so this will be one of the SocialBase subclasses. For this particular example I know I'm going to be looking for the LinkedIn type. So after getting type LinkedIn I need to invoke the generic method from my MobileServiceTable so normally it would look like MobileServiceTable.GetTable<LinkedIn>() but thanks to reflection we have to take the longer route around this.

After getting my returned instance of IMobileServiceTable<LinkedIn> I get value of the foreign key I'm looking for. In this case it will be called LinkedInUUID. Now here comes the tricky part. I have this extension method that will construct my query expression for me since it has to be of type Expression<Func<LinkedIn, bool>>

public async static Task<List<TSource>> FilterByNamedProperty<TSource, TValue>(this IMobileServiceTable<TSource> source, string propertyName, TValue value)
    {
        // uuid
        var property = typeof(TSource).GetProperty(propertyName);
        // (TSource)p
        var parExp = Expression.Parameter(typeof(TSource));
        //p.uuid
        var methodExp = Expression.Property(parExp, property);
        // value
        var constExp = Expression.Constant(value, typeof(TValue));
        // p.uuid == value
        var binExp = Expression.Equal(methodExp, constExp);
        // p => p.uuid == value
        var lambda = Expression.Lambda<Func<TSource, bool>>(binExp, parExp);
        return await source.Where(lambda).ToListAsync(); 
    }

I believe the comments do a pretty good job of explaining the build process as each statement occurs. However, once we get to the return await ... the application will crash. Here is the output right before that line and the error immediately following.

IMobileServiceTable<TSource> source = {Microsoft.WindowsAzure.MobileServices.MobileServiceTable<SocialConnect.Linkedin>} (Correct)

propertyName = uuid (Correct)

TValue value = dscRJQSIxJaEfd (Correct)

Resulting Error:

An unhandled exception of type 'System.AggregateException' occurred in mscorlib.dll at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions) at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken) at System.Threading.Tasks.Task.Wait() at MobileServiceSample.Program.Main(String[] args) in MobileServiceSample\MobileServiceSample\Program.cs:line 21 at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args) at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args) at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() at System.Threading.ThreadHelper.ThreadStart_Context(Object state) at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Threading.ThreadHelper.ThreadStart()

I have a strong feeling the problem is with my lambda expression, but if I test the extension with this line var test = await (source as IMobileServiceTable<Linkedin>).Where(p => p.uuid == (value as string)).ToListAsync(); it works perfectly fine. But as soon as I change it to use the lambda variable than I get the exception.

Any ideas?





Aucun commentaire:

Enregistrer un commentaire