How to add new DLL locations to WebAPI's controller discovery? - asp.net-web-api

ASP.NET WebAPI has a much appreciated ability to discover ApiController classes in external DLLs even if those DLLs are not referenced. For example, I may have MyWebApiProject that has a set of ApiControllers. I could then create a completely separate project called MyApiProjectPlugin that contains ApiController classes also. I have been able to add the MyApiProjectPlugin.dll file to the bin folder with the first MyApiProject.dll and the original project will discover all the controllers in the plugin project. I really like that ability.
However, What I would like to do is be able to add the plugin project to a sub directory inside of the bin folder. Something like bin/plugins. When I tried this, the original MyApiProject was unable to discover the plugin's controllers.
Is there a simple way to get WebAPI to look for ApiController classes in the bin's subdirectories? If I can avoid rewriting a controller factory from scratch I would like to.

You can write an assembly resolver.
public class PluginsResolver : DefaultAssembliesResolver
{
public override ICollection<Assembly> GetAssemblies()
{
List<Assembly> assemblies = new List<Assembly>(base.GetAssemblies());
assemblies.Add(Assembly.LoadFrom(#"<Path>\MyApiProjectPlugin.dll"));
return assemblies;
}
}
In the Register method in WebApiConfig, register the resolver.
config.Services.Replace(typeof(IAssembliesResolver), new PluginsResolver());

Related

How to use controllers and views in another project in ASP.NET Core

If I have set up a project with lots of controllers and views, how could I use them in another project in ASP.NET Core? I tried refering the project but when I use the views in previous project it just return NOT FOUND.
While it is reasonable to create your own ControllerFactory, I found it more convenient to define all my Controllers in each project, but derive them from Controllers in my Shared project:
namespace MyProject1.Controllers
{
public class MyController : MySharedProject.Controllers.MyController
{
// nothing much to do here...
}
}
namespace MySharedProject.Controllers
{
public abstract class MyController : Microsoft.AspNetCore.Mvc.Controller
{
// all (or most) of my controller logic here...
}
}
This has the added benefit that you have a place to put your Controller logic that differs from project to project. Also, it is easier for other developers to quickly find your Controller logic because the Controllers exist in the standard place.
Regarding whether this is advisable, I think it absolutely is. I've created some common Account Management logic that I want to share between projects that otherwise have very different business logic. So I'm sharing my Account and Admin Controllers, but the other Controllers are specific to their respective projects.

Type controller does not have a default constructor

Hi friends in webapi using mvc format i created one webapi project here i added autofac resolver to resolve DI(dependency injection) its working fine then in next step i took this dlls and created new project for this i added the dlls now in this project i added one controller but it is showing the error 'x(controller name)' Type controller does not have a default constructor but for this many people given solution for this add new bootstrap class(which contain autofac for di resolve) but i am inherting my old global.asax file from my current global.asax file so i think dont want to add again all the stuff the autofac resolver will get from old dlls so i find many solutions but now luck can any one help me
Note : please ask me incase of any clarity (my english is not good excluse me for this)
The Mistake I did is Spelling mistake this is if Controller name is SampleController i am putting SampleContoller (missing r in Controller splleing) thats it now i corrected splleing now its working fine

using App.xaml.cs Reference in another project present in same solution?

hi i am having a different projects in my solution in the initial project (default project) i am accessing the global reference to App.xaml.cs in this way :-
App objref = (App)Application.Current;
But now i have added new project to my solution and trying to access the app.xaml.cs in the same way as defined earlier but i am not able to access app.xaml.cs ?
1)can i know the reason
2)What should i do if i want to use it in both the projects ?
Please let me know
Thanks in advance.
You can access it, but the new project will not be familiar with the derived App class that is in your project. To explain further we need to take inheritance into consideration.
There's a generic definition for the Application class that exposes a number of predefined methods. Your App.xaml.cs is a new class definition that is derived from the Application class. It has the methods it inherited plus what ever methods and properties that you've added. To make use of these any code that is seeking to use your extra properties or methods must have access to the class definition. Your classes in the other projects that you've added do not have access to this definition.
You'll need to make a class or interface definition that both projects have access to. There are several ways of organizing this. I'll present one.
Create your main project in the solution. This contains your
App.xaml.cs.
Create your class library project that contains the
other code.
Create a third project called Common that only contains
an Interface definition.
On the Interface definition define all of the methods/properties
that you want both your class library and main project to have
access too.
Have App.Xaml.cs implement this interface.
In the Class Library access var appReference =
(IMyInterfaceName)Applcation.Current. You'll have access to the
methods that were defined in the interface

MEF and MVC 3 - how to load embedded views dynamically from mef container?

I'm building an MVC 3 application where MEF is used. The main idea is to have plug-in mechanism where models, controllers and views are loaded dynamically during runtime from mef container.
Each plugin/module consists of two assemblies:
Module1.Data.dll (contains definitions of models)
Module1.Web.dll (contains controllers and views)
and are put in the Plugins directory inside web application bin:
WebApp/Bin/Plugins/Module1.Data.dll
WebApp/Bin/Plugins/Module1.Web.dll
WebApp/Bin/Plugins/Module2.Data.dll
WebApp/Bin/Plugins/Module2.Web.dll
WebApp/Bin/Plugins/ModuleCore.Data.dll
WebApp/Bin/Plugins/ModuleCore.Web.dll
etc...
There is also core module that is referenced by all other modules: ModuleCore.Data.dll and respectively ModuleCore.Web.dll.
Then, in Global.asax, container is build in the following way:
AggregateCatalog catalog = new AggregateCatalog();
var binCatalog = new DirectoryCatalog(HttpRuntime.BinDirectory, "Module*.dll");
var pluginsCatalot = new DirectoryCatalog(Path.Combine(HttpRuntime.BinDirectory, "Plugins"), "Module*.dll");
catalog.Catalogs.Add(binCatalog);
catalog.Catalogs.Add(pluginsCatalot);
CompositionContainer container = new CompositionContainer(catalog);
container.ComposeParts(this);
AppDomain.CurrentDomain.AppendPrivatePath(Path.Combine(HttpRuntime.BinDirectory, "Plugins"));
CustomViewEngine is created and registered and used for finding views in module assembly:
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new CustomViewEngine());
controller factory for loading controllers from container:
ControllerBuilder.Current.SetControllerFactory(new MefControllerFactory(_container));
and also custom virtual path provider for getting assemblies from container:
HostingEnvironment.RegisterVirtualPathProvider(new ModuleVirtualPathProvider());
Ok, so the whole infrastructure for handling pluggable models, controllers and views are ready. Now everything works... except one thing - strongly typed views.
To ilustrate the problem in more details, let's prepare the scene:
UserDTO model is located in Module1.Data.dll
ShowUserController.cs is located in Module1.Web.dll/Controllers/
Index.cshtml is located in Module1.Web.dll/Views/ShowUser (with declared #model Module1.Data.UserDto)
Now we do the following:
Run the application and go to HOST/ShowUser/Index (action method Index is executed on ShowUserController and view Index.cshtml is fetched)
After the view Index.cshtml is fetched - compilation starts (by RazorBuildProvider)
Exceptions is thrown: "cannot find Data type in namespace Module1", in other words UserDTO couldn't be found during building the view dynamically
So it seems that compiler/builder didnt look through bin/Plugins folder for Module1.Data.dll, because when I copied this file into bin folder - it worded fine.
Question/problem: why builder didn't look into bin/Plugins folder even though this directory was added by AppDomain.CurrentDomain.AppendPrivatePath method?
How to add private paths for assembly builder once so that plugins folder will be taken into consideration??
I have managed to do some work around by creating CustomRazorBuildProvider that overrides standard one:
public class CustomRazorBuildProvider : RazorBuildProvider
{
public override void GenerateCode(System.Web.Compilation.AssemblyBuilder assemblyBuilder)
{
Assembly a = Assembly.LoadFrom(Path.Combine(HttpRuntime.BinDirectory, "Plugins", "Module1.Data.dll"));
assemblyBuilder.AddAssemblyReference(a);
base.GenerateCode(assemblyBuilder);
}
}
but the drawback of this solution is that everytime the view is compiled, references to all assemblies in Plugins folder need to be added, which can cause performance issues later on when lots of plugins will be used.
Any nicer solutions?
Here is a thought.
If you follow the View Model Pattern then instead of sending the DTO's straight to the view use a ViewModel that is would be located in the same assembly as the View.
So Instead of:
UserDTO model is located in Module1.Data.dll
ShowUserController.cs is located in Module1.Web.dll/Controllers/
Index.cshtml is located in Module1.Web.dll/Views/ShowUser (with declared #model Module1.Data.UserDto)
You would have:
UserDTO model is located in Module1.Data.dll
ShowUserController.cs is located in Module1.Web.dll/Controllers/
UserVM located in Module1.Web.dll/ViewModels
Index.cshtml is located in Module1.Web.dll/Views/ShowUser (with declared #model Module1.Web.ViewModels.UserVM)
Have the Controller Map your DTO's to ViewModels
See AutoMapper to help with the Mapping

Automapping mapping lost when using it

I have this a web application and a dll which I use in that application. Both applications have their own automapper configurations.
I call both both configurations in global.asax but somehow the mapping in the dll is lost when I actually go to use it. I get an exception that mapping is not defined.
What do you think could be the reason?
In my MVC3 application, I have this AutoMapperConfiguration class.
class AutoMapperConfiguration {
Mapper.CreateMap<ClassA, classB>();
}
I have a separate dll named sep.dll which also has
class AutoMapperConfiguration {
Mapper.CreateMap<classC, classD>();
}
In my global.asax, I have this code in Application_start:
AutoMapperConfiguration.Configure();
sep.AutoMapperConfiguration.Configure();
Somewhere in my web application, I do
Mapper.Map(objC, new ObjOfClassD()); // mapping not found exception,
if I do this, it works
sep.AutoMapperConfiguration.Configure();
Mapper.Map(objC, new objD());

Resources