I develop web api application using Entity Framework 6.
I'd like to integrate and start using Automapper to map to and from my EF entites models.
I've read about projections and realized it's necessary to use the Project().To<> for better performance if I decide using Automapper. However, I don't want to expose my DAL to the Automapper library.
Is there a way I can abstract away the Automapper Project()?
Thanks in advance
Create an interface with a "projection" method,you can copy the
original AutoMapper method.
Then create a concrete implementation.
After that add this interface to your repository's constructor.
Use it
Register the interface to your dependency injection container.
Press F5
Here is an complete example
(Of course you will have each class/interface in the correct layer).
using AutoMapper;
using AutoMapper.QueryableExtensions;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
namespace ConsoleApplicationMapper
{
class Program
{
static void Main(string[] args)
{
Mapper.Initialize(c =>
{
c.CreateMap<Customer, CustomerModel>();
});
//If you use a dependency injection container you don't have to use the constructors
var repository = new CustomerRepository(new EntityProjector());
foreach (var item in repository.GetCustomers())
{
Console.WriteLine(item.Name);
}
Console.ReadLine();
}
}
public class CustomerModel
{
public int CustomerId { get; set; }
public string Name { get; set; }
}
public interface IEntityProjector
{
IQueryable<TDestination> ProjectTo<TDestination>(IQueryable source, params Expression<Func<TDestination, object>>[] membersToExpand);
}
public class EntityProjector : IEntityProjector
{
public IQueryable<TDestination> ProjectTo<TDestination>(IQueryable source, params Expression<Func<TDestination, object>>[] membersToExpand)
{
return source.ProjectTo(membersToExpand);
}
}
public interface ICustomerRepository
{
IEnumerable<CustomerModel> GetCustomers();
}
public class CustomerRepository : ICustomerRepository
{
private readonly IEntityProjector projector;
public CustomerRepository(IEntityProjector theProjector)
{
projector = theProjector;
}
public IEnumerable<CustomerModel> GetCustomers()
{
MyContext context = new MyContext();
//Uncomment this if you want to confirm that only CustomerId,Name are selected and not LastName
//context.Database.Log = s => Console.WriteLine(s);
return context.Customers.SelectTo<CustomerModel>();
}
}
public class Customer
{
public int CustomerId { get; set; }
public string Name { get; set; }
public string LastName { get; set; }
}
public class MyContext : DbContext
{
public DbSet<Customer> Customers { get; set; }
}
public static class MyExtentions
{
static IEntityProjector projector;
static MyExtentions()
{
//You can get it from your dependecy injection container if you use one
projector = new EntityProjector();
}
//I renamed this SelectTo instead of ProjectTo so you don't have any conflict if you use AutoMapper
//Change it to to ProjectTo if you want
public static IQueryable<TDestination> SelectTo<TDestination>(this IQueryable source, params Expression<Func<TDestination, object>>[] membersToExpand)
{
return projector.ProjectTo<TDestination>(source);
}
}
}
Related
I am creating a small console application using .net core 2.2 and i am trying to implement dependency injection with my app.
I am getting some unhandled exceptions.
Person.cs
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public int? Age { get; set; }
public string Gender { get; set; }
}
IPersonRepository
public interface IPersonRepository
{
bool AddPerson(Person entity);
IEnumerable<Person> GetAllPersons();
}
PersonRepository.cs
public class PersonRepository:IPersonRepository
{
private readonly IPersonRepository _personRepository;
public PersonRepository(IPersonRepository personRepository)
{
_personRepository = personRepository;
}
public bool AddPerson(Person entity)
{
_personRepository.AddPerson(entity);
return true;
}
public IEnumerable<Person> GetAllPersons()
{
throw new System.NotImplementedException();
}
}
Program.cs
using Microsoft.Extensions.DependencyInjection;
namespace ConsoleAppWithDI
{
internal static class Program
{
private static void Main(string[] args)
{
var serviceProvider = new ServiceCollection()
.AddTransient<IPersonRepository, PersonRepository>()
.BuildServiceProvider();
var personRepositoryObj = serviceProvider
.GetService<IPersonRepository>();
personRepositoryObj
.AddPerson(new Person
{
Id = 1,
Name = "Tom",
Age = 24,
Gender = "Male"
});
}
}
}
I am getting this Exception. Can anybody tell me where i am making mistake ? Also i would like to know when making .exe in console app (which does not runs 24*7) using DI is safe ?
Any help would be much appreciated. Thanks
Your person Repository takes in a IPersonRepository, The Dependency Injector is trying to create a class in which it needs to inject itself. You probably want to take in a DbContext instead. This code assumes you've created a DbContext Named ApplicationContext
private readonly ApplicationContext _context;
public PersonRepository(ApplicationContext context)
{
_context = context;
}
public bool AddPerson(Person entity)
{
_context.Persons.Add(entity);
_context.SaveChanges();
return true;
}
public PersonRepository(IPersonRepository personRepository)
{
_personRepository = personRepository;
}
This is your problem. You need to remove the IPersonRepository parameter from the constructor because its trying to create an instance of itself within itself. Hence, your circular reference issue
Iam planning to use Contrib with dapper to make my classes in desktop applications look like this
public abstract class DB : IDisposable
{
public virtual long Insert()
{
using (var db = ConFactory.GetConnection())
{
db.Open();
return db.Insert(this);
}
}
// ... and other CRUD operations
}
then any Concept class will inherits from DB class; like this
[Table("test")]
public class Test : DB
{
[Key]
public int TestId { get; set; }
public string Name { get; set; }
}
using Test class
using (Test t = new Test { Name = textBox2.Text })
{
textBox1.Text = t.Insert().ToString();
}
this sample always fails and gives me
SQL logic error near ")": syntax error
BUT when I implement Insert() method inside child class it works well !!!
the problem is: my code contains lot of classes, rewrite All CUD operations is so disturbing,
any idea to solve this with less code?
I figured out a better way to solve this... which is more flexible and more pro i think.
I'll put the solution here ... soon on github
the solution based on Extension methods, to inject CUD operation with only concept classes I proposed IDbTable base class
public abstract class IDbTable : IDisposable
{
public void Dispose()
{
// do disposing if you need
}
}
public static class DB
{
public static long Insert<T>(this T entity) where T : IDbTable
{
using (var db = ConFactory.GetConnection())
{
db.Open();
return db.Insert(entity);
}
}
public static void Delete<T>(this T entity) where T : IDbTable
{
using (var db = ConFactory.GetConnection())
{
db.Open();
db.Delete(entity);
}
}
public static void Update<T>(this T entity) where T : IDbTable
{
using (var db = ConFactory.GetConnection())
{
db.Open();
SqlMapperExtensions.Update(db, entity);
}
}
public static T Get<T>(int id)
{
using (var db = ConFactory.GetConnection())
{
db.Open();
return db.Get<T>(id);
}
}
}
ok this is all
when any class inherits from IDbTable base class, it have insert, update and delete method as extenssion
[Table("test")]
public class Test : IDbTable
{
[Key]
public int TestId { get; set; }
public string Name { get; set; }
}
.......
Test t = new Test { Name = textBox2.Text };
textBox1.Text = t.Insert().ToString();
AND it WORKS fine !!!
any suggestion to improve will be appreciated.
I'm trying to use AutoMapper v6.1.1 to map a class using projection, but AutoMapper doesn't include deeply nested objects.
I've attached a complete Visual Studio 2015 solution with a unit test here:
https://www.dropbox.com/s/omue5ou5dvxsa57/UnitTestProject2.zip?dl=0
I'm basically trying to map a Child and Parent hierarchy into a Person hierarchy, but the grand-Parents aren't getting included in the projection result.
Models:
public class Child
{
public string Name { get; set; }
public virtual Parent Parent { get; set; }
}
public class Parent
{
public string Name { get; set; }
public virtual Parent GrandParent { get; set; }
}
public class Person
{
public string Name { get; set; }
public virtual Person Parent { get; set; }
}
Mapping profile:
public class PersonProfile : Profile
{
public PersonProfile()
{
this.CreateMap<Child, Person>()
.MaxDepth(5);
this.CreateMap<Parent, Person>()
.ForMember(destinationMember => destinationMember.Parent, memberOptions => memberOptions.MapFrom(sourceMember => sourceMember.GrandParent))
.MaxDepth(5);
}
}
Unit test:
[TestClass]
public class UnitTest1
{
IMapper mapper;
List<Child> children;
[TestInitialize]
public void TestInitialize()
{
MapperConfiguration configuration = new MapperConfiguration((config =>
{
config.AddProfile(new PersonProfile());
config.ForAllMaps((mapType, mapperExpression) =>
{
mapperExpression.MaxDepth(5);
});
}));
this.mapper = configuration.CreateMapper();
mapper.ConfigurationProvider.AssertConfigurationIsValid();
this.children = new List<Child>
{
new Child
{
Name = "Child1",
Parent = new Parent
{
Name = "Parent1",
GrandParent = new Parent
{
Name = "GrandParent1",
GrandParent = new Parent
{
Name = "GreatGrandParent1"
}
}
}
}
};
}
[TestMethod]
public void TestProjection()
{
IQueryable<Person> people = children.AsQueryable().ProjectTo<Person>(mapper.ConfigurationProvider);
AssertPeople(people);
}
[TestMethod]
public void TestMap()
{
List<Person> people = mapper.Map<List<Child>, List<Person>>(children);
AssertPeople(people.AsQueryable());
}
private void AssertPeople(IQueryable<Person> people)
{
Assert.IsNotNull(people);
Assert.AreEqual(1, people.Count());
Person child1 = people.ElementAt(0);
Assert.AreEqual("Child1", child1.Name);
Person parent1 = child1.Parent;
Assert.IsNotNull(parent1);
Assert.AreEqual("Parent1", parent1.Name);
Person grandParent1 = parent1.Parent;
Assert.IsNotNull(grandParent1); // fails when using ProjectTo
Assert.AreEqual("GrandParent1", grandParent1.Name);
}
}
Using the Map method works but ProjectTo doesn't.
The classes in the sample code are much simpler than those used in production.
I'm trying to use projection so that I can return an IQueryable<Person> from OData and take advantage of the SQL generated by LINQ to Entities with query options automatically applied.
Any help is appreciated.
Thank you!
I think this describes the issue:
https://github.com/AutoMapper/AutoMapper/issues/2171
But as a workaround is it not possible to create an extension method that basically calls the Map internally:
public static class Extenstions
{
public static IQueryable<TDestination> ProjectToExt<TDestination, TSource>(this IQueryable<TSource> #this,
IMapper mapper)
{
return mapper.Map<IEnumerable<TDestination>>(#this).AsQueryable();
}
}
Then the calling code is like:
IQueryable<Person> people = children.AsQueryable().ProjectToExt<Person, Child>(mapper);
What is the best practice, following DI, to create two separate repository classes...e.g.
public class FirstDbRepo : Repository
public class SecondDbRepo : Repository
That essentially implement the Repository class shown below
namespace MyApp.Persistence
{
public class Repository<T> : IRepository<T> where T : EntityBase
{
public IConfig Config { get; set; }
private Database Database
{
get
{
// Use Config to get connection
};
set;
}
public Repository(IConfig config)
{
Config = config;
}
public IEnumerable<T> Get(Expression<Func<T, bool>> predicate)
{
// Use database to get items
}
public T CreateItem(T item)
{
// Use database to create item
}
}
}
But to inject different config values/instances...
public interface IConfig
{
string DatabaseName{ get; }
string DatabaseEndpoint{ get; }
string DatabaseAuthKey{ get; }
}
The first thing I thought of was to create marker interfaces, but wanted to know if this smells...is there a more correct way to do this using DI?
public interface IFirstDbRepo { }
public class FirstDbRepo<T> : Repository<T> where T: EntityBase
{
public FirstDbRepo(FirstConfig config)
: base(config)
{ }
}
public class FirstConfig : IConfig
{
public string DatabaseName{ get { return "MyName" }; } // From web.config
}
And then use a ninject binding for each repo...the consumer could use as follows
public class Consumer() {
private readonly IFirstDbRepo _firstRepo;
public Consumer(IFirstDbRepo firstRepo) {
_firstRepo = firstRepo;
}
}
Bind<IConfig>().To<MyConfigOne>().WhenInjectedInto(typeof(FirstDbRepo));
Bind<IConfig>().To<MyConfigTwo>().WhenInjectedInto(typeof(SecondDbRepo ));
Contextual binding
I'm using EntityFramework as a DataLayer and DTO to transfer data between layer. I develop Windows Forms in N-Tier architecture and when I try to mapping from Entity to DTO in BLL:
public IEnumerable<CategoryDTO> GetCategoriesPaged(int skip, int take, string name)
{
var categories = unitOfWork.CategoryRepository.GetCategoriesPaged(skip, take, name);
var categoriesDTO = Mapper.Map<IEnumerable<Category>, List<CategoryDTO>>(categories);
return categoriesDTO;
}
I've got this error:
http://s810.photobucket.com/user/sky3913/media/AutoMapperError.png.html
The error said that I missing type map configuration or unsupported mapping. I have registered mapping using profile in this way at UI Layer:
[STAThread]
static void Main()
{
AutoMapperBusinessConfiguration.Configure();
AutoMapperWindowsConfiguration.Configure();
...
Application.Run(new frmMain());
}
and AutoMapper configuration is in BLL:
public class AutoMapperBusinessConfiguration
{
public static void Configure()
{
Mapper.Initialize(cfg =>
{
cfg.AddProfile<EntityToDTOProfile>();
cfg.AddProfile<DTOToEntityProfile>();
});
}
}
public class EntityToDTOProfile : Profile
{
public override string ProfileName
{
get { return "EntityToDTOMappings"; }
}
protected override void Configure()
{
Mapper.CreateMap<Category, CategoryDTO>();
}
}
public class DTOToEntityProfile : Profile
{
public override string ProfileName
{
get { return "DTOToEntityMappings"; }
}
protected override void Configure()
{
Mapper.CreateMap<CategoryDTO, Category>();
}
}
I've got the same error too when mapping from DTO to Entity.
category = Mapper.Map<Category>(categoryDTO);
How to solve this?
Its because you are using Mapper.Initialize multiple times. If you look at the source code it calls Mapper.Reset() which means only the last mapping defined will work. so instead simply remove the Initialize calls and replace with Mapper.AddProfile< >
Use AutoMapper.AssertConfigurationIsValid() after the Configure() calls. If anything fails it will throw an exception with a descriptive text. It should give you more info to debug further.
Mapping DTOs to Entities using AutoMapper and EntityFramework
here we have an Entity class Country and an CountryDTO
public class Country
{
public int CountryID { get; set; }
public string ContryName { get; set; }
public string CountryCode { get; set; }
}
CountryDto
public class CountryDTO
{
public int CountryID { get; set; }
public string ContryName { get; set; }
public string CountryCode { get; set; }
}
Create Object of CountryDTO
CountryDTO collection=new CountryDTO();
collection.CountryID =1;
collection.ContryName ="India";
collection.CountryCode ="in";
Country model = Convertor.Convert<Country, CountryDTO>(collection);
dbcontext.Countries.Add(model);
dbcontext.SaveChanges();
this will work fine for a new Country, the above code will map CountryDTO to Country Entity Object and add new entities to the dbcontext and save the changes.
using System.Reflection;
public static TOut Convert<TOut, TIn>(TIn fromRecord) where TOut : new()
{
var toRecord = new TOut();
PropertyInfo[] fromFields = null;
PropertyInfo[] toFields = null;
fromFields = typeof(TIn).GetProperties();
toFields = typeof(TOut).GetProperties();
foreach (var fromField in fromFields)
{
foreach (var toField in toFields)
{
if (fromField.Name == toField.Name)
{
toField.SetValue(toRecord, fromField.GetValue(fromRecord, null), null);
break;
}
}
}
return toRecord;
}
public static List<TOut> Convert<TOut, TIn>(List<TIn> fromRecordList) where TOut : new()
{
return fromRecordList.Count == 0 ? null : fromRecordList.Select(Convert<TOut, TIn>).ToList();
}
http://bhupendrasinghsaini.blogspot.in/2014/09/convert-enity-framwork-data-in-entity.html