I am writing some Web API application, where I have 4 basic layers - API, BusinessLogic(which I call BusinessServices), DAL (which using EF to speak with the database), and EntitiesData(where I have my entities).
API calls businessService, bs ask DAL, DAL using EF is asking database about my EntitiesData.
Ok, now what's the problem ;)
On the BusinessServices, I want to map entities to some DTO, which I can return to API.
I wanted to use AutoMapper, but on tutorials, there are really simple examples, which I understand.
The first question: Should I use 2 IoC containers? Or maybe move my IOC from API to the business services layer?
1st Container is on API level and it contains BusinessServices (like UsesrsService, MessageService, etc.)
The second container would be at BusinessServices level - I want to use it to store my AutoMapper maps.
And this is the second question - what should I do with AutoMapper.
I know, how to create the configuration, did sth like this:
private void Congifure()
{
if(!(configuration == null))
return;
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<User, UserDto>();
cfg.CreateMap<Message,MessageDto>();
});
}
but what should I do now? pack it to the IoC container?
From which place in the code I should call my class which is configuring mapper?
In businessServices I have only my business-logic classes and DTO's.
You can pack it into your Startup.cs ConfigureServices method:
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<User, UserDto>();
cfg.CreateMap<Message,MessageDto>();
});
var mapper = config.CreateMapper();
services.AddScoped<AutoMapper.IMapper>(c => mapper);
And than inject it into your classes:
public class MyService
{
public MyService(IMapper mapper)
{
...
}
}
I would use one mapper, and put it somewhere vertically to your layer like into "helpers" project. Your mapper has to map between different layers so it should sit "between" them. Just move the logic of creation of MapperConfiguration into separate project and call it from your Startup.cs.
The very first thing in the automapper docs speaks about initialization. This should be done where ever you are bootstrapping your IOC container.
You only need 1 IOC container, making 2 would kind of make them useless as you would have broken the dependency tree into 2 halves.
And you should consider using mapping profiles for your different layers.
Related
I try to implement Microservice Architecture in my project. I was wondering can i create 1 method for Depedency Injection there is to implement EntityFrameworkCore in many services so i didnt DRY.
So what iam thinking is i still create my own DbContext, and than when i want to register this i just put it in my json file and add something like services.UseSql() in my startup.cs and inside UseSql is using Configuration from .json file.
what i try to say is something like generic DbContext.
I have some example to this but is using MongoDb :
Example
public static void AddMongo(this ContainerBuilder builder)
{
builder.Register(context =>
{
var configuration = context.Resolve<IConfiguration>();
var options = configuration.GetOptions<MongoDbOptions>("mongo");
return options;
}).SingleInstance();
builder.Register(context =>
{
var options = context.Resolve<MongoDbOptions>();
return new MongoClient(options.ConnectionString);
}).SingleInstance();
builder.Register(context =>
{
var options = context.Resolve<MongoDbOptions>();
var client = context.Resolve<MongoClient>();
return client.GetDatabase(options.Database);
}).InstancePerLifetimeScope();
builder.RegisterType<MongoDbInitializer>()
.As<IMongoDbInitializer>()
.InstancePerLifetimeScope();
builder.RegisterType<MongoDbSeeder>()
.As<IMongoDbSeeder>()
.InstancePerLifetimeScope();
}
If I understand your problem statement correctly, you want multiple microservices to connect to the same database. As comments above suggest, you really shouldn't do that as you'd essentially be coupling your microsrvices through your data layer - which can be messy if your microservices grow into incompatible data layers. This would eliminate a lot of benefits of the architecture and leave you to deal with pains. So if that is indeed the case you might want to consider revisiting your arсhitecture and trying to figure out whether you need a new microservice that fronts this common data store and provides it to others.
In reality however you sometimes must share code between projects and there's a official way to do that: extract the common code into a nuget package, host it somewhere in your private enterprise repository (if you care) and reference it from your other projects.
I'm setting up an ASP.NET Core project and following a CQRS pattern basing my work on the Tripod project. I've followed the Simple Injector integration guide but a little puzzled on one point... I want to keep make use of the UseInMemoryDatabase option for testing and can only find examples of that using the Core DI AddDbContext method.
My DbContext implement several interfaces:
public class EntityDbContext : DbContext,
IUnitOfWork,
IReadEntities,
IWriteEntities
{
// code here
}
I'm doing this in the Startup.ConfigureServices method:
services.AddDbContext<EntityDbContext>(options => options.UseInMemoryDatabase("Snoogans"));
and following the SI integration guide this in the Startup.Configure method:
container.Register<IUnitOfWork, xxxx>();
container.Register<IReadEntities, xxxx>();
container.Register<IWriteEntities, xxxx>();
Am I able to get the Core registration over crosswire to plugin to the target for each of them or should I just be registering the context directly in SI?
==
I'm playing around with the original concept from Tripod:
var contextRegistration =
lifestyle.CreateRegistration<EntityDbContext, EntityDbContext>(container);
container.AddRegistration(typeof(EntityDbContext), contextRegistration);
container.AddRegistration(typeof(IUnitOfWork), contextRegistration);
container.AddRegistration(typeof(IWriteEntities), contextRegistration);
container.AddRegistration(typeof(IReadEntities), contextRegistration);
trying to do everything with SI, but not sure how I get at the Registration for the 3 interfaces:
container.Register<EntityDbContext>(() =>
{
var optionsBuilder =
new DbContextOptionsBuilder<EntityDbContext>().UseInMemoryDatabase("Snoogans");
return new EntityDbContext(optionsBuilder.Options);
});
container.AddRegistration<IUnitOfWork>(xxxx);
container.AddRegistration<IReadEntities>(xxxx);
container.AddRegistration<IWriteEntities>(xxxx);
Am I able to get the Core registration over crosswire to plugin to the target for each of them or should I just be registering the context directly in SI?
Both options are feasible. You can chose to register the DbContext directly into Simple Injector. This would typically be the most obvious choice as your DbContext is an application component. Application components should typically be registered in your application container (i.e. Simple Injector) instead of in the framework's registration system (i.e. ServiceCollection).
When it comes to registering DbContext, however, there is some tight integration with the ASP.NET Core configuration system. This can make it more straightforward to do the registration there and simply cross-wire from Simple Injector. That lets the configuration system stay in control over the creation and destruction of the DbContext. This becomes especially valuable when doing things like DbContext pooling, as this pooling functionality is tightly coupled with this configuration and registration API.
Typically, both options are pretty straightforward, but because your DbContext implements multiple interfaces that you want to register seperately, this causes your registrations to become more complicated. This would have been the case as well in case you're using the built-in DI Container as well, so this is not specific to Simple Injector.
In your case, doing the registrations purely in Simple Injector would look like this:
var reg = Lifestyle.Scoped.CreateRegistration(() =>
{
var optionsBuilder =
new DbContextOptionsBuilder<EntityDbContext>().UseInMemoryDatabase("Snoogans");
return new EntityDbContext(optionsBuilder.Options);
},
container);
container.AddRegistration<IUnitOfWork>(reg);
container.AddRegistration<IReadEntities>(reg);
container.AddRegistration<IWriteEntities>(reg);
In case you chose to cross-wire your DbContext from the .NET Core configuration system, your configuration would look as follows:
// Add to the built-in ServiceCollection
services.AddDbContext<EntityDbContext>(options => options.UseInMemoryDatabase("Snoogans"));
// Cross-wire in Simple Injector
container.CrossWire<EntityDbContext>(app);
// Pull that registration out of Simple Injector and use it for the interface registrations
var reg = container.GetRegistration(typeof(EntityDbContext)).Registration;
// Same as before
container.AddRegistration<IUnitOfWork>(reg);
container.AddRegistration<IReadEntities>(reg);
container.AddRegistration<IWriteEntities>(reg);
If you wouldn't be using Simple Injector, but purely .NET Core's built-in DI Container, the registration would look as follows:
services.AddDbContext<EntityDbContext>(options => options.UseInMemoryDatabase("Snoogans"));
services.AddScoped<IUnitOfWork>(c => c.GetRequiredService<EntityDbContext>());
services.AddScoped<IReadEntities>(c => c.GetRequiredService<EntityDbContext>());
services.AddScoped<IWriteEntities>(c => c.GetRequiredService<EntityDbContext>());
I've never worked with a .Net Core project before but have a history with .Net including MVC and entity framework. I'm working with a new .Net Core project which has five solution folders, EHA.PROJ.API, EHA.PROJ.DTO,EHA.PROJ.Repository, EHA.PROJ.Repository.Test and EHA.PROJ.Web. The EHA.PROJ.DTO folder has a number of files such as CategoryDTO.cs which looks like this
namespace EHA.PROJ.DTO
{
public class CategoryDescDTO
{
public int CategoryRef { get; set; }
public string CategoryName { get; set; }
}
}
I'm looking to set up a mapping arrangement to get the data from the EHA.PROJ.DTO files to the model files in my models folder in my EHA.PROJ.Web folder. I've been browsing as I've never done anything like this before as I've previously worked with data from a DAL folder using entity framework and connection done through connection strings. I'm guessing that there must be some process to map the data in my dbContext to connect the files in both folders. I did find some information on AutoMapper but was unsure how to implement it.
This arrangement with .Net Core is new to me so if anyone can help with any examples or point me in the right direction I would be grateful.
Your first problem is having your entities in your web project. Right off the bat, you have tight-coupling between the web project and your data layer, which then pretty much negates the point of all your other layers: DTO, repository, etc. You want to move out your entities and context into a true data layer (i.e. a class library project separate from your web project).
Then, you want to decide how far your data layer should extend. If the API is to feed the Website, then you want to actually remove all dependencies on the data layer from the web project. Your DTO project would be shared between the API and Web projects and your API would send/receive your DTOs, mapping back and forth from your entities under the hood.
However, if you're going to do that, then the repository project should just go away entirely. Just have your API work directly with EF and your entities. Your abstraction is the API itself; there is no need for another. The only reason to have the repository layer is if both the API and Web will both directly utilize the repositories, which isn't a very good pattern actually. You'll inevitably end up with a bunch of duplicated logic specific to each project.
Simply, the repository pattern is superfluous when using an ORM like EF. The ORM is your data layer. You're simply using a DAL provided by a third-party, rather than one you created yourself. The repository pattern only makes sense when working directly with SQL using something like ADO.NET directly. Otherwise, get rid of it.
Having an API is enough of an abstraction, if your goal is simply to hide the data layer. The website knows nothing of the underlying data source, and an API is really just a service layer that returns JSON over HTTP rather than object instances directly, i.e. the API is essentially your "repository" layer.
The situation can be improved even further by moving to a microservices-based architecture. With that, you essentially have multiple small, self-contained APIs that work with just one part of your domain or piece of functionality. Each can utilize EF directly, or an entirely different ORM, or even an entirely different stack. You could have APIs build on Node.js or python, etc. The website simply makes requests to the various services to get the data it needs and doesn't know or care how those services actually work.
I have been using Automapper for quite some time in .NET Core projects due to ease of use and built-in dependency injection.
Install from PM:
Install-Package AutoMapper
Install-Package AutoMapper.Extensions.Microsoft.DependencyInjection
Register in the Startup.cs, ConfigureServices method:
services.AddAutoMapper(typeof(Startup));
Create a class to keep your mappings, e.g. MappingProfile.cs using Profile from automapper, you can define mappings.
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<Operator, OperatorDto>().ReverseMap();
}
}
}
The above mapping tells automapper that Operator can be mapped to OperatorDto and OperatorDto can be mapped to Operator.
In your controller, you can inject an IMapper
private readonly IMapper _mapper;
public OperatorsController(IMapper mapper)
{
_mapper = mapper;
}
and map values like below:
var dto = _mapper.Map<OperatorDto>(op); // Map op object to dto
var op = _mapper.Map<Operator>(dto); // Map dto to op object
Automapper offers custom mappings, should you need it.
While it is very easy to perform mappings with Automapper, you need to learn the framework.
I believe it is worth the effort to learn it as it will save you a lot of time writing mapping code in the future.
This article is a good reference to start: https://buildplease.com/pages/repositories-dto/
My suggestion is to have a DTO assembler that maps your model to the DTO object. So, you start with your DTO class:
namespace EHA.PROJ.DTO
{
public class CategoryDescDTO
{
public int CategoryRef { get; set; }
public string CategoryName { get; set; }
}
}
Then build the assembler:
public class CategoryDescAssembler {
public CategoryDescDTO WriteDto(CategoryDesc categoryDesc) {
var categoryDescDto = new CategoryDescDTO();
categoryDescDto.CategoryRef = categoryDesc.CategoryRef;
categoryDescDto.CategoryName = categoryDesc.CategoryName;
return categoryDescDto;
}
}
Now you implement the service to do all the work required to get the DTO object:
public class CategoryDescService : ICategoryDescService {
private readonly IRepository<CategoryDesc> _categoryDescRepository;
private readonly CategoryDescAssembler _categoryDescAssembler;
public CategoryDescService(IRepository<CategoryDesc> categoryDescRepository, CategoryDescAssembler categoryDescAssembler) {
_categoryDescRepository= categoryDescRepository;
_categoryDescAssembler= categoryDescAssembler;
}
public CategoryDescDTO GetCategoryDesc(int categoryRef) {
var categDesc = _categoryDescRepository.Get(x => x.CategoryRef == categoryRef);
return _categoryDescAssembler.WriteDto(categDesc);
}
}
With the interface looking like this:
public interface ICategoryDescService
{
CategoryDescDTO GetCategoryDesc(int categoryRef);
}
You would then need to add the service to your Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddTransient<ICategoryDescService, CategoryDescService>();
}
Now you can call your service from you view controller.
I have an N-Layer application as shown below
MyApp.Model - contains edmx and data models
MyApp.DataAccess - Repositories with EF
MyApp.Domain - Domain/business models
MyApp.Services - services(class library)
MyApp.Api - ASP.NET Web API
I am using Unity as my IoC container and Automapper for OO mapping.
My DataAccess layer references Model layer which contains all my Data objects.
I do not want to refer my model project in my Api layer. So returning DomainObjects (business models) from service layer and mapping to DTOs in API layer(DTOs are in API layer).
I configured domainModel to DTO mapping in API layer as below
public static class MapperConfig
{
public static void Configure() {
Mapper.Initialize(
config => {
config.CreateMap<StateModel, StateDto>();
config.CreateMap<StateDto, StateModel>();
//can't do this as I do not have reference for State which is in MyApp.Model
//config.CreateMap<State, StateModel>();
//config.CreateMap<StateModel, State>();
});
}
}
Now my question is how/where to configure my auto mapper mappings to convert my Entity models to Domain models?
To do in my API layer I do not have reference to my model project. I believe I should do this in service layer but not sure how to do that. Please help how to configure this mapping.
Note: Before asking here I googled with all eyes
Where to place AutoMapper map registration in referenced dll says to use static constructor which I do not think a good option to add in all my models (I have 100 models) and another answer says to use PreApplicationStartMethod for which I have to add reference to System.web.dll to my services which is not correct.
https://groups.google.com/forum/#!topic/automapper-users/TNgj9VHGjwg also did not answer my question properly.
You need to create mapping profiles in each of your layer projects, then tell AutoMapper to use those profiles in the topmost/outermost (invoking) layer that references all the lower layers. In your example:
MyApp.Model
public class ModelMappingProfile : AutoMapper.Profile
{
public ModelMappingProfile()
{
CreateMap<StateModel, StateDto>();
CreateMap<StateDto, StateModel>();
}
}
MyApp.Api
public class ApiMappingProfile : AutoMapper.Profile
{
public ApiMappingProfile()
{
CreateMap<State, StateModel>();
CreateMap<StateModel, State>();
}
}
MyApp.Services
Mapper.Initialize(cfg =>
{
cfg.AddProfile<MyApp.Model.ModelMappingProfile>();
cfg.AddProfile<MyApp.Model.ApiMappingProfile>();
});
or if you are using a DI container (e.g. SimpleInjector):
container.RegisterSingleton<IMapper>(() => new Mapper(new MapperConfiguration(cfg =>
{
cfg.AddProfile<MyApp.Model.ModelMappingProfile>();
cfg.AddProfile<MyApp.Model.ApiMappingProfile>();
})));
I'm using ASP.net Core with AutoMapper. To get the DI running, I use the AutoMapper.Extensions.Microsoft.DependencyInjection Nuget-Package and let AutoMapper register the profiles via
private static void InitializeAutoMapper(IServiceCollection services)
{
services.AddAutoMapper();
}
This works fine, but for some Profiles, I'd like to inject also some dependencies to them, for example:
public class IndividualDtoProfile : Profile
{
private readonly IIndividualFactory _individualFactory;
private readonly IMapper _mapper;
public IndividualDtoProfile(IIndividualFactory individualFactory, IMapper mapper)
{
_individualFactory = individualFactory;
_mapper = mapper;
}
public IndividualDtoProfile()
{
CreateMap<Individual, IndividualDto>();
CreateMap<IndividualDto, Individual>()
.ConstructUsing(
dto =>
{
var gender = _mapper.Map<IndividualGender>(dto.Gender);
return _individualFactory.CreateIndividual(dto.FirstName, dto.LastName, gender, dto.BirthDate);
});
}
}
The only relevant discussion I found is here : https://groups.google.com/forum/#!topic/automapper-users/5XK7pqGu_Tg
Which pretty much seems to suggest to not use the goodness of the existing possibilities, but manually map the Profiles. The only other possibility I'd see is to provide a static ServiceProvider-Singleton, which also doesn't seem too appealing.
Is there already a possibility, to use ASP.Net Core with AutoMapper and let dependencies inject into Profiles?
Edit: Due to the comment, probably I'm also something fundamentally wrong: I'm learning Domain Driven Design, where I have an application layer. I'd like to map the DTOs, which are used from a Web-Service, back to the domain entities, and I assumed, it would make sense to use the Factories there too, since otherwise I would bypass the logic in the Factories.
That's not supported out of the box, by design. If you want it, you have to do it yourself using your DI container. This has been discussed many times before. For example, here. The docs.