Move container registrations to a new assembly with Prism.Unity.Forms - xamarin.forms

I'm currently using Prism with Unity in a Xamarin.Forms project.
I'm using
protected override void RegisterTypes(IContainerRegistry containerRegistry) {}
to register my classes in App.xaml.cs, but as my project grows, so does this method / numbers of usings, etc.
I thought about offloading some of the registrations to their respective namespaces. That is, if I have a namespace for Users, I can have a class like this:
public sealed class UsersRegistry : IRegistry {
public UsersRegistry(IUnityContainer container)
{
container.RegisterType<IUserService, UserService>();
}
}
I started by using assembly scanning to find all classes that implement IRegistry, but I'm coming up a little short on how to create the class and pass in the container used by Prism.
I was attempting something like this:
var registries = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(type => typeof(IRegistry).IsAssignableFrom(type) && !type.IsInterface);
foreach (var type in registries) {
var instance = Activator.CreateInstance(type, <get unity container here>);
}
But I'm not certain this is the correct direction.
Also, it doesn't have to be this particular way. My main goal here is to break out the registrations for navigation and my various types into a more manageable system.
Any help would be appreciated.

Prism provides this functionality in the form of modules.

Related

Autofac Multiple Regsistrations to Single service. Simple Injector -> Autofac translation

I've developed a CQRS style database access framework based on Tripod and other inspirations but targeting .NET Standard and simplifying for easier use. I want to split the IoC into separate integration packages so consumers can get the type registration I'm currently doing internally easily without being locked into a specific IoC container. My issue is I've only really worked closely with SimpleInjector so not familiar with other systems and their nuances around how they handle specific scenarios. I have an iminent need to support Autofac so thought I'd try here to see if anyone can translate.
I have the following Simple Injector CompositionRoot static class:
public static void RegisterDatabase(this Container container, DbContextOptions<EntityDbContext> dbContextOptions, params Assembly[] assemblies)
{
var scopedLifeStyle = container.Options.DefaultScopedLifestyle;
//container.Register<ICreateDbModel, DefaultDbModelCreator>(scopedLifeStyle); // lifestyle c
container.RegisterInitializer<EntityDbContext>( //(container.InjectProperties);
handlerToInitialise => handlerToInitialise.ModelCreator = new DefaultDbModelCreator()
);
// Setup DbContext
var ctxReg = scopedLifeStyle.CreateRegistration(
() => new EntityDbContext(dbContextOptions),
container);
container.AddRegistration<IUnitOfWork>(ctxReg);
container.AddRegistration<IReadEntities>(ctxReg);
container.AddRegistration<IWriteEntities>(ctxReg);
}
In ASP.NET Core solutions I invoke the above from Startup.Configure(...) with:
var optionsBuilder = new DbContextOptionsBuilder<EntityDbContext>()
//.UseInMemoryDatabase("Snoogans");
.UseSqlServer(_config.GetConnectionString("DefaultConnection"));
container.RegisterDatabase(optionsBuilder.Options);
which allows me to switch out to an in memory database for unit testing if needed. EntityDbContext contains all my unit of work methods for calling onto the context without having to specify explicit DbSet for each table. The IUnitOfWork, IReadEntities and IWriteEntities interfaces all define methods on the EntityDbContext.
So I'm not sure how I'd go about making an Autofac module that allows scoped registration of the dbcontext with passed in DbContextOptions followed by multiple registrations of interfaces to this registration.
Does anyone know how this can be achieved?
I worked out the process and now have an AutoFac module. I was able to registermodule by instance of the class and also pass in the options when I instantiate. Because EntityDbContext implements the three interfaces I was registering separately in the Simple Injector scenario, AutoFac has the convenience of being able to just infer them and register with AsImplementedInterfaces()
public class EntityFrameworkModule : Module
{
private readonly DbContextOptions<EntityDbContext> _dbContextOptions;
public EntityFrameworkModule(DbContextOptions<EntityDbContext> dbContextOptions)
{
_dbContextOptions = dbContextOptions;
}
protected override void Load(ContainerBuilder builder)
{
// If the calling code hasn't already registered a custom
// ICreateDbModel then register the internal DefaultDbModelCreator
builder.RegisterType<DefaultDbModelCreator>()
.IfNotRegistered(typeof(ICreateDbModel))
.As<ICreateDbModel>();
// Expecting IUnitOfWork, IReadEntities and IWriteEntities to be registered with this call
builder.Register(c => new EntityDbContext(_dbContextOptions)
{
ModelCreator = c.Resolve<ICreateDbModel>()
})
.AsImplementedInterfaces()
.InstancePerLifetimeScope();
}
}

IContainerRegistryExtensions how to register an instance as singleton

I am trying to migrate an old Prism Xamarin Form project to latest Prism and XF version.
I'd like to register a factory for creating connections like this Func<SQLiteConnection>:
public class AndroidInitializer : IPlatformInitializer
{
string DbFilePath => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), "test.db3");
public void RegisterTypes(IContainerRegistry container)
{
container.RegisterSingleton<Func<SQLiteConnection>>(() => new SQLiteConnection(DbFilePath));
}
}
Howeve this doesn't work, there's no overload which takes an instance like I was used to do in old Prism Unity version.
The ContainerRegistry is intentionally basic to handle the 90+% of registrations that you need in a consistent manner regardless of which container you're using. You can continue to use the underlying container for more advanced registrations.
For both DryIoc and Unity it would be:
containerRegistry.GetContainer().SomeContainerSpecificMethod();
where SomeContainerSpecificMethod would match what you had in Prism 6.3
Another possibility is to use RegisterInstance to register a single instance of a class. Not sure if this has implications for object lifetime though.
var connection = new SQLiteConnection(DbFilePath)
container.RegisterInstance(connection);

ASP.NET Core Identity - UserManager and UserStore woes

I'm trying to implement the Identity system in an ASP.NET Core app (RC2 libraries) and there is a particular hangup that is driving me crazy.
First of all, I am not using EntityFramework. I'm not even using SQL. I'm backing up to RavenDB, so I need the implementation to be very specific to that; Which isn't a problem.
So I designed a RavenUserStore class, and it looks like this;
public class RavenUserStore<TUser> :
IUserStore<TUser>,
IUserLoginStore<TUser>,
IUserPasswordStore<TUser>,
IUserRoleStore<TUser>,
IUserSecurityStampStore<TUser>,
IUserClaimStore<TUser>,
IUserLockoutStore<TUser>,
IUserTwoFactorStore<TUser>,
IUserEmailStore<TUser> {
// ...
}
Works great on its own. I've implemented all the methods, etc. It's wonderful. Very clean and efficient.
Now, I go over to my web application and wire things up;
services.AddTransient<ILookupNormalizer>(s => new LowerInvariantLookupNormalizer());
services.AddTransient<IPasswordHasher<Member>>(s => new PasswordHasher<Member>());
services.AddTransient<IUserStore<Member>, RavenUserStore<Member>>();
services.AddIdentity<Member, Role>(o => {
o.Password.RequiredLength = 6;
o.Password.RequireDigit = true;
o.Password.RequireLowercase = false;
o.Password.RequireUppercase = false;
})
.AddUserStore<RavenUserStore<Member>>()
.AddRoleStore<RavenRoleStore<Role>>();
So I go make a controller to use this, per all the samples I've seen, and the very core sample from the Identity Framework Github Repository
//... [PROPERTIES]...//
public AccountController(UserManager<Member> userManager, SignInManager<Member> signInManager) {
// ... [attach constructor parameters to properties] ...//
}
Alright, so I inspect the classes carefully.
UserManager<T> has a property Store,which is a type of IUserStore<T>.
So theoretically.. if the dependency injection resolves types of IUserStore<T> to RavenUserStore<T> when they are injected through a constructor.. shouldn't that mean that the UserManager<T> gets a RavenUserStore<T> as its Store property?
I thought it would too; But when I call methods on the UserManager, it DOES NOT call the ones on my RavenUserStore. Why is this? What can I do?
Do I really have to ALSO make a custom UserManager class and do all of those methods AGAIN?
You need to add your own custom providers before calling services.AddIdentity(). Internally, AddIdentity uses TryAddScoped() which only adds the default items if they don't already exist in the services container.
So just putting the call to AddIdentity() after you registered all your custom implementations should mean that they will take precedence as you expect.

Controller reuse in asp.net mvc

I have a two different projects that are actually two different websites when deployed, say WebA & WebB. The structure of both websites is the same in terms of the controllers and models they use. However, the front end is very different. They each have their own UI and own jquery plugins that work for them.
To reduce code duplication, I am proposing both those projects inherit from controllers in another referenced project[controllerDLL]. ControllerDLL is a project that will have all the logic for calling the business layer and returning json serialized model objects. WebA & WebB will each just have empty controller classes that just inherit from the base project[controllerDLL]. This way I feel the controller code is not duplicated.
Can anyone tell me a better way to achieve controller reuse other than the way I have proposed? Does asp.net mvc 4 provide any built-in way to do this better? Can I use AREAS here productively? Is DotNetNuke something I should look at? Or is my approach the best way forward? I am not looking for the how to move controllers into another project. I just want to know what my options are for achieving controller reuse.
Thanks.
Take a look at this SO question. You could keep all the views in the same project and use a custom view engine to find the views (based off a web.config setting).
For example you could have 2 folders /Views/WebA and /Views/WebB. The custom view engine could look up the web.config setting to find out which folder to use and search for the views there. This way you would not have to duplicate the controller code or move it into a separate project. It will just swap out one presentation layer for another at runtime.
You should be able to achieve this by implementing custom controller factory to instantiate the right controller class based on config settings.
You need to implement the interface System.Web.Mvc.IControllerFactory.The two methods in this interface are:
1.System.Web.Mvc.IController CreateController(System.Web.Routing.RequestContext requestContext, string controllerName) - To create the controller instance based on RequestContext and controller name.
2.void ReleaseController(System.Web.Mvc.IController controller) - Release the controller instance
Detailed information about using that interface is available
Since the difference between the two applications are the views, you don't need to have them as separate projects. you could have two sets of views and deploy the same project twice using different settings in the web config. To do this, you'll have to implement your own ViewEngine so that the controller can return the correct right views.
public class AppSettingViewEngine: RazorViewEngine
{
public AppSettingViewEngine()
{
string[] viewLocations = new string[] { };
if (ConfigurationManager.AppSettings["Site"] == "WebA")
{
viewLocations = new[] {
"~/WebB/Views/{1}/{0}.cshtml",
"~/WebB/Views/{1}/{0}.cshtml",
"~/WebB/Views/Shared/{0}.cshtml",
"~/WebB/Views/Shared/{0}.cshtml",
};
}
if (ConfigurationManager.AppSettings["Site"] == "WebB")
{
viewLocations = new[] {
"~/WebB/Views/{1}/{0}.cshtml",
"~/WebB/Views/{1}/{0}.cshtml",
"~/WebB/Views/Shared/{0}.cshtml",
"~/WebB/Views/Shared/{0}.cshtml",
};
}
else
{
//Default Settings
viewLocations = new[] {
"~/Views/{1}/{0}.cshtml",
"~/Views/{1}/{0}.cshtml",
"~/Views/Shared/{0}.cshtml",
"~/Views/Shared/{0}.cshtml",
};
}
this.PartialViewLocationFormats = viewLocations;
this.ViewLocationFormats = viewLocations;
}
}
Then you register it in your Application_Start as follows
protected void Application_Start()
{
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new AppSettingViewEngine());
}

Get instance of type inheriting from base class, implementing interface, using StructureMap

Continuing on my quest for a good plugin implementation I have been testing the StructureMap assembly scanning features.
All plugins will inherit from abstract class PluginBase. This will provide access to common application services such as logging. Depending on it's function, each plugin may then implement additional interfaces, for example, IStartUpTask.
I am initializing my plugins like so:
Scan(x => {
x.AssembliesFromPath(HttpContext.Current.Server.MapPath("~/Plugins"),
assembly => assembly.GetName().Name.Contains("Extension"));
x.AddAllTypesOf<PluginBase>();
});
The difficulty I am then having is how to work against the interface (not the PluginBase) in code. It's easy enough to work with PluginBase:
var plugins = ObjectFactory.GetAllInstances<PluginBase>();
foreach (var plugin in plugins)
{
}
But specific functionality (e.g. IStartUpTask.RunTask) is tied to the interface, not the base class.
I appreciate this may not be specific to structuremap (perhaps more a question of reflection).
Thanks,
Ben
Do you know all of the specific interfaces at registration time? If so, you can make a custom registration convention that registers each type with the plugin "family" of the interface it implements. An IRegistrationConvention gets each type, one at a time. You could do a simple check to see if the current type implements the desired interface, and if so, add it.
if (typeof(IStartUpTask).IsAssignableFrom(currentType)){
For<IStartUpTask>().Add(currentType);
}
Then later in the code, you can retrieve plugins for each specific interface individually:
var startupTasks = ObjectFactory.GetAllInstances<IStartUpTask>();
This approach has the benefit of allowing you to inject an enumerable of your custom interface plugins into a class that needs them, instead of making the service location call.
Alternatively, if you don't want to make a registration convention, you can just do the filtering at runtime using the handy OfType linq extension method:
var startupTasks = ObjectFactory.GetAllInstances<PluginBase>().OfType<IStartupTask>();
In case it helps others, I followed Joshua's advice and added my own registration convention:
public class PluginConvention : IRegistrationConvention
{
public void Process(Type type, Registry registry) {
if (type.BaseType == null) return;
if (type.BaseType.Equals(typeof(PSAdmin.Core.Domain.PluginBase))) {
if (typeof(IStartUpTask).IsAssignableFrom(type)) {
registry.For<IStartUpTask>()
.TheDefault.Is.OfConcreteType(type);
}
}
}
}
I couldn't get the .Add method to work, no matter what I tried, so had to use TheDefault.Is.OfConcreteType(type).
Then in my bootstrapper I am scanning like so:
Scan(x => {
x.AssembliesFromPath(HttpContext.Current.Server.MapPath("~/Plugins"),
assembly => assembly.GetName().Name.Contains("Extension"));
x.Convention<PluginConvention>();
});
I can then grab my IStartUp task types like so:
var plugins = ObjectFactory.GetAllInstances<IStartUpTask>();
foreach (var plugin in plugins)
{
plugin.Configure();
}
That said, after reading up on some of the new features of StructureMap, I'm not sure I need to do any of the above. For example I could just change my Scan delegate function to:
Scan(x => {
x.AssembliesFromPath(HttpContext.Current.Server.MapPath("~/Plugins"),
assembly => assembly.GetName().Name.Contains("Extension"));
x.AddAllTypesOf<PluginBase>();
});
And to use my interface concrete types (that inherit from PluginBase):
var tasks = ObjectFactory.Model.GetAllPossible<IStartUpTask>();
foreach (var task in tasks)
{
task.Configure();
}
Both methods seem to achieve the same thing.

Resources