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

Add ContainerProviderExtension #2069

Merged
merged 8 commits into from Jul 2, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Uno/Prism.Uno/Prism.Uno.csproj
Expand Up @@ -40,6 +40,7 @@
<ItemGroup>
<Compile Remove="..\..\Wpf\Prism.Wpf\Bootstrapper.cs" />
<Compile Remove="..\..\Wpf\Prism.Wpf\Interactivity\InvokeCommandAction.cs" />
<Compile Remove="..\..\Wpf\Prism.Wpf\Ioc\ContainerProviderExtension.cs" />
<Compile Remove="..\..\Wpf\Prism.Wpf\PrismApplicationBase.cs" />
<Compile Remove="..\..\Wpf\Prism.Wpf\PrismBootstrapperBase.cs" />
<Compile Remove="..\..\Wpf\Prism.Wpf\Properties\AssemblyInfo.cs" />
Expand Down
70 changes: 70 additions & 0 deletions src/Wpf/Prism.Wpf/Ioc/ContainerProviderExtension.cs
@@ -0,0 +1,70 @@
using System;
using System.Windows.Markup;

namespace Prism.Ioc
{
/// <summary>
/// Provides Types and Services registered with the Container
/// <example>
/// <para>
/// Usage as markup extension:
/// <![CDATA[
/// <TextBlock
/// Text="{Binding
/// Path=Foo,
/// Converter={prism:ContainerProvider {x:Type local:MyConverter}}}" />
/// ]]>
/// </para>
/// <para>
/// Usage as XML element:
/// <![CDATA[
/// <Window>
/// <Window.DataContext>
/// <prism:ContainerProvider Type="{x:Type local:MyViewModel}" />
/// </Window.DataContext>
/// </Window>
/// ]]>
/// </para>
/// </example>
/// </summary>
public class ContainerProviderExtension : MarkupExtension
{
/// <summary>
/// Initializes a new instance of the <see cref="ContainerProviderExtension"/> class.
/// </summary>
public ContainerProviderExtension()
{
}

/// <summary>
/// Initializes a new instance of the <see cref="ContainerProviderExtension"/> class.
/// </summary>
/// <param name="type">The type to Resolve</param>
public ContainerProviderExtension(Type type)
{
Type = type;
}

/// <summary>
/// The type to Resolve
/// </summary>
public Type Type { get; set; }

/// <summary>
/// The Name used to register the type with the Container
/// </summary>
public string Name { get; set; }

/// <summary>
/// Provide resolved object from <see cref="ContainerLocator"/>
/// </summary>
/// <param name="serviceProvider"></param>
/// <returns></returns>
public override object ProvideValue(IServiceProvider serviceProvider)
{
return string.IsNullOrEmpty(Name)
? ContainerLocator.Container?.Resolve(Type)
: ContainerLocator.Container?.Resolve(Type, Name);
}
}
}
2 changes: 1 addition & 1 deletion src/Wpf/Prism.Wpf/Properties/AssemblyInfo.cs
Expand Up @@ -22,5 +22,5 @@
[assembly: XmlnsDefinition("http://prismlibrary.com/", "Prism.Mvvm")]
[assembly: XmlnsDefinition("http://prismlibrary.com/", "Prism.Interactivity")]
[assembly: XmlnsDefinition("http://prismlibrary.com/", "Prism.Services.Dialogs")]

[assembly: XmlnsDefinition("http://prismlibrary.com/", "Prism.Ioc")]

@@ -0,0 +1,151 @@
using Prism.Ioc;
using Prism.IocContainer.Wpf.Tests.Support.Mocks;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Xaml;
using Xunit;

namespace Prism.Container.Wpf.Tests.Ioc
{
public class ContainerProviderExtensionFixture : IDisposable
{
private static readonly MockService _unnamedService = new MockService();
private static readonly IReadOnlyDictionary<string, MockService> _namedServiceDictionary = new Dictionary<string, MockService>
{
["A"] = new MockService(),
["B"] = new MockService(),
};

private static readonly IContainerExtension _containerExtension
= ContainerHelper.CreateContainerExtension();

static ContainerProviderExtensionFixture()
{
// Preload assembly to resolve 'xmlns:prism' on xaml.
Assembly.Load("Prism.Wpf");

_containerExtension.RegisterInstance<IService>(_unnamedService);
foreach (var kvp in _namedServiceDictionary)
{
_containerExtension.RegisterInstance<IService>(kvp.Value, kvp.Key);
}
_containerExtension.FinalizeExtension();
}

public ContainerProviderExtensionFixture()
{
ContainerLocator.ResetContainer();
ContainerLocator.SetContainerExtension(() => _containerExtension);
}

public void Dispose()
{
ContainerLocator.ResetContainer();
}

[Fact]
public void CanResolveUnnamedServiceUsingConstructor()
{
var containerProvider = new ContainerProviderExtension(typeof(IService));
var service = containerProvider.ProvideValue(null);

Assert.Same(_unnamedService, service);
}

[Fact]
public void CanResolveUnnamedServiceUsingProperty()
{
var containerProvider = new ContainerProviderExtension
{
Type = typeof(IService)
};
var service = containerProvider.ProvideValue(null);

Assert.Same(_unnamedService, service);
}

[Theory]
[InlineData("A")]
[InlineData("B")]
public void CanResolvedNamedServiceUsingConstructor(string name)
{
var expectedService = _namedServiceDictionary[name];

var containerProvider = new ContainerProviderExtension(typeof(IService))
{
Name = name,
};
var service = containerProvider.ProvideValue(null);

Assert.Same(expectedService, service);
}

[Theory]
[InlineData("A")]
[InlineData("B")]
public void CanResolvedNamedServiceUsingProperty(string name)
{
var expectedService = _namedServiceDictionary[name];

var containerProvider = new ContainerProviderExtension()
{
Type = typeof(IService),
Name = name,
};
var service = containerProvider.ProvideValue(null);

Assert.Same(expectedService, service);
}

private const string _xamlWithMarkupExtension =
@"<Window
xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
xmlns:prism='http://prismlibrary.com/'
xmlns:mocks='clr-namespace:Prism.IocContainer.Wpf.Tests.Support.Mocks;assembly=Prism.IocContainer.Wpf.Tests.Support'
DataContext='{prism:ContainerProvider mocks:IService}' />";

private const string _xamlWithXmlElement =
@"<Window
xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
xmlns:prism='http://prismlibrary.com/'
xmlns:mocks='clr-namespace:Prism.IocContainer.Wpf.Tests.Support.Mocks;assembly=Prism.IocContainer.Wpf.Tests.Support'>
<Window.DataContext>
<prism:ContainerProvider Type='mocks:IService' />
</Window.DataContext>
</Window>";

[Theory]
[InlineData(_xamlWithMarkupExtension)]
[InlineData(_xamlWithXmlElement)]
public void CanResolveServiceFromXaml(string xaml)
{
// Don't use StaTheoryAttribute.
// If use StaTheoryAttribute, ContainerLocator.Current will be changed by other test method
// and Window.DataContext will be null.

object dataContext = null;
var thread = new Thread(() =>
{
using (var reader = new StringReader(xaml))
{
var window = XamlServices.Load(reader) as Window;
dataContext = window.DataContext;
}
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
thread.Join();

Assert.Same(_unnamedService, dataContext);
}
}
}
Expand Up @@ -17,6 +17,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Fixtures\Bootstrapper\BootstrapperNullModuleManagerFixture.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Fixtures\Bootstrapper\BootstrapperRunMethodFixture.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Fixtures\Ioc\ContainerExtensionFixture.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Fixtures\Ioc\ContainerProviderExtensionFixture.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Mocks\NullLoggerBootstrapper.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Mocks\NullModuleCatalogBootstrapper.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Mocks\NullModuleManagerBootstrapper.cs" />
Expand Down