3

I have a very specific use-case. I have entities generated with EF6.1.3

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated from a template.
//
//     Manual changes to this file may cause unexpected behavior in your application.
//     Manual changes to this file will be overwritten if the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

namespace MyProject.Data
{
    using System;
    using System.Collections.Generic;

    public partial class Market
    {
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
        public Market()
        {
            this.Company = new HashSet<Company>();
            this.Partner = new HashSet<Partner>();
            this.Activity = new HashSet<Activity>();
            this.Product = new HashSet<Product>();
        }

        public int market_id { get; set; }
        public string market_name { get; set; }
        public int display_color { get; set; }
        public bool is_modifiable { get; set; }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
        public virtual ICollection<Company> Company { get; set; }
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
        public virtual ICollection<Partner> Partner { get; set; }
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
        public virtual ICollection<Activity> Activity { get; set; }
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
        public virtual ICollection<Product> Product { get; set; }
    }
}

I also have a repository class that reads the generated entity types. In the next layer (Service) I use the repository and AutoMapper to map the entities to the desired types. For example I have a read method inside the MarketServices class

public ModelType Read<ModelType>(Expression<Func<Market, bool>> predicate)
{
    var entity = Read(predicate);
    var market = entityMapper.Map<ModelType>(entity);
    return market;
}

With the appropriate mapping configurations this works flawlessly. Now, in another service I need to use the MarketServices (many-to-many relationship) because I want to add a new Company to a subset of already existing Markets. Things got furious from here, and there are no hidden details, I'm going to show everything: CompanyServices.Create:

public void CreateAndSave(CreateCompanyViewModel viewModel)
{
    var entity = entityMapper.Map<Company>(viewModel);
    if (viewModel.SelectedOperations != null)
    {
        foreach (var marketID in viewModel.SelectedOperations)
        {
            var marketModel = marketsProvider.Read<Market>(m => m.market_id == marketID);
            entity.Market.Add(marketModel);
        }
    }

    Create(entity);
}

The error pops at the marketsProvider.Read line. Yes, that would be the source and target types exactly the same. Everything else works, if I choose to use different mappings that I've defined it works. I defined this mapping like so:

config.CreateMap<Market, Market>();

So the repository returns the appropriate Market entities, but when I try to just "passthrough" them with AutoMapper I get the strangest (I'll be damned if I understand why you try to do that) exception I've ever seen:

Mapping types: Market_C126EFB65DDE81A0F02B0BF191A2C8B857DA7226E54AB43FB5DF74D3D3A406ED -> ICollection1 System.Data.Entity.DynamicProxies.Market_C126EFB65DDE81A0F02B0BF191A2C8B857DA7226E54AB43FB5DF74D3D3A406ED -> System.Collections.Generic.ICollection1[[MyProject.Data.Activity, MyProject, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]

Destination path: Market.Activity.Activity

Source value: System.Data.Entity.DynamicProxies.Market_C126EFB65DDE81A0F02B0BF191A2C8B857DA7226E54AB43FB5DF74D3D3A406ED

So what exactly is happening here? Why won't Automapper return me the original object if both the source and target type of the mapping is the same? Is there any way to achieve this behavior?

Thanks!

I ended up using a partial solution found Can I get AutoMapper to return the same object sometimes? and I changed my Read function like so:

public ModelType Read<ModelType>(Expression<Func<Market, bool>> predicate) where ModelType: class
{
    var entity = Read(predicate);
    if (entity is ModelType)
    {
        return entity as ModelType;
    }

    return entityMapper.Map<ModelType>(entity);
}

I'm just gonna leave this here in case anyone else stumbles upon the same use case.

Community
  • 1
  • 1
Joseph
  • 85
  • 6
  • Try changing the line to `.Read>` if possible. What marketsProvider class is about? I also strongly recommend not to use automapper for mapping viewmodel -> model and model -> model. This is not what AutoMapper is for. – Red Apr 13 '16 at 06:56
  • Do you have a mapping configured for `Activity`? – Brad Apr 13 '16 at 07:02
  • the Read method returns a single or default entry from market. marketsProvider is a service class to handle Market entities. I'd be happy if I wouldn't have to use AutoMapper for reverse mappings but then I happen to encounter one tiny problem. EF tries to insert in both Companies and Markets table even though I set entities for Markets that're already existing. Namely: 1) Query the entities 2) Add them to the new Company 3) Attach the new Company 4) Have a nice Unique constraint violation in Markets table as it tries to insert the already existing records. – Joseph Apr 13 '16 at 07:04
  • @Brad: Actually, No. At this point I don't even need it, these are not even fetched from the DB for the Markets. I'm trying to insert a Company and link to existing markets. Markets have a primary key (int) and a unique constraint (market_name). I only fetch (id and name) from the DB. The purpose for that mapping would be: In this very specific use-case I'm using the Markets Service to return the entity type instead of a mapped ViewModel which I usually am doing from the controllers. For this I really don't want to create a new Read function that does essentially the same without the mapping. – Joseph Apr 13 '16 at 07:19
  • @Joseph It's clearly saying that it's trying to map a collection of proxies for `Market` entity to collection of `Activity` entities somewhere in the code, when it's mapping `Market.Activity.Activity`. This might happen implicitely, so check your mappings. – Red Apr 13 '16 at 07:20
  • @Raderick: Yeah, that's great; I get that. I just wish it wouldn't do that. Apparently there's no way to return the original object without mapping and create a general function that when the targettype is the source type just return the original and when the target type is a view model perform the mapping. If I get it right. Because I absolutely have no intention of querying the other linked entities for the market if I have no use of them. You were 100% right, it's a horrible idea to map backwards. :) – Joseph Apr 13 '16 at 07:32

0 Answers0