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