AutoMapper not detecting profiles in external assemblies - asp.net-core

I am using ASP.Net core with latest Automapper. This is related to this and this. I think I am doing what needs to be done according to those questions, but still I get below error from Automapper.
AutoMapper.AutoMapperMappingException: Missing type map configuration or unsupported mapping.
Mapping types:
ToDoDto -> CreateTodoCommand
Module.Todo.Domain.Dto.ToDoDto -> Module.Todo.Domain.Commands.CreateTodoCommand
at lambda_method19(Closure , ToDoDto , CreateTodoCommand , ResolutionContext )
I have an interface which will be implemented by classes that needs to participate in mappings.
public interface IMapFrom<T>
{
void Mapping(Profile profile) => profile.CreateMap(typeof(T), GetType());
}
But mapping classes can be in many assemblies since I have a plugin architecture. So, lot of stuff are being loaded dynamically by the host application. My Assembly locating method use below line to load Assembly in a loop until it finishes loading all modules.
AssemblyLoadContext.Default.LoadFromAssemblyPath(file.FullName); //file.FullName is full path to Dll like E:\Project\Modules\Module.Todo\bin\Module.Todo.dll
Once it's done I can see loaded assemblies in watch window
Above loading method gets called in ConfigureServices(). I know this should work because the same assembly collection is passed to MediatR services.AddMediatR(assemblies) as well as Automapper services.AddAutoMapper(assemblies);
MediatR scans and find all Commands etc. but Automapper fails to locate mappings from other assemblies. However Automapper loads profiles properly from directly linked assemblies via Assembly.GetExecutingAssembly().GetReferencedAssemblies()
What may be the problem?

I got my assemblies to load properly and setup the mappings to work by making my mappings by inheriting Profile class like
public class MyProfile:Profile
{
public MyProfile()
{
CreateMap<A,B>();
}
}
and not using
public interface IMapFrom<T>
{
void Mapping(Profile profile) => profile.CreateMap(typeof(T), GetType());
}

Related

Unable to create a View in ASP.NET Core MVC

I am working on an ASP.NET Core 7.0 MVC app. I have created a data access layer using EF Core 7.0 with a database-first approach. I was trying to create a Razor view through the "AddView" option from the controller for the "Create" process.
However, I am getting this error:
This is how I inject my DbContext:
builder.Services.AddDbContext<NorthwindContext>(options => options.UseSqlServer(
builder.Configuration.GetConnectionString("DefaultConnection")));
I am new to ASP.NET Core. It looks like I have to pass additional options in the Program.cs file. I did a Google search, but couldn't find any help!
If I create the view manually it might work. However, I would like to create it through the scaffolding process.
This is my NorthwindContext code:
After removing the parameterless constructor as per Neil I am getting the new error:
More specifically to my comment, the AddDbContext registers the context class in the asp.net core DI container so it can be provided whenever any class or process wants an instance of the DbContext.
The view generator will want that. However, if the DI container find a parameterless constructor it will use that first, and therefore not use the constructor that passes in the options.
The outcome is a context is provided that does not have the "UseSqlServer" options set.
Hence the error that a database provider has not been configured.
Remove that parameterless constructor from the DbContext and you should be good to go.
The latest error indicates the constructor requires an object of type DbContextOptions.But the injector cannot create the instance.
You could try with the parameterless constructor and configure the options in OnConfiguring method
And the picture you've shown indicates you've seprated Modes from your MVC projects,Make sure you've setted the right startup project(right click on your solution -- Set startup projects) and configrue which projects would contain the migration classes
public class SomeDbContext : DbContext
{
public SomeDbContext()
{
}
public DbSet<SomeEntity> SomeEntity { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("connectionstring", b => b.MigrationsAssembly("MVCProj"));
}
}
Regist it as below:
services.AddDbContext<SomeDbContext>();
If you still got the error,please upload the minimal codes that could reproduce the error

How do I auto-register/resolve services by their interface?

I'm developing a MVC .NET Core 3/Angular/Entity Framework application. My application will likely have a lot of repositories in it. To support this I would like to resolve the service (repository) by its default interface (i.e. I request IRepository and the DI resolver gives me Repository). I know I can manually wire up the dependencies, but all of my DI frameworks in the past have had a means of auto-registering/resolving based on patterns or the like.
For example, with Castle Winsdor I could wire it up like this:
container.Register(Classes
.FromAssemblyNamed("MyLibary.Repository")
.InNamespace("MyLibrary.Repository")
.WithService.DefaultInterfaces()
.LifestyleTransient()
);
This would register all classes in the MyLibrary.Repository namespace to be resolved by their default interfaces they implement. How can this be done in .NET Core 3? If this isn't built-in, I'm assuming I will have to use reflection to query all the classes in the assembly, iterate through each class and register it by its interface it implements.
I would recommend AutoRegisterDI lib to not reinvent a bicycle. It's fast, simple and based on Microsoft Dependency Injection. This benchmark will show you the speed difference.
And this is the article about how to use it
Install this package:
NetCore.AutoRegisterDi
Add this code to your program.cs:
builder.Services.RegisterAssemblyPublicNonGenericClasses()
.Where(c => c.Name.EndsWith("Service"))
.AsPublicImplementedInterfaces(ServiceLifetime.Scoped); // default is Transient
If you have a Singleton service , add [RegisterAsSingleton] on top of your service class.
If you want to ignore injection of an particular interface add this line after .Where() :
.IgnoreThisInterface<IMyInterface>()
you can do this with reflection in c#.first create an extension method like this :
public static void AddScopedServices(this IServiceCollection serviceCollection)
{
}
and use it in startup.cs ,ConfigureServices method : services.AddScopedServices();
now to implement this method like this:
var allProviderTypes = System.Reflection.Assembly.GetAssembly(typeof(ICartRepository))
.GetTypes().Where(t => t.Namespace != null).ToList();
foreach (var intfc in allProviderTypes.Where(t => t.IsInterface))
{
var impl = allProviderTypes.FirstOrDefault(c => c.IsClass && intfc.Name.Substring(1) == c.Name);
if (impl != null) serviceCollection.AddScoped(intfc, impl);
}
you just need to put all your interfaces in a namespace and introduce one of them in this method ,in my case I used ICartRepository ,so it takes all interfaces and search for classes which inherit from that interface.
one point you need to consider is that class names must be like interfacenames without 'I' in the beginning ,like 'CartRepository'
I recently got tired of writing the same old
services.AddTransient<IInterface,IImplementation>
so I created a simple library to help me auto-register services.
You can check it out here.
Register the library in Startup.cs
There are 3 interface -> ITransient, IScoped, ISingleton generic and non-generic versions.
Let's say you want to register service "TestService" as transient.
TestService: ITransient<ITestService>, ITestService
Inherit ITransient interface and its done.
For more detailed information please refer to the Readme section.
Currently it lacks registering services with implementation factory, but it's something I'm willing to do in the near future.

Lost scope using DependencyResolver in another class library, using Castle Windsor and FluentNhibernate and NHibernate

In my WCF project I register my interface using Castle Windsor in the global.asax:
Component.For<IStrategy>()
.ImplementedBy<MyStrategy>()
.LifestylePerWcfOperation(),
Then later on in the same file I configure NHibernate using FluentNhibernate using a provider:
FluentConfiguration configuration = Fluently.Configure()
.Database(
MsSqlConfiguration.MsSql2008.ConnectionString(myConnString)
.Provider<ConnectionProvider>())
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<TenantMap>()) etc etc
However this ConnectionProvider is hosted in a common shared dll library as there are other WCF services that need to use it. I want to register this dependency as below but it doesn't work which means I have to manually new up a instance.
public ConnectionProvider()
{
// doesnt work
using (IDependencyScope scope = GlobalConfiguration.Configuration.DependencyResolver.BeginScope())
{
_myStrategy = scope.GetService<IStrategy>();
}
}
Is there anyway to make this work? Its like its lost scope as its in another assembly now. In the same assembly the DependencyScope works fine and creates an instance, but I want to keep it in a shared dll.
EDIT: The error I get is "System.Web.Http.Dependencies.EmptyResolver" on a watch on this bit of code: GlobalConfiguration.Configuration.DependencyResolver
I can see several problems with the code above:
1) You are registering IStrategy,MyStrategy with a per WcfOperation lifestyle. This means that windsor will create a strategy for each WcfOperation. On the otherhand you are trying to manually set the lifestyle of the component by using scope.GetService. For scope.GetService to work you will need a lifestyle scoped.
2) Assuming that the code for ConnectionProvider above is the constructor, it seems that the constructor is trying to get something from the container. This is in general a bad idea, and even worse when using an Ioc container like windsor. Instead pass the IStrategy to the constructor (inject it).
3) Seeing that you are calling the container a constructor here, probably means that you are not adhering to the principle that there should be only 3 calls to the container, 1 to register component, 1 to retrieve a top level component, and 1 to release the container.
I suggest you read a bit more about depedency injection and Ioc containers in general to fully understand how to work with this container.

How do I use Enterprise Library to specify config for only one DLL or subsystem?

How can I use EL to specify trace listeners and formatters for only one subsystem (say, DLL) of a large system?
I'm writing a sub-module of a very large large application (thick client, not web app) that uses a gateway/portal model to load its various subsystems. I'd like use EL 5.0 (already in use) to manage logging/tracing configuration, but just for my subsystem. It looks like app.config is getting converted by VS to foo.dll.config. Can I convince EL to pick up the settings in foo.dll.config (at run-time) and merge them into its existing configuration data, in memory? (Not into a merged config file.)
(Looks like, based on Enterprise Library Class Library App.Config, it can't be done.)
I wouldn't recommend trying to merge configuration -- it can can get messy.
Instead, I would configure your sub-module to configure and use private references to the appropriate Enterprise Library objects you need.
I'm assuming your module is a class library that has an app.config. When compiled it generates a module.dll.config file in the output directory. To take an example of logging using Enterprise Library 6, I would create a helper class to load your own configuration and maintain a reference to your module's LogWriter. In this way your module loads its own configuration and does not interfere and is not influenced by other configuration in the application.
namespace MyModule
{
public class MyModuleClass
{
public void DoSomething()
{
MyModuleLogger.LogWriter.Write("MyModule Test", "General");
}
}
public static class MyModuleLogger
{
private static Lazy<LogWriter> logWriter = new Lazy<LogWriter>(() =>
{
FileConfigurationSource configSource =
new FileConfigurationSource("MyModule.dll.config");
LogWriterFactory factory = new LogWriterFactory(configSource);
return factory.Create();
});
public static LogWriter LogWriter
{
get { return logWriter.Value; }
}
}
}
I've used a static class but you could use a variety of approaches that may fit with your design (singleton, factory, etc.)
You will have to ensure that your dll.config file is deployed along with your assembly. You could also hit problems if there are version issues with Enterprise Library (e.g. you are using Version 6 while the application uses Version 5.)

ActionFilter is not executing in WebApplication but executing in WebApi

I have created an ActionFilterAttribute
public class LoggingNHibernateSessionAttribute : ActionFilterAttribute
The purpose of the filter as name indicates is logging and opens and commits a transaction, before and after target action respectively.
I have a WebApi(myAPI) project (MVC4) and a WebApplication(myContent).
Each Api Controller in myAPI is decorated with this attribute.
using myApp.Web.Common.Filters;
namespace myAPI.Web.Api.Controllers
{
[LoggingNHibernateSession]
public class CategoriesController : ApiController
{
When a Http action (Get/Post) is executed inside the ApiController, the ActionFilter gets executed and it works fine as expected.
The problem:
In the WebApplication(myContent), I have decorated those controllers as well with the ActionFilter.
using myApp.Web.Common.Filters;
namespace myContent.Web.Content.Places.Controllers
{
[LoggingNHibernateSession]
public class PlacesController : Controller
{
But here, when an action is executed inside the controller, the ActionFilter is not getting executed.
The ActionFilter belongs to System.Web.Http.Filters;
I have read some posts, and they said to use System.Web.Mvc filters. So I changed the ActionFilter to be from System.Web.Mvc
And when I switched that, the ActionFilter stopped working in WebApi as well.
What am I doing wrong here?
Although WebApi and MVC are very similar and technically consist of largely the same code, WebApi was created by copying all the code rather than through reuse. This happened, I'm told, because the WCF team didn't want a dependency on the MVC assemblies.
Therefore, code (such as your custom filter) compiled against one assembly will not work in the context of the other.
The solution is to duplicate your own code and compile against both sets of assemblies. You could also set up a build system to cross-compile the same code files using different reference assemblies.
It's truly sad when internal company politics result in something like this. The least they could do was acknowledge the problem and add some proper cross-compile support to their tooling, but alas, I digress.