Skip to content

Commit

Permalink
fixing Scoped Navigation
Browse files Browse the repository at this point in the history
  • Loading branch information
dansiegel committed May 5, 2020
1 parent 5c33d8c commit 2896142
Show file tree
Hide file tree
Showing 18 changed files with 300 additions and 62 deletions.
72 changes: 61 additions & 11 deletions src/Containers/Prism.DryIoc.Shared/DryIocContainerExtension.cs
Expand Up @@ -16,7 +16,7 @@ internal partial
#endif
class DryIocContainerExtension : IContainerExtension<IContainer>, IContainerInfo
{
private IResolverContext _currentScope;
private DryIocScopedProvider _currentScope;

/// <summary>
/// Gets the Default DryIoc Container Rules used by Prism
Expand Down Expand Up @@ -57,6 +57,11 @@ public DryIocContainerExtension(IContainer container)
}
#endif

/// <summary>
/// Gets the current scope
/// </summary>
public IScopedProvider CurrentScope => _currentScope;

/// <summary>
/// Used to perform any final steps for configuring the extension that may be required by the container.
/// </summary>
Expand Down Expand Up @@ -284,7 +289,7 @@ public object Resolve(Type type, params (Type Type, object Instance)[] parameter
{
try
{
var container = _currentScope ?? Instance;
var container = _currentScope?.Resolver ?? Instance;
return container.Resolve(type, args: parameters.Select(p => p.Instance).ToArray());
}
catch (Exception ex)
Expand All @@ -304,7 +309,7 @@ public object Resolve(Type type, string name, params (Type Type, object Instance
{
try
{
var container = _currentScope ?? Instance;
var container = _currentScope?.Resolver ?? Instance;
return container.Resolve(type, name, args: parameters.Select(p => p.Instance).ToArray());
}
catch (Exception ex)
Expand Down Expand Up @@ -352,7 +357,7 @@ Type IContainerInfo.GetRegistrationType(Type serviceType)
/// <summary>
/// Creates a new Scope
/// </summary>
public virtual void CreateScope() =>
public virtual IScopedProvider CreateScope() =>
CreateScopeInternal();

/// <summary>
Expand All @@ -362,17 +367,62 @@ Type IContainerInfo.GetRegistrationType(Type serviceType)
/// <remarks>
/// This should be called by custom implementations that Implement IServiceScopeFactory
/// </remarks>
protected IResolverContext CreateScopeInternal()
protected IScopedProvider CreateScopeInternal()
{
if (_currentScope != null)
var resolver = Instance.OpenScope();
_currentScope = new DryIocScopedProvider(resolver);
return _currentScope;
}

private class DryIocScopedProvider : IScopedProvider
{
public DryIocScopedProvider(IResolverContext resolver)
{
_currentScope.Dispose();
_currentScope = null;
GC.Collect();
Resolver = resolver;
}

_currentScope = Instance.OpenScope();
return _currentScope;
public bool IsAttached { get; set; }

public IResolverContext Resolver { get; private set; }
public IScopedProvider CurrentScope => this;

public IScopedProvider CreateScope() => this;

public void Dispose()
{
Resolver.Dispose();
Resolver = null;
}

public object Resolve(Type type) =>
Resolve(type, Array.Empty<(Type, object)>());

public object Resolve(Type type, string name) =>
Resolve(type, name, Array.Empty<(Type, object)>());

public object Resolve(Type type, params (Type Type, object Instance)[] parameters)
{
try
{
return Resolver.Resolve(type, args: parameters.Select(p => p.Instance).ToArray());
}
catch (Exception ex)
{
throw new ContainerResolutionException(type, ex);
}
}

public object Resolve(Type type, string name, params (Type Type, object Instance)[] parameters)
{
try
{
return Resolver.Resolve(type, name, args: parameters.Select(p => p.Instance).ToArray());
}
catch (Exception ex)
{
throw new ContainerResolutionException(type, name, ex);
}
}
}
}
}
79 changes: 67 additions & 12 deletions src/Containers/Prism.Unity.Shared/UnityContainerExtension.cs
Expand Up @@ -19,7 +19,7 @@ internal partial
#endif
class UnityContainerExtension : IContainerExtension<IUnityContainer>, IContainerInfo
{
private IUnityContainer _currentScope;
private UnityScopedProvider _currentScope;

/// <summary>
/// The instance of the wrapped container
Expand Down Expand Up @@ -49,6 +49,11 @@ public UnityContainerExtension(IUnityContainer container)
}
#endif

/// <summary>
/// Gets the current <see cref="IScopedProvider"/>
/// </summary>
public IScopedProvider CurrentScope => _currentScope;

/// <summary>
/// Used to perform any final steps for configuring the extension that may be required by the container.
/// </summary>
Expand Down Expand Up @@ -281,7 +286,7 @@ public object Resolve(Type type, params (Type Type, object Instance)[] parameter
{
try
{
var c = _currentScope ?? Instance;
var c = _currentScope?.Container ?? Instance;
var overrides = parameters.Select(p => new DependencyOverride(p.Type, p.Instance)).ToArray();
return c.Resolve(type, overrides);
}
Expand All @@ -302,9 +307,9 @@ public object Resolve(Type type, string name, params (Type Type, object Instance
{
try
{
var c = _currentScope ?? Instance;
var c = _currentScope?.Container ?? Instance;

// Unit will simply return a new object() for unregistered Views
// Unity will simply return a new object() for unregistered Views
if (!c.IsRegistered(type, name))
throw new KeyNotFoundException($"No registered type {type.Name} with the key {name}.");

Expand Down Expand Up @@ -359,7 +364,7 @@ Type IContainerInfo.GetRegistrationType(Type serviceType)
/// <summary>
/// Creates a new Scope
/// </summary>
public virtual void CreateScope() =>
public virtual IScopedProvider CreateScope() =>
CreateScopeInternal();

/// <summary>
Expand All @@ -369,17 +374,67 @@ Type IContainerInfo.GetRegistrationType(Type serviceType)
/// <remarks>
/// This should be called by custom implementations that Implement IServiceScopeFactory
/// </remarks>
protected IUnityContainer CreateScopeInternal()
protected IScopedProvider CreateScopeInternal()
{
var child = Instance.CreateChildContainer();
_currentScope = new UnityScopedProvider(child);
return _currentScope;
}

private class UnityScopedProvider : IScopedProvider
{
if (_currentScope != null)
public UnityScopedProvider(IUnityContainer container)
{
_currentScope.Dispose();
_currentScope = null;
GC.Collect();
Container = container;
}

_currentScope = Instance.CreateChildContainer();
return _currentScope;
public IUnityContainer Container { get; private set; }
public bool IsAttached { get; set; }
public IScopedProvider CurrentScope => this;

public IScopedProvider CreateScope() => this;

public void Dispose()
{
Container.Dispose();
Container = null;
}

public object Resolve(Type type) =>
Resolve(type, Array.Empty<(Type, object)>());

public object Resolve(Type type, string name) =>
Resolve(type, name, Array.Empty<(Type, object)>());

public object Resolve(Type type, params (Type Type, object Instance)[] parameters)
{
try
{
var overrides = parameters.Select(p => new DependencyOverride(p.Type, p.Instance)).ToArray();
return Container.Resolve(type, overrides);
}
catch (Exception ex)
{
throw new ContainerResolutionException(type, ex);
}
}

public object Resolve(Type type, string name, params (Type Type, object Instance)[] parameters)
{
try
{
// Unity will simply return a new object() for unregistered Views
if (!Container.IsRegistered(type, name))
throw new KeyNotFoundException($"No registered type {type.Name} with the key {name}.");

var overrides = parameters.Select(p => new DependencyOverride(p.Type, p.Instance)).ToArray();
return Container.Resolve(type, name, overrides);
}
catch (Exception ex)
{
throw new ContainerResolutionException(type, name, ex);
}
}
}
}
}
1 change: 1 addition & 0 deletions src/Forms/Prism.Forms/Behaviors/PageBehaviorFactory.cs
Expand Up @@ -80,6 +80,7 @@ void IPageBehaviorFactory.ApplyPageBehaviors(Page page)
}

page.Behaviors.Add(new PageLifeCycleAwareBehavior());
page.Behaviors.Add(new PageScopeBehavior());
ApplyPageBehaviors(page);
}

Expand Down
24 changes: 24 additions & 0 deletions src/Forms/Prism.Forms/Behaviors/PageScopeBehavior.cs
@@ -0,0 +1,24 @@
using Xamarin.Forms;

namespace Prism.Behaviors
{
/// <summary>
/// Controls the Page container Scope
/// </summary>
public sealed class PageScopeBehavior : BehaviorBase<Page>
{
protected override void OnAttachedTo(Page page)
{
base.OnAttachedTo(page);
// Ensure the scope gets created and NavigationService is created
Navigation.Xaml.Navigation.GetNavigationService(page);
}

protected override void OnDetachingFrom(Page page)
{
base.OnDetachingFrom(page);
// This forces the Attached Property to get cleaned up.
page.SetValue(Navigation.Xaml.Navigation.NavigationScopeProperty, null);
}
}
}
16 changes: 0 additions & 16 deletions src/Forms/Prism.Forms/Ioc/IContainerExtensionExtensions.cs

This file was deleted.

8 changes: 7 additions & 1 deletion src/Forms/Prism.Forms/Navigation/PageNavigationService.cs
Expand Up @@ -782,7 +782,7 @@ protected virtual Page CreatePage(string segmentName)
try
{
_container.CreateScope();
var page = _container.Resolve<object>(segmentName) as Page;
var page = (Page)_container.Resolve<object>(segmentName);

if (page is null)
throw new NullReferenceException($"The resolved type for {segmentName} was null. You may be attempting to navigate to a Non-Page type");
Expand Down Expand Up @@ -830,6 +830,12 @@ protected virtual Page CreatePageFromSegment(string segment)

private Page SetNavigationServiceForPage(Page page)
{
// Someone explicitly set Autowire ViewModel
if (page.GetValue(Xaml.Navigation.NavigationServiceProperty) != null)
return page;

// This will wireup the Navigation Service in case you have something injected that
// actually required the Nav Service
var childNavService = _container.Resolve<INavigationService>();
if (childNavService is IPageAware pa)
pa.Page = page;
Expand Down

0 comments on commit 2896142

Please sign in to comment.