Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide additional parameters to support provision of execution context to Load and LoadAsync #588

Open
statler opened this issue Apr 6, 2023 · 3 comments

Comments

@statler
Copy link

statler commented Apr 6, 2023

Hi Aleksey

Statler here - you may remember me from such posts as

So, just when I thought I had every base covered, I have come across a new use case that I need to manage. I am hoping you can help me out here.

My case deals successfully with automapper projection by:

  1. Registering CustomAccessors automatically from the automapper config (Support AutoMapper's ProjectTo in DataSourceLoader #367)
  2. Getting the IDs of all results from the Devexpress.AspNet.data expression compilation query
  3. Querying the base type for all ids in (2), and using ProjectTo

This all works really well for ProjectTo(Mapconfig)

Where I have come unstuck is the overload of ProjectTo(Mapconfig, MapParameter) (https://docs.automapper.org/en/stable/Queryable-Extensions.html#parameterization)

This enables the injection of runtime variables into the projection and is a case I can't see a way to resolve as it is not possible to provide context in the CustomAccessors or CustomFilters. Would you be open to modifying the signatures for these methods to support the provision of context via an additional parameter to DataSourceLoader.LoadAsync / DataSourceLoader.Load

@statler
Copy link
Author

statler commented Apr 6, 2023

I have just put together a PR (#589) that shows what I am thinking. You would have different thoughts on how it should be done, but the solution works and shows the principle.

The main thing this PR does is:

  • Add additional property to DataSourceLoadOptions - an object called RuntimeResolutionContext. The idea is that this can be passed into the LoadAsync and becomes available in the RegisterBinaryExpressionCompiler registered methods
  • Added new RegisterBinaryExpressionCompilerWithContext for methods that need the context. This is added with a different signature so backwards compatibility isn't affected. The additional information available to registered methods is actually the FilterExpressionCompiler that calls the customfuncs. The runtimecontext is available through this reference.
  • The reason for exposing FilterExpressionCompiler (and a new method CompileNonCustomBinary) is so that when writing customfuncs, we can leverage the heavy lifting done by CompileBinary regarding multiple different operators, rather than having to do all this by hand. This necessitated changing some accessibility on the class.

As I started out, I expect you may want to refactor the exact way this is done, but do you see the utility and would you be willing to implement in the root library?

Here is an example of it in use (real world). This method:

  • Gets the userid of the current session through the RuntimeResolutionContext that was passed in to the options in the Load call
  • Uses the newly exposed method on FilterExpressionCompiler to access CompileBinary and resolve the Expression for querying a subcollection of the object
  • Joins this query to an additional operator that also limits by the current userId
  • Returns the new Expression

Subsequently the operator is projected to a DTO, which is achieved using the same object passed in the runtimecontext - but that's part of my other library.

        CustomFilterCompilers.RegisterBinaryExpressionCompilerWithContext((info, rtContext) =>
        {
            if (info.DataItemExpression.Type == typeof(Notification))
            {
                if (info.AccessorText == "DateDismissed")
                {
                    dynamic dynObj = new DynamicWrapper(rtContext.RuntimeResolutionContext);
                    if (int.TryParse(dynObj.UserId.ToString(), out int _ui))
                    {
                        var pBaseExp = info.DataItemExpression as ParameterExpression;
                        var pBaseProperty = Expression.PropertyOrField(pBaseExp, "NotificationTos");

                        var _p = Expression.Parameter(typeof(NotificationTo), "nTo");
                        var baseResult = rtContext.CompileNonCustomBinary(_p, GetParametersAsList(info));
                        var predicate = PredicateBuilder.New(Expression.Lambda<Func<NotificationTo, bool>>(baseResult, _p));
                        predicate.And((p) => p.UserId == _ui);

                        var result = Expression.Call(
                            typeof(Enumerable), "Any", new[] { _p.Type },
                            pBaseProperty, predicate);

                        var ex22 =  Expression.Lambda(result, pBaseExp) as Expression<Func<Notification, bool>>;
                        return CompileWhereExpression(info, ex22).Body;
                    }
                }
            }
            return null;
        });

`

@statler
Copy link
Author

statler commented Apr 11, 2023

OK - so I have expanded the scope of this somewhat. I have finally gotten around to implementing a non-intrusive integration of Automapper. PR is at #589.

The PR is mainly to start a discussion about what (if any) you want in the main library.

Anyone wanting an automapper-ready implementation in the meantime can pull https://github.com/statler/DevExtreme.AspNet.Data

I will have a nuget up shortly

@statler
Copy link
Author

statler commented Apr 13, 2023

OK - nuget package is available.

Available as nuget at https://www.nuget.org/packages/DXAutomap.AspNet.Data/1.12.0

NuGet\Install-Package DXAutomap.AspNet.Data -Version 1.12.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant