Skip to content

Instantly share code, notes, and snippets.

@rdavisau
Last active January 9, 2020 02:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rdavisau/09d57eed64aa89ea6222637b84128bc9 to your computer and use it in GitHub Desktop.
Save rdavisau/09d57eed64aa89ea6222637b84128bc9 to your computer and use it in GitHub Desktop.
public class DryIoCZeroWithFallbackContainerExtension : IContainerExtension<Container>
{
public DryIoc.Container Fallback { get; set; }
public Container Instance { get; }
public DryIoCZeroWithFallbackContainerExtension()
{
Instance = new Container();
}
private object ResolveFromStaticContainer(Type type, string key = null)
{
Debug.WriteLine($"Resolving {type} ({key}) from static container");
return Instance.Resolve(type, key);
}
private object ResolveFromFallback(Type type, string key = null)
{
Debug.WriteLine($"Resolving {type} ({key}) from fallback container");
return Fallback.Resolve(type, key);
}
private object ResolveFromStaticContainerWithParameters(Type type, object[] parameters, string key = null)
{
Debug.WriteLine($"Resolving {type} ({key}) from static container with parameters: {String.Join(", ", parameters)}");
return Instance.Resolve(type,
key,
args: parameters);
}
private object ResolveFromFallbackWithParameters(Type type, object[] parameters, string key = null)
{
Debug.WriteLine($"Resolving {type} ({key}) from fallback container with parameters: {String.Join(", ", parameters)}");
return Fallback.Resolve(type,
parameters,
IfUnresolved.Throw,
null,
key
);
}
private bool IsRegisteredInFallBack(Type type, string key = null)
{
return Fallback != null && Fallback.IsRegistered(type, key);
}
private bool IsRegisteredInStatic(Type type, string key = null)
{
return Instance.IsRegistered(type, key);
}
public object Resolve(Type type)
{
if (IsRegisteredInFallBack(type) || !IsRegisteredInStatic(type))
return ResolveFromFallback(type);
return ResolveFromStaticContainer(type);
}
public object Resolve(Type type, string name)
{
if (IsRegisteredInFallBack(type, name) || !IsRegisteredInStatic(type, name))
return ResolveFromFallback(type, name);
return ResolveFromStaticContainer(type, name);
}
public object Resolve(Type type, params (Type Type, object Instance)[] parameters)
{
var ps = parameters.Select(x => x.Instance).ToArray();
if (IsRegisteredInFallBack(type) || !IsRegisteredInStatic(type))
return ResolveFromFallbackWithParameters(type, ps);
return ResolveFromStaticContainerWithParameters(type, ps);
}
public object Resolve(Type type, string name, params (Type Type, object Instance)[] parameters)
{
var ps = parameters.Select(x => x.Instance).ToArray();
if (IsRegisteredInFallBack(type, name) || !IsRegisteredInStatic(type, name))
return ResolveFromFallbackWithParameters(type, ps, name);
return ResolveFromStaticContainerWithParameters(type, ps, name);
}
private bool IsPlaceholder(Type type, string key = null)
{
return true;
}
private void RegisterInstanceInFallback(Type type, object instance, string key = null)
{
CreateFallbackIfRequired();
Debug.WriteLine($"Registering {instance} as {type} ({key}) into fallback container");
Fallback.RegisterInstance(type, instance, IfAlreadyRegistered.Replace, serviceKey: key);
}
private void RegisterInstanceInStaticPlaceholder(Type type, object instance, string key = null)
{
Debug.WriteLine($"Registering {instance} as {type} ({key}) into static placeholder");
Instance.RegisterDelegate(type, _ => instance, serviceKey: key);
}
private void RegisterTypeInFallback(Type from, Type to, IReuse reuse, string key = null)
{
CreateFallbackIfRequired();
Debug.WriteLine($"Registering {to} as {from} ({key}) with reuse {reuse} into fallback container");
Fallback.Register(from, to, reuse: reuse, serviceKey: key);
}
private void CreateFallbackIfRequired()
{
if (Fallback != null)
return;
Debug.WriteLine("Creating fallback container.");
Factory UnknownResolver(Request request)
{
object FactoryDelegate(IResolverContext x) => Instance.Resolve(request.ServiceType, serviceKey: request.ServiceKey);
return new DelegateFactory(FactoryDelegate);
}
Fallback = new DryIoc.Container(
Rules.Default.WithAutoConcreteTypeResolution()
.With(Made.Of(FactoryMethod.ConstructorWithResolvableArguments))
.WithoutThrowOnRegisteringDisposableTransient()
.WithFuncAndLazyWithoutRegistration()
.WithDefaultIfAlreadyRegistered(IfAlreadyRegistered.Replace)
.WithoutFastExpressionCompiler()
.WithUnknownServiceResolvers(UnknownResolver));
}
public IContainerRegistry RegisterInstance(Type type, object instance)
{
if (IsPlaceholder(type))
RegisterInstanceInStaticPlaceholder(type, instance);
else
RegisterInstanceInFallback(type, instance);
return this;
}
public IContainerRegistry RegisterInstance(Type type, object instance, string name)
{
if (IsPlaceholder(type, name))
RegisterInstanceInStaticPlaceholder(type, instance);
else
RegisterInstanceInFallback(type, instance);
return this;
}
public IContainerRegistry RegisterSingleton(Type from, Type to)
{
RegisterTypeInFallback(from, to, Reuse.Singleton);
return this;
}
public IContainerRegistry RegisterSingleton(Type from, Type to, string name)
{
RegisterTypeInFallback(from, to, Reuse.Singleton, name);
return this;
}
public IContainerRegistry Register(Type from, Type to)
{
RegisterTypeInFallback(from, to, Reuse.Transient);
return this;
}
public IContainerRegistry Register(Type from, Type to, string name)
{
RegisterTypeInFallback(from, to, Reuse.Transient, name);
return this;
}
public bool IsRegistered(Type type)
{
var ret = IsRegisteredInFallBack(type) || Instance.IsRegistered(type);
Debug.WriteLine($"Returning {ret} for IsRegistered({type})");
return ret;
}
public bool IsRegistered(Type type, string name)
{
var ret = IsRegisteredInFallBack(type, name) || Instance.IsRegistered(type, name);
Debug.WriteLine($"Returning {ret} for IsRegistered({type}, {name})");
return ret;
}
public void FinalizeExtension()
{
}
}
// this isn't well tested so don't use it for anything important
// it definitely won't load non-nuget dependencies
public System.Reflection.Assembly LoadAssemblyWithDependencies(string assemblyPath)
{
var coreLibPath = assemblyPath;
var nuget = System.Environment.ExpandEnvironmentVariables(@"%USERPROFILE%\.nuget\packages");
var dependencyContext =
Microsoft.Extensions.DependencyModel.DependencyContextLoader
.Default.Load(System.Reflection.Assembly.LoadFrom(coreLibPath));
var libs =
dependencyContext
.RuntimeLibraries
.SelectMany(r => r.RuntimeAssemblyGroups.SelectMany(bb => bb.RuntimeFiles),
(r, bb) => new { r.Name, r.Version, bb.Path })
.Where(r => !r.Name.StartsWith("runtime."))
.Skip(1) // the dll comes first
.ToList();
foreach (var l in libs)
{
try
{
System.Reflection.Assembly.LoadFrom(System.IO.Path.Combine(nuget, l.Name, l.Version, l.Path));
}
catch (Exception ex)
{
// break here to make sure nothing important is failing
// non-package assemblies will definitely fail
}
}
var coreLib = System.Reflection.Assembly.LoadFrom(coreLibPath);
return coreLib;
}
public void RegisterPrismTypes(Assembly asm, IContainer container)
{
var types = new Dictionary<Type, Type>
{
[FindType("Prism.Navigation.INavigationService")] = FindType("Prism.Navigation.PageNavigationService"),
[FindType("Prism.Behaviors.IPageBehaviorFactory")] = FindType("Prism.Behaviors.PageBehaviorFactory"),
[FindType("Prism.Common.IApplicationProvider")] = FindType("Prism.Common.ApplicationProvider"),
[FindType("Prism.Logging.ILoggerFacade")] = FindType("Prism.Logging.EmptyLogger"),
[FindType("Prism.AppModel.IApplicationStore")] = FindType("Prism.AppModel.ApplicationStore"),
[FindType("Prism.Events.IEventAggregator")] = FindType("Prism.Events.EventAggregator"),
[FindType("Prism.Services.IPageDialogService")] = FindType("Prism.Services.PageDialogService"),
[FindType("Prism.Services.Dialogs.IDialogService")] = FindType("Prism.Services.Dialogs.DialogService"),
[FindType("Prism.Services.IDeviceService")] = FindType("Prism.Services.DeviceService"),
[FindType("Prism.Modularity.IModuleCatalog")] = FindType("Prism.Modularity.ModuleCatalog"),
[FindType("Prism.Modularity.IModuleManager")] = FindType("Prism.Modularity.ModuleManager"),
[FindType("Prism.Modularity.IModuleInitializer")] = FindType("Prism.Modularity.ModuleInitializer")
};
foreach (var t in types)
container.Register(t.Key, t.Value);
// nav service needs to be registered with a service key too
container.Register(
FindType("Prism.Navigation.INavigationService"),
FindType("Prism.Navigation.PageNavigationService"),
serviceKey: "PageNavigationService");
}
public void RegisterViews(Assembly asm, IContainer container)
{
var pageTypes = GetTypesFromAssembly(asm, t => t.IsSubclassOf(typeof(BasePage))).ToArray();
// register nav page
container.Register(typeof(object), FindType("Xamarin.Forms.NavigationPage"), serviceKey: "NavigationPage");
// register all our pages and matching vm
// assumes matching vm exists, 1->1 relationship, ignores namespaces, etc.
// this might not be valid for you, in which case this will blow up
foreach(var pageType in pageTypes)
{
var vmType = GetTypesFromAssembly(asm, t => t.Name == pageType.Name + "ViewModel").First();
// register page type
container.Register(pageType);
// prism wants it as a named registration against object, so maybe the above is unneccessary
container.Register(typeof(object), pageType, serviceKey: pageType.Name);
// register the vm
container.Register(vmType);
// keep track of the pair of types so we can generate our "register pages" method later
PageTypes.Add(($"{pageType.Namespace}.{pageType.Name}", $"{vmType.Namespace}.{vmType.Name}"));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment