Bug Report T1147982
Visible to All Users

Security.Blazor - "ValueManagerContext.Storage is null" error occurs when a custom function criteria operator is not processed by the SecurityFunctionPatcher logic

created 2 years ago (modified 2 years ago)

The error occurs in an XAF ASP.NET Core application (both in Blazor and Web API Service). The error occurs when a custom function criteria operator is not processed by the SecurityFunctionPatcher logic.

ValueManager API are unavailable in standalone Web API Service and XAF Blazor application in some cases. Refer to the following Breaking Change for more details: T1121273 - Core - ValueManager API availability and deprecated static helpers in XAF .NET 6+ apps (Blazor, Web API Service, WinForms). In particular, see this section: Filtering: Custom Criteria Functions & Operators.

Workarounds

All possible workarounds are described at T1135049 - Core - The SecurityFunctionPatcher logic is not called for a non-secured XPObjectSpace. A general workaround for all usage scenarios is the use of custom 'ScopedValueAccessor', which was offered by our customer in that thread.

Known cases:

C#
[PersistentAlias("CurrentOrgId")]

Where the CurrentOrgId fuctrion uses the DevExpress.ExpressApp.SecuritySystem.Instance static property to evaluate 'CurrentOrgId' value at runtime. In this case, a general workaround is the use of custom 'ScopedValueAccessor'.

  • When the IObjectSpace.GetObjectsQuery<T> method uses a property with a persistent alias attribute, which in turn uses a custom function criteria operator. Although the data is loaded via an IObjectSpace instance, the SecurityFunctionPatcher logic is not called, and the persistent alias cannot be calculated:
C#
var testObject = objectSpace.GetObjectsQuery<TestObjectWithCustomCriteria>().Where(t => t.CurrentOrgId_PersistentAlias == organization.Oid); [PersistentAlias("CurrentOrgId)")] public Guid CurrentOrgId_PersistentAlias => //...;
C#
using(var uow = new UnitOfWork(Session.DataLayer)) { //... some code }
  • When the Session.Evaluate method is called directly, the SecurityFunctionPatcher logic is not called.
  • When DataLayer or ObjecLayer is directly used to upload data.

T1147608 - Security.Blazor - The "ValueManagerContext.Storage is null" error occurs on downloading a file attachment with CurrentUserId() in PersistentAlias
T1135049 - Core - The SecurityFunctionPatcher logic is not called for a non-secured XPObjectSpace

Comments (2)
M M
Martin Praxmarer - DevExpress MVP 2 years ago

    can you share an update on this?

    Anatol (DevExpress) 2 years ago

      Hello Martin,

      Our developers are working on this task. We will update this ticket when we have a reliable result.

      Answers approved by DevExpress Support

      created 2 years ago (modified 2 years ago)

      We have addressed the issue described in this ticket and will include a fix in our next maintenance update. Should you need to apply our fix prior to official release, you can request a hotfix here.

      Important Notes:

      • Hotfixes may be unavailable for Early Access/Beta builds or updates set for release within a week.
      • .NET only: in the NuGet Package Manager, use your personal NuGet feed and check the "Include prerelease" option to view the hotfix package in the "Updates" tab.
      Additional information:

      Implementation details

      With this fix, the IServiceProvider object should be passed to Session/UnitOfWork objects. An XAF application does this automatically when a new Session/UnitOfWork instance is created.

      Take the following information into account:

      • The described mechanism only works in an XAF application with security.
      • If the DevExpress.ExpressApp.Xpo.XPObjectSpaceProvider class is created manually, the IServiceProvider object should be passed to a constructor.
      • If you create a new instance of Session/UnitOfWork in custom code, you need to pass the IServiceProvider instance to a constructor.
      • In XAF WinForms applications, Application Builder must be used for this mechanism to work.
      • In XAF WinForms v22.2, the IServiceProvider instance is created when the XAF application builder executes the security registration step. This service provider instance contains only the services related to XAF Security and cannot be used to resolve other services. It is also not available through the XafApplication.ServiceProvider property. ServiceCollection created to build this IServiceProvider object can not be reached and customized. v23.1 does not have this limitation.

      Examples - how to create a new UnitOfWork and pass IServiceProvider

      Create a new UnitOfWork instance in a BO:

      C#
      using DevExpress.Persistent.BaseImpl; using DevExpress.Xp public class Task : BaseObject { public Task(Session session) : base(session) { } protected override void OnSaving() { base.OnSaving(); using(var uow = new UnitOfWork(Session.ServiceProvider, Session.DataLayer)) { //code... uow.CommitChanges(); } } //... }

      With the Session.ServiceProvider property, you can use the following approach to obtain the current security instance and security user instead of using the SecuritySystem.Instance static approach (it doesn't work in Web API Service), which utilizes ValueManger or other SecuritySystem static properties:

      C#
      var security = Session.ServiceProvider.GetRequiredService<ISecurityStrategyBase>(); CreatedBy = Session.GetObjectByKey<ApplicationUser>(((ApplicationUser)security.User).Oid);

      Create a new UnitOfWork instance in an XAF controller:

      NOTE:

      In XAF controllers, we recommend using IObjectSpace instead of directly using Session/UnitOfWork instances. You can create a new IObjectSpace through the XafApplication.CreateObjectSpace method call. The code samples below directly create a new Session/UnitOfWork instance and are intended to demonstrate advanced cases. See also: Avoid creating a new Session/UnitOfWork with parameterless constructors in XPO-based projects.

      • In an XAF WinForms v22.2 application, the IServiceProvider object can not be obtained in a controller through DI (this feature is supported in v23.1+). You can use the XPObjectSpace.Session property:
      C#
      using DevExpress.ExpressApp; using DevExpress.ExpressApp.Xpo; using DevExpress.Xpo; public class WinController : ViewController<ListView> { protected override void OnActivated() { base.OnActivated(); var session = ((XPObjectSpace)View.ObjectSpace).Session; using(var uow = new UnitOfWork(session.ServiceProvider, session.DataLayer)) { //code... uow.CommitChanges(); } } }
      • In an XAF Blazor v22.2 application, the IServiceProvider object can be obtained from an application instance:
      C#
      using DevExpress.ExpressApp; using DevExpress.Xpo; public class BlazorController : ViewController<ListView> { protected override void OnActivated() { base.OnActivated(); var serviceProvider = Application.ServiceProvider; IDataLayer dataLayer = //... using(var uow = new UnitOfWork(serviceProvider, dataLayer)) { //code... uow.CommitChanges(); } } }
      • In an XAF Blazor v23.1 application, the IServiceProvider object can be obtained in a controller as in XAF v22.2. Additionally, it can be passed to a constructor using DI. Due to performance reasons, we recommend only injecting the IServiceProvider dependency as this works much faster in comparison with other service injections. The constructor should be marked by the Microsoft.Extensions.DependencyInjection.ActivatorUtilitiesConstructorAttribute attribute.
      C#
      using DevExpress.ExpressApp; using DevExpress.Xpo; using Microsoft.Extensions.DependencyInjection; public class CustomController : ViewController<ListView> { readonly IServiceProvider serviceProvider; public CustomController() { } [ActivatorUtilitiesConstructor] public CustomController(IServiceProvider serviceProvider) : this() { this.serviceProvider = serviceProvider; } protected override void OnActivated() { base.OnActivated(); IDataLayer dataLayer = null; //... using(var uow = new UnitOfWork(serviceProvider, dataLayer)) { //code... uow.CommitChanges(); } } }

      NOTE:

      Prior to v23.1, XAF WinForms applications do not support DI at the same level as XAF Blazor applications. In v22.2, the IServiceProvider object from Session/UnitOfWork instances contains only services related to the ISecurityStrategyBase service. It cannot be used to work with other XAF services, such as IObjectSpaceProviderService and other core services.

      Breaking changes

        Show previous comments (1)

          Hello,
          Thanks, this is a very interesting feature that, as we discussed in other related tickets, I'm waiting for.
          I would like to ask also what would be the best approach to retrieve an instance of the current security strategy from within a custom operator (I mean a class that implements ICustomFunctionOperatorConvertibleToExpression and/or ICustomFunctionOperatorFormattable). It would be possible to get a reference to the IServiceProvider from within it?
          Thanks
          G

          Dmitry M (DevExpress) 2 years ago

            Hello,

            The original issue relates to the fact that XAF ValueManager is not supported in Web API Service. XAF ValueManager is used a lot in custom function operators in the context of XAF, but not all cases were covered by the OnCustomizeSecurityCriteriaOperator event. ICustomFunctionOperatorConvertibleToExpression and ICustomFunctionOperatorFormattable are global APIs. Changes that were made in the context of this issue are related to the XPO.Session class only and are specifically designed to work with XAF. The ServiceProvider instance in ICustomFunctionOperatorConvertibleToExpression or ICustomFunctionOperatorFormattable implementors is not supported. The approach from the 'Filtering: Custom Criteria Functions & Operators' section of the following ticket should be used as a substitute for the global static approach with ICustomFunctionOperatorConvertibleToExpression or ICustomFunctionOperatorFormattable: T1121273 - Core - ValueManager API availability and deprecated static helpers in XAF .NET 6+ apps (Blazor, Web API Service, WinForms). The OnCustomizeSecurityCriteriaOperator event parameter has a link to the IServiceProvider instance.

            I also would like to mention that the XPO part from the mentioned 'Filtering: Custom Criteria Functions & Operators' section, where we described the necessity of registering a dummy ICustomFunctionOperator implementor, is not required.

              Thanks for the additional clarification.
              I know. Anyhow I'm asking because as I mentioned in the related ticket T1135049 - Core - The SecurityFunctionPatcher logic is not called for a non-secured XPObjectSpace with reference to the OnCustomizeSecurityCriteriaOperator that in the current implementation needs to move all the original ICustomFunction code outside from the original class (that is demanded to host the function logic) and this is a bit complex when you need to use the same Custom Function in multiple projects. In this case you need to remember that the function logic must be exported within the event handler.
              But thanks again for the additional confirmation according to which my understanding is that you do not have any plan to allow, for example, DI from within a Custom Function. Is my understanding correct?
              Thanks
              G

              Disclaimer: The information provided on DevExpress.com and affiliated web properties (including the DevExpress Support Center) is provided "as is" without warranty of any kind. Developer Express Inc disclaims all warranties, either express or implied, including the warranties of merchantability and fitness for a particular purpose. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.

              Confidential Information: Developer Express Inc does not wish to receive, will not act to procure, nor will it solicit, confidential or proprietary materials and information from you through the DevExpress Support Center or its web properties. Any and all materials or information divulged during chats, email communications, online discussions, Support Center tickets, or made available to Developer Express Inc in any manner will be deemed NOT to be confidential by Developer Express Inc. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.