2

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

  • I would highly, highly suggest not having these sorts of layer rules. Indirection just introduces complexity with not much benefit. – Jimmy Bogard Mar 07 '16 at 01:02

1 Answers1

1
  1. Create an interface with a "projection" method,you can copy the original AutoMapper method.
  2. Then create a concrete implementation.
  3. After that add this interface to your repository's constructor.
  4. Use it
  5. Register the interface to your dependency injection container.
  6. 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);
        }
    }
}
George Vovos
  • 7,563
  • 2
  • 22
  • 45
  • Are you willing to fully complete your example, including the possible extension method you mentioned in the end? I dont think I know how to implement it myself. Regardless, thank you very much –  Mar 05 '16 at 21:10
  • @S.Peter Updated.Let me know if you have any problems – George Vovos Mar 05 '16 at 21:31
  • Of course ,now you can remove the Constructor of you repository and the private field.I just left it there in case you don't want to use the extension method – George Vovos Mar 05 '16 at 21:37
  • The extensions class must be static right? Can I inject into static class? Never tried that before –  Mar 05 '16 at 21:41
  • Unfortunately you need a static class.It is tricky.see related questions http://stackoverflow.com/questions/14894980/refactoring-a-static-class-to-use-with-dependency-injection and http://stackoverflow.com/questions/1293489/dependency-injection-with-a-static-logger-static-helper-class – George Vovos Mar 05 '16 at 21:44
  • Maybe interesting to know that AutoMapper will soon have a non-static API. – Gert Arnold Mar 05 '16 at 21:53
  • Can't I take the EntityPtojector as a parameter to the extension method instead of injecting in a ctor? –  Mar 05 '16 at 21:58
  • if you want yes,you can do that – George Vovos Mar 05 '16 at 22:00
  • @GertArnold Thanks for the info – George Vovos Mar 05 '16 at 22:01
  • Shouldn't the soucre in EntityProjector ProjectTo method be of type Iqueryable? –  Mar 06 '16 at 08:05
  • @S.Peter No,the source can be anything(Iqueryable),the return type must be Iqueryable – George Vovos Mar 06 '16 at 10:31
  • I don't have the ProjectTo from the source –  Mar 06 '16 at 12:37
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/105479/discussion-between-george-vovos-and-s-peter). – George Vovos Mar 06 '16 at 12:39
  • Oh I guess you meant SelectTo..sorry –  Mar 06 '16 at 12:44