Netcore: Alternate approach for MSDI refactor (#9247)

* Doesn't make much sense to have Concrete on IRegister, only on IFactory

* Handle FilesTreeController requires IFileSystem of type PhysicalFileSystem

* Handle registration of default MediaFileSystem without using RegisterUniqueFor

* Remove RegisterFor / RegisterUniqueFor from IRegister

* Switch over from LightInject to wrappers around MSDI

* Made mapper dependencies more explicit

* Remove registration for AngularJsonMediaTypeFormatter

It's dependencies aren't registered so container validation fails

* Resolve lifetime issue for EnsureValidSessionId by service locating

else resolve scoped in singleton

* Make registration more explicit for backoffice UserManager

* Make install step registrations more explicit

* Disable service provider validation so site can launch

Maybe this is a problem maybe not, we build about 8000 service providers so maybe everything is fine later...

* Further cleanup of IFactory interface

* Further cleanup of IRegister interface

* Revert "Make registration more explicit for backoffice UserManager"

This reverts commit 7215fe836103c597cd0873c66737a79b91ed4c49.

* Resolve issue where NewInstallStep would fail to reset password for "SuperUser"

Before MSDI, somehow BackOfficeIdentityOptions would be configured with token provider map from IdentityBuilder.AddDefaultTokenProviders.
After switchover those config actions are lost.

Subclass IdentityBuilder to ensure BackOfficeIdentityOptions doesn't miss config setup upstream.

* Initialize current.

* Add todo to turn container validation back on.

* Migrated ScopeFileSystemsTests to integration tests

Signed-off-by: Bjarke Berg <mail@bergmania.dk>

* Resolve issue where MediaFileSystem was skipping ShadowFileSystem

* Attempt to fix ScopeFileSystemsTests on azure devops

Signed-off-by: Bjarke Berg <mail@bergmania.dk>

* Be interesting to know what the actual full path is in pipeline.

* Clarify intent of CreateMediaTest

Doesn't help resolve weird UnauthorizedAccessException but it cuts so much cognitive overhead for the future.

* Use ILoggerfactory rather than mock for the manually constructed file PhysicalFileSystem

* Maybe resolve failing test on azure pipeline.

Co-authored-by: Bjarke Berg <mail@bergmania.dk>
This commit is contained in:
Paul Johnson
2020-10-26 10:47:14 +00:00
committed by GitHub
parent 7f9c1bb998
commit a99f625f6a
75 changed files with 517 additions and 864 deletions

View File

@@ -64,10 +64,7 @@ namespace Umbraco.Core.Composing
#endregion
#region IRegister
/// <inheritdoc />
public object Concrete => _register.Concrete;
/// <inheritdoc />
public void Register(Type serviceType, Lifetime lifetime = Lifetime.Transient)
=> _register.Register(serviceType, lifetime);
@@ -85,33 +82,6 @@ namespace Umbraco.Core.Composing
public void Register(Type serviceType, object instance)
=> _register.Register(serviceType, instance);
/// <inheritdoc />
public void RegisterFor<TService, TTarget>(Lifetime lifetime = Lifetime.Transient)
where TService : class
=> _register.RegisterFor<TService, TTarget>(lifetime);
/// <inheritdoc />
public void RegisterFor<TService, TTarget>(Type implementingType, Lifetime lifetime = Lifetime.Transient)
where TService : class
=> _register.RegisterFor<TService, TTarget>(implementingType, lifetime);
/// <inheritdoc />
public void RegisterFor<TService, TTarget>(Func<IFactory, TService> factory, Lifetime lifetime = Lifetime.Transient)
where TService : class
=> _register.RegisterFor<TService, TTarget>(factory, lifetime);
/// <inheritdoc />
public void RegisterFor<TService, TTarget>(TService instance)
where TService : class
=> _register.RegisterFor<TService, TTarget>(instance);
/// <inheritdoc />
public void RegisterAuto(Type serviceBaseType)
=> _register.RegisterAuto(serviceBaseType);
/// <inheritdoc />
public void ConfigureForWeb()
=> _register.ConfigureForWeb();
/// <inheritdoc />
public IFactory CreateFactory()
@@ -127,13 +97,7 @@ namespace Umbraco.Core.Composing
builder.RegisterWith(_register);
_builders.Clear(); // no point keep them around
IFactory factory = null;
// ReSharper disable once AccessToModifiedClosure -- on purpose
_register.Register(_ => factory, Lifetime.Singleton);
factory = _register.CreateFactory();
return factory;
return _register.CreateFactory();
}
/// <summary>
@@ -186,38 +150,6 @@ namespace Umbraco.Core.Composing
public void RegisterUnique(Type serviceType, object instance)
=> _uniques[GetUniqueName(serviceType)] = register => register.Register(serviceType, instance);
/// <summary>
/// Registers a unique service for a target, as its own implementation.
/// </summary>
/// <remarks>Unique services have one single implementation, and a Singleton lifetime.</remarks>
public void RegisterUniqueFor<TService, TTarget>()
where TService : class
=> _uniques[GetUniqueName<TService, TTarget>()] = register => register.RegisterFor<TService, TTarget>(Lifetime.Singleton);
/// <summary>
/// Registers a unique service for a target, with an implementing type.
/// </summary>
/// <remarks>Unique services have one single implementation, and a Singleton lifetime.</remarks>
public void RegisterUniqueFor<TService, TTarget>(Type implementingType)
where TService : class
=> _uniques[GetUniqueName<TService, TTarget>()] = register => register.RegisterFor<TService, TTarget>(implementingType, Lifetime.Singleton);
/// <summary>
/// Registers a unique service for a target, with an implementation factory.
/// </summary>
/// <remarks>Unique services have one single implementation, and a Singleton lifetime.</remarks>
public void RegisterUniqueFor<TService, TTarget>(Func<IFactory, TService> factory)
where TService : class
=> _uniques[GetUniqueName<TService, TTarget>()] = register => register.RegisterFor<TService, TTarget>(factory, Lifetime.Singleton);
/// <summary>
/// Registers a unique service for a target, with an implementing instance.
/// </summary>
/// <remarks>Unique services have one single implementation, and a Singleton lifetime.</remarks>
public void RegisterUniqueFor<TService, TTarget>(TService instance)
where TService : class
=> _uniques[GetUniqueName<TService, TTarget>()] = register => register.RegisterFor<TService, TTarget>(instance);
#endregion
#region Collection Builders

View File

@@ -21,15 +21,6 @@ namespace Umbraco.Core.Composing
/// <remarks>Throws an exception if the container failed to get an instance of the specified type.</remarks>
object GetInstance(Type type);
/// <summary>
/// Gets a targeted instance of a service.
/// </summary>
/// <typeparam name="TService">The type of the service.</typeparam>
/// <typeparam name="TTarget">The type of the target.</typeparam>
/// <returns>The instance of the specified type for the specified target.</returns>
/// <remarks>Throws an exception if the container failed to get an instance of the specified type.</remarks>
TService GetInstanceFor<TService, TTarget>();
/// <summary>
/// Tries to get an instance of a service.
/// </summary>
@@ -52,18 +43,7 @@ namespace Umbraco.Core.Composing
/// <typeparam name="TService">The type of the service.</typeparam>
IEnumerable<TService> GetAllInstances<TService>()
where TService : class;
/// <summary>
/// Releases an instance.
/// </summary>
/// <param name="instance">The instance.</param>
/// <remarks>
/// See https://stackoverflow.com/questions/14072208 and http://kozmic.net/2010/08/27/must-i-release-everything-when-using-windsor/,
/// you only need to release instances you specifically resolved, and even then, if done right, that might never be needed. For
/// instance, LightInject does not require this and does not support it - should work with scopes.
/// </remarks>
void Release(object instance);
/// <summary>
/// Begins a scope.
/// </summary>
@@ -72,13 +52,5 @@ namespace Umbraco.Core.Composing
/// <para>Scopes can be nested. Each instance is disposed individually.</para>
/// </remarks>
IDisposable BeginScope();
/// <summary>
/// Enables per-request scope.
/// </summary>
/// <remarks>
/// <para>Ties scopes to web requests.</para>
/// </remarks>
void EnablePerWebRequestScope();
}
}

View File

@@ -7,11 +7,6 @@ namespace Umbraco.Core.Composing
/// </summary>
public interface IRegister
{
/// <summary>
/// Gets the concrete container.
/// </summary>
object Concrete { get; }
/// <summary>
/// Registers a service as its own implementation.
/// </summary>
@@ -33,69 +28,8 @@ namespace Umbraco.Core.Composing
/// </summary>
void Register(Type serviceType, object instance);
/// <summary>
/// Registers a service for a target, as its own implementation.
/// </summary>
/// <remarks>
/// There can only be one implementation or instanced registered for a service and target;
/// what happens if many are registered is not specified.
/// </remarks>
void RegisterFor<TService, TTarget>(Lifetime lifetime = Lifetime.Transient)
where TService : class;
/// <summary>
/// Registers a service for a target, with an implementation type.
/// </summary>
/// <remarks>
/// There can only be one implementation or instanced registered for a service and target;
/// what happens if many are registered is not specified.
/// </remarks>
void RegisterFor<TService, TTarget>(Type implementingType, Lifetime lifetime = Lifetime.Transient)
where TService : class;
/// <summary>
/// Registers a service for a target, with an implementation factory.
/// </summary>
/// <remarks>
/// There can only be one implementation or instanced registered for a service and target;
/// what happens if many are registered is not specified.
/// </remarks>
void RegisterFor<TService, TTarget>(Func<IFactory, TService> factory, Lifetime lifetime = Lifetime.Transient)
where TService : class;
/// <summary>
/// Registers a service for a target, with an implementing instance.
/// </summary>
/// <remarks>
/// There can only be one implementation or instanced registered for a service and target;
/// what happens if many are registered is not specified.
/// </remarks>
void RegisterFor<TService, TTarget>(TService instance)
where TService : class;
/// <summary>
/// Registers a base type for auto-registration.
/// </summary>
/// <remarks>
/// <para>Auto-registration means that anytime the container is asked to create an instance
/// of a type deriving from <paramref name="serviceBaseType"/>, it will first register that
/// type automatically.</para>
/// <para>This can be used for instance for views or controllers. Then, one just needs to
/// register a common base class or interface, and the container knows how to create instances.</para>
/// </remarks>
void RegisterAuto(Type serviceBaseType);
#region Control
/// <summary>
/// Configures the container for web support.
/// </summary>
/// <remarks>
/// <para>Enables support for MVC, WebAPI, but *not* per-request scope. This is used early in the boot
/// process, where anything "scoped" should not be linked to a web request.</para>
/// </remarks>
void ConfigureForWeb(); // TODO: Unsure if we need this anymore
/// <summary>
/// Creates the factory.
/// </summary>

View File

@@ -1,18 +0,0 @@
namespace Umbraco.Core.Composing
{
/// <summary>
/// Provides a base class for targeted service factories.
/// </summary>
/// <typeparam name="TService"></typeparam>
public abstract class TargetedServiceFactory<TService>
{
private readonly IFactory _factory;
protected TargetedServiceFactory(IFactory factory)
{
_factory = factory;
}
public TService For<TTarget>() => _factory.GetInstanceFor<TService, TTarget>();
}
}

View File

@@ -19,13 +19,6 @@ namespace Umbraco.Core
public static void RegisterUnique<TService, TImplementing>(this Composition composition)
=> composition.RegisterUnique(typeof(TService), typeof(TImplementing));
/// <summary>
/// Registers a unique service with an implementation type, for a target.
/// </summary>
public static void RegisterUniqueFor<TService, TTarget, TImplementing>(this Composition composition)
where TService : class
=> composition.RegisterUniqueFor<TService, TTarget>(typeof(TImplementing));
/// <summary>
/// Registers a unique service with an implementing instance.
/// </summary>

View File

@@ -282,7 +282,7 @@ namespace Umbraco.Core.IO
{
lock (_shadowLocker)
{
var wrapper = new ShadowWrapper(filesystem, _ioHelper, _hostingEnvironment, _loggerFactory, shadowPath, IsScoped);
var wrapper = new ShadowWrapper(filesystem, _ioHelper, _hostingEnvironment, _loggerFactory, shadowPath,() => IsScoped());
if (_shadowCurrentId != null)
wrapper.Shadow(_shadowCurrentId);
_shadowWrappers.Add(wrapper);

View File

@@ -10,7 +10,8 @@ using Umbraco.Core.Hosting;
namespace Umbraco.Core.IO
{
public class PhysicalFileSystem : IFileSystem
public interface IPhysicalFileSystem : IFileSystem {}
public class PhysicalFileSystem : IPhysicalFileSystem
{
private readonly IIOHelper _ioHelper;
private readonly ILogger<PhysicalFileSystem> _logger;
@@ -56,7 +57,6 @@ namespace Umbraco.Core.IO
if (string.IsNullOrEmpty(rootUrl)) throw new ArgumentException("Value can't be empty.", nameof(rootUrl));
if (rootPath.StartsWith("~/")) throw new ArgumentException("Value can't be a virtual path and start with '~/'.", nameof(rootPath));
// rootPath should be... rooted, as in, it's a root path!
if (Path.IsPathRooted(rootPath) == false)
{
@@ -65,6 +65,9 @@ namespace Umbraco.Core.IO
rootPath = Path.Combine(localRoot, rootPath);
}
// clean up root path
rootPath = Path.GetFullPath(rootPath);
_rootPath = EnsureDirectorySeparatorChar(rootPath).TrimEnd(Path.DirectorySeparatorChar);
_rootPathFwd = EnsureUrlSeparatorChar(_rootPath);
_rootUrl = EnsureUrlSeparatorChar(rootUrl).TrimEnd('/');
@@ -328,7 +331,7 @@ namespace Umbraco.Core.IO
// nothing prevents us to reach the file, security-wise, yet it is outside
// this filesystem's root - throw
throw new UnauthorizedAccessException("File '" + opath + "' is outside this filesystem's root.");
throw new UnauthorizedAccessException($"File original: [{opath}] full: [{path}] is outside this filesystem's root.");
}
/// <summary>

View File

@@ -1,11 +0,0 @@
using Umbraco.Core.Composing;
namespace Umbraco.Core.IO
{
public class SupportingFileSystems : TargetedServiceFactory<IFileSystem>
{
public SupportingFileSystems(IFactory factory)
: base(factory)
{ }
}
}

View File

@@ -13,13 +13,6 @@ namespace Umbraco.Core
public static void Register<TService, TImplementing>(this IRegister register, Lifetime lifetime = Lifetime.Transient)
=> register.Register(typeof(TService), typeof(TImplementing), lifetime);
/// <summary>
/// Registers a service with an implementation type, for a target.
/// </summary>
public static void RegisterFor<TService, TImplementing, TTarget>(this IRegister register, Lifetime lifetime = Lifetime.Transient)
where TService : class
=> register.RegisterFor<TService, TTarget>(typeof(TImplementing), lifetime);
/// <summary>
/// Registers a service as its own implementation.
/// </summary>
@@ -33,12 +26,5 @@ namespace Umbraco.Core
public static void Register<TService>(this IRegister register, TService instance)
where TService : class
=> register.Register(typeof(TService), instance);
/// <summary>
/// Registers a base type for auto-registration.
/// </summary>
public static void RegisterAuto<TServiceBase>(this IRegister register)
where TServiceBase : class
=> register.RegisterAuto(typeof(TServiceBase));
}
}