The code below is a workaround we are trying to use for the entity security issues I mentioned in the past, where '.Include'ed entities must be filtered in the IEntityFetching server-side process.
As you can see, I'm basically chaining a series of async fetches to populate the client cache with all of the entities in my hierarchy, then the intention was to call the fetch with a .Include() and in theory the whole thing should be retrieved from the entity cache (which of course now contains a permission filtered set of entities).
However, if the code is used with the statement ".With(
QueryStrategy.CacheOnly)" then the OnFetching() fails when it tries to apply a filter collection to the original query with the following error:
The type or method has 2 generic parameter(s), but 1 generic argument(s) were provided. A generic argument must be provided for each generic parameter.
at System.RuntimeType.SanityCheckGenericArguments(Type[] genericArguments, Type[] genericParamters) at System.Reflection.RuntimeMethodInfo.MakeGenericMethod(Type[] methodInstantiation) at IdeaBlade.EntityModel.EntityQueryFilterVisitor.ModifyExpression(MemberExpression memberExpression, Expression fqExpression) at IdeaBlade.EntityModel.EntityQueryFilterVisitor.VisitMemberAccess(MemberExpression me, Expression expr) at IdeaBlade.Linq.ExpressionVisitor.VisitExpressionCore(Expression e) at IdeaBlade.Linq.TransformExpressionVisitor.VisitExpressionCore(Expression expr) at IdeaBlade.Linq.LocalizingExpressionVisitor.VisitExpressionCore(Expression expr) at IdeaBlade.Linq.ExpressionVisitor.VisitExpression(Expression expr) at IdeaBlade.Linq.ExpressionVisitor.VisitExpressionCore(Expression e) at IdeaBlade.Linq.TransformExpressionVisitor.VisitExpressionCore(Expression expr) at IdeaBlade.Linq.LocalizingExpressionVisitor.VisitExpressionCore(Expression expr) at IdeaBlade.Linq.ExpressionVisitor.VisitExpression(Expression expr) at IdeaBlade.Linq.ExpressionVisitor.VisitExpressionCore(Expression e) at IdeaBlade.Linq.TransformExpressionVisitor.VisitExpressionCore(Expression expr) at IdeaBlade.Linq.LocalizingExpressionVisitor.VisitExpressionCore(Expression expr) at IdeaBlade.Linq.ExpressionVisitor.VisitExpression(Expression expr) at IdeaBlade.Linq.ExpressionVisitor.<VisitExpressions>b__0(Expression e) at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext() at System.Linq.Buffer`1..ctor(IEnumerable`1 source) at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source) at IdeaBlade.Linq.ExpressionVisitor.VisitExpressions(IEnumerable`1 expressions) at IdeaBlade.Linq.ExpressionVisitor.VisitExpressionCore(Expression e) at IdeaBlade.Linq.TransformExpressionVisitor.VisitExpressionCore(Expression expr) at IdeaBlade.Linq.LocalizingExpressionVisitor.VisitExpressionCore(Expression expr) at IdeaBlade.Linq.ExpressionVisitor.VisitExpression(Expression expr) at IdeaBlade.Linq.ExpressionVisitor.Visit(Expression expr) at IdeaBlade.Linq.LocalizingExpressionVisitor.Visit(Expression expr) at IdeaBlade.EntityModel.EntityQueryFilterVisitor.FilterQuery(EntityQuery query) at IdeaBlade.EntityModel.EntityQueryExtensions.Filter[TQuery](TQuery query, EntityQueryFilterCollection filters) at QuestManager.Web.BOSExtensions.EntityRestrictionFilter.OnFetching(EntityServerFetchingEventArgs args) in D:\Source\QuestManager\QuestManager.Web\BOSExtensions\EntityRestrictionFilter.cs:line 71 at IdeaBlade.EntityModel.Server.EntityServer.OnFetching(SessionBundle sessionBundle, IEntityQuery& query, Object& context) at IdeaBlade.EntityModel.Server.EntityServer.Fetch(SessionBundle sessionBundle, IEntityQuerySurrogate surrogate) at SyncInvokeFetch(Object , Object[] , Object[] ) at System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke(Object instance, Object[] inputs, Object[]& outputs) at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc& rpc)
|
Replace the cache fetch with "
QueryStrategy.Normal" and the error disappears, so I suspect there's something failing in the filter application code.
Code for server-side filtering is as follows:
public void OnFetching(EntityServerFetchingEventArgs args) { var manager = new DomainModelEntityManager(EntityManager.DefaultManager);
if (args.Principal == null) throw new ApplicationException("Attempt to fetch data with an unauthenticated user");
int uid = GetUserId(args.Principal);
var eqFilters = new EntityQueryFilterCollection();
Debug.WriteLine("==== OnFetching {0}", args.Query.ToString());
eqFilters.AddFilter((IQueryable<CustomerAccount> custs) => (from e in custs join perm in manager.CachedPermissions on e.CustomerAccountID equals perm.ElementID where (perm.PermissionBits != 0) && perm.UserProfileId == uid && e.DeleteDate == null select e).AsQueryable());
eqFilters.AddFilter((IQueryable<Client> clis) => (from e in clis join perm in manager.CachedPermissions on e.ClientID equals perm.ElementID where (perm.PermissionBits != 0) && perm.UserProfileId == uid && e.DeleteDate == null select e).AsQueryable());
(etc...)
var original = args.Query as EntityQuery; args.Query = original.Filter(eqFilters); }
|
Code for fetching the entities is as follows:
private void GrabEntities<T>(IEntityQuery<T> qry, Action<List<T>> callback) where T:Entity { List<T> results = null; Debug.WriteLine("1 - Fetching records from {0}", typeof (T).Name); _dom.ExecuteQueryAsync<T>(qry, args => { if (args.Error != null) throw args.Error; else { results = args.Result.ToList(); Debug.WriteLine("2 - Fetched {0} records of type {1}", results.Count, typeof (T).Name); callback(results); } }, null); }
private void ReloadCustomers(Guid customerAccountid) { LoadCustomers(customerAccountid); }
private void LoadCustomers(Guid customerAccountid) { if (customerAccountid == Guid.Empty) return;
GrabEntities<CustomerAccount>(from a in _dom.CustomerAccounts select a, cba => GrabEntities<Client>(from c in _dom.Clients select c, cbc => GrabEntities<QuestionnaireProject>(from p in _dom.QuestionnaireProjects select p, cbp => GrabEntities<QuestionnaireWave>(from w in _dom.QuestionnaireWaves select w, cbw => GrabEntities<Questionnaire>(from q in _dom.Questionnaires select q, cbq => ShowTree()))))); }
private void ShowTree(){ Debug.WriteLine("*** Displaying results from cache for Account!");
var qry = from c in _dom.CustomerAccounts.Include("Clients.QuestionnaireProjects.QuestionnaireWaves.Questionnaires") .With(QueryStrategy.Normal) where c.CustomerAccountID == _customerId select c;
_dom.ExecuteQueryAsync<CustomerAccount>(qry, args => { if (args.Error != null) throw args.Error; else { CustomerAccount = args.Result.First(); Debug.WriteLine("*** CustomerAccount retrieved!"); } }, null); }
|