Can I safely use a Scoped service inside a singleton Service?

I have a global builder:

[Service(ServiceType = typeof(JsonModelBuilder), Lifetime = DependencyLifetime.Singleton)]|

Today it can be shared between different pages and sessions, but now i want to inject and use

[Service(ServiceType = typeof(PreloadMediaAccessor), Lifetime = DependencyLifetime.Scoped)]

inside a method in that builder, but PreloadMediaAccessor need to be unique per request. Will this brake since its first created inside a singleton?

Do I need to skip the injection and use IoC.Resolve<PreloadMediaAccessor>() inside the method?

No, you should not inject the scoped service in the singelton service, the reason is that the scoped service will keep the data from when it was created.

Dependent on what your PreloadMediaAccessor are doing it may be that the service not should be registered as scoped and instead singelton.

If you open example the RequestModelAccessor in accelerator you will see that it will have a private static readonly AsyncLocal<RequestModel> _routeRequest = new AsyncLocal<RequestModel>(); that will keep the state, this property is set in the beginning of the request and reset in the end of the request and will keep the RequestModel during the request.

Oki, so there is a lot of thing i dont understand in this code, i’ll read upp on it but basicaly like this?

Booth AsyncLocal and System.Web.HttpContext.Current is knowlage gaps for me right now, so i cant tell if this make sense :smiley:

Is the httpContext checks needed or was that just for the requestModel? I also need to set this value somewhere? do i need to create an IActionFilter like the RequestModelActionFilter?

namespace Litium.Accelerator.Services
{
[Service(ServiceType = typeof(PreloadMediaAccessor)]
public class PreloadMediaAccessor
{
	private static readonly AsyncLocal<List<MediaJsonModel>> _mediaToPreload = new AsyncLocal<List<MediaJsonModel>>();

	public virtual List<MediaJsonModel> MediaToPreload
	{
		get
		{
			var context = System.Web.HttpContext.Current;
			if (context != null)
			{
				return (List<MediaJsonModel>)context.Items[nameof(List<MediaJsonModel>)];
			}

			return _mediaToPreload.Value;
		}

		set
		{
			var context = System.Web.HttpContext.Current;
			if (context != null)
			{
				context.Items[nameof(List<MediaJsonModel>)] = value;
			}
			else
			{
				_mediaToPreload.Value = value;
			}
		}
	}
}
}

Insted of writing a ActionFilter could i just set it if null like this?

		get
		{
			var context = System.Web.HttpContext.Current;
			if (context != null)
			{
				var mediaToPreload = (List<MediaJsonModel>)context.Items["MediaToPreload"];
				if(mediaToPreload == null)
				{
					mediaToPreload = MediaToPreload = new List<MediaJsonModel>();
				}
				return mediaToPreload;
			}

			return _mediaToPreload.Value;
		}

The HttpContext is the holder for all properties that are for the incoming request, this is always null if the item are tried to be used from a background job, that’s the reason we fallback to AsyncLocal to not got any strange runtime errors :slight_smile:

Both the HttpContext and AsyncLocal are stored in the thread-call-context to be accessible within the current request or for the AsyncLocal also for background jobs.

Best practice is to clear the values out in the end of the request (setting the value to null) to improve the memory management on the server. That´s the reason we are using the global action filter or request delegate to set (in the start of request) and reset (in the end of request) the values.

This topic was automatically closed 28 days after the last reply. New replies are no longer allowed.