0

I'm using Automapper 5.2.0 with EF 6.1.3 in web api project.

public class PushNotification
{
    public int[] Stores { get; set; }
}

public class PushNotificationEntity
{
    [Key]
    public int Id { get; set; }
    public ICollection<StoreEntity> Stores { get; set; }
}

public class StoreEntity
{
    [Key]
    public int Id { get; set; }

    public virtual ICollection<PushNotificationEntity> PushNotifications { get; set; }
}

this is mapping,

cfg.CreateMap<PushNotificationEntity, PushNotification>()
                .ForMember(dest => dest.Stores,
                            opts => opts.MapFrom(src => src.Stores.Select(s => s.Id).ToArray()));

this is how I access it,

var item = ctx.PushNotifications.Include(p => p.Stores)
            .Where(n => n.Id == id).ProjectTo<PushNotification>(mapperCfg).SingleOrDefault();

I'm getting following error,

System.NotSupportedException.
LINQ to Entities does not recognize the method 'Int32[]
ToArray[Int32](System.Collections.Generic.IEnumerable`1[System.Int32])'
method, and this method cannot be translated into a store expression.
SajithK
  • 1,014
  • 12
  • 23
  • 8
    Did you read the message? The problem isn't automapper, the problem is with LINQ to Entities, you can't run the `.ToArray()` method *on the server*, you will have to do some of this in client code, on the final results. – Lasse V. Karlsen Jan 03 '17 at 09:21
  • Automapper *maps*. Your code tries to force it to execute a database query instead by lazily loading related entries from the database (Stores). As a result, the mapping cast ends up in the EF query. – Panagiotis Kanavos Jan 03 '17 at 09:33
  • Even if this succeeded it's a very bad idea. You end up with arbitrary database queries *and* a chance for a runtime exception if the mapping occurs after you close your EF context. At the very least you should use `.Include()` to eagerly load the stores. *Far* better is to use a LINQ query that returns the data you actually want, *not* the entire object. What's the point of loading the entire `Store` if you only care about its Id? – Panagiotis Kanavos Jan 03 '17 at 09:34
  • @PanagiotisKanavos, I do use .Include() to eagerly load the stores. I don't know a way to do as you suggested, to return only the ids of stores instead of stores. Can you show me an example? – SajithK Jan 03 '17 at 09:52
  • @LasseV.Karlsen, Automapper runs on the client side. If I run following without Automapper it runs OK, var item = ctx.PushNotifications.Include(p => p.Stores).Where(n => n.Id == id).Single(); var ss = item.Stores.Select(s => s.Id).ToArray(); I don't think this is a Linq to Entity problem – SajithK Jan 03 '17 at 09:56
  • Use it where? Where is the code that loads these? As for the query, In the code that actually loads the entities create a new PushNotification in your Select instead of loading the entity, eg: `new PushNotification {Id=entity.Id,Stores=entity.Stores(s => s.Id).ToArray()}`. Why map when your ORM can already map to the entity you want? – Panagiotis Kanavos Jan 03 '17 at 09:57
  • @sajith the error messages is a LINQ to Entity error. Post your code in the *question* though, not the comments. – Panagiotis Kanavos Jan 03 '17 at 09:58
  • I updated the code. Yes it is Linq to Entity error. But I don't see anything wrong here. That's why I asked the question. Pls help me if you can, don't suggest me not to use Automapper. – SajithK Jan 03 '17 at 10:06
  • No one is suggesting you to not to - but instead to use it properly. – Amit Kumar Ghosh Jan 03 '17 at 10:08
  • @AmitKumarGhosh, "Panagiotis Kanavos" suggests in his comment, "Why map when your ORM can already map to the entity you want?" – SajithK Jan 03 '17 at 10:10
  • What is the actual type of object inside the `.Stores` property? There is an `ICollection` there, but what is the underlying object? Likely this type implements `.ToArray()` by going through LINQ to Entities. – Lasse V. Karlsen Jan 03 '17 at 10:14
  • @LasseV.Karlsen, I updated question with StoreEntity type declaration. – SajithK Jan 03 '17 at 10:19
  • Yes, but that doesn't help, if you don't have the actual code because this is generated by LINQ to Entities, please use `.GetType().FullName` to get the name of the underlying type on that collection, at runtime. – Lasse V. Karlsen Jan 03 '17 at 10:20
  • I found this question it suggests the same, http://stackoverflow.com/questions/7665873/using-automapper-to-map-a-property-of-a-collection-to-an-array-of-primitives – SajithK Jan 03 '17 at 10:21
  • 1
    The issue is indeed caused by EF6 projection limitation. Simply `ToArray` is not supported as indicated in the exception message. The easier would be to change `public int[] Stores { get; set; }` to let say `public IReadOnlyList Stores { get; set; }` and use `ToList` which **is** supported. – Ivan Stoev Jan 03 '17 at 10:23
  • @lvans your suggestion solves the problem. But I don't think it is an EF limitation. If use `.ToArray()` outside of Automapper It works fine, `var item = ctx.PushNotifications.Include(p => p.Stores).Where(n => n.Id == id).Single(); var ss = item.Stores.Select(s => s.Id).ToArray();` – SajithK Jan 03 '17 at 11:40
  • 1
    @sajith Negative - the sample you are providing is not equivalent of the `ProjectTo` query. Try `var item = ctx.PushNotfications.Where(n => n.Id == id).Select(n => new PushNotification { Stores = n.Stores.Select(s => s.Id).ToArray() }).Single();` and you'll get the exact same exception. – Ivan Stoev Jan 03 '17 at 13:10
  • @IvanStoev I realized it right now. Thanks everyone for your help. – SajithK Jan 03 '17 at 17:16

0 Answers0