8

I have a solution with several projects. A business components project, an MVC web app, a DTO's and ViewModels project, a business component unit test project, and an MVC unit test project. All in all, not too unusual. The business component had a Service reference to several WCF endpoints. Within the business component, the data contracts from the WCF end points gets automapped using AutoMapper into the data necessary for the ViewModels. The problem I wanted to solve was that the data contract POCO's in the autogenerated WCF proxies are all PUBLIC, so when I reference my business component from my MVC web app (actually injected via StructureMap so I can use a mock business component if I need to), I have access to the WCF POCO's from within the web app. Since several other developers will be working on the web app, I'd prefer them not to be tempted to directly use the WCF POCO's but instead go through the business components. So I removed the service reference in the business components and instead added a script that invokes SVCUTIL with the /INTERNAL flag so that the autogenerated classes are marked INTERNAL instead of public. However, now AutoMapper won't map to/from my data contract POCO's.

I could not find any documentation that would show me how to get AutoMapper to work with INTERNAL properties, so I pulled the source from github and modified TypeInfo.cs so that it ignored Fields and included nonpublic members. Now my solution works perfectly, but feels pretty hackish having my own custom version of AutoMapper. It seems there should be a way to map from WCF data contract POCO's without them having to be PUBLIC. What am I missing?

Changed TypeInfo.cs

private IEnumerable<MemberInfo> GetAllPublicReadableMembers()
{
    IEnumerable<Type> typesToScan = new[] { Type, Type.BaseType };

    if (Type.IsInterface)
        typesToScan = typesToScan.Concat(Type.GetInterfaces());

    return typesToScan
        .Where(x => x != null)
        .SelectMany(x => x.FindMembers(
            MemberTypes.Property, //changed this
            BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, //and this
            (m, f) => m is FieldInfo ||
                      m is PropertyInfo && ((PropertyInfo)m).CanRead && !((PropertyInfo)m).GetIndexParameters().Any(),
            null)
        );
}
Boiethios
  • 38,438
  • 19
  • 134
  • 183
Pat P
  • 651
  • 6
  • 4

3 Answers3

14

Just set the ShouldMapProperty property of your configuration object in the initialize method.

Here is an example using the static API, however, you should be able to achieve the same in a similar fashion by using the non-static API.

Mapper.Initialize(i =>
{
    i.ShouldMapProperty = p => p.GetMethod.IsPublic || p.GetMethod.IsAssembly;
    i.CreateMap<Source, Target>();                
});

If you use a profile, this must go in the constructor:

public class MyProfile : Profile
{
    public MyProfile()
    {
        ShouldMapProperty = arg => arg.GetMethod.IsPublic || arg.GetMethod.IsAssembly;

        // The mappings here.
    }
}
Boiethios
  • 38,438
  • 19
  • 134
  • 183
CShark
  • 2,183
  • 1
  • 24
  • 42
  • 2
    This is now part of `IMapperConfiguration`. Example: `new MapperConfiguration(cfg => { cfg.ShouldMapProperty = ...; });` – xr280xr Nov 08 '17 at 18:28
0

Have you thought about mapping to interfaces instead? Have the data contract implement an interface, and then just map to that. You could then explicitly implement the interface, effectively hiding those members.

Jimmy Bogard
  • 26,045
  • 5
  • 74
  • 69
  • Yes, that seems to work but means I'll need interfaces to match the dozens of data contracts, and then editing the autogenerated datacontracts to inherit the Interface. Too bad SVCUtil can't create the Interfaces with the client proxies. Writing the Interfaces myself is more work than just writing custom mappers. :( – Pat P Jul 09 '10 at 13:54
  • 2
    I think I can just incorporate this patch back into the fold. There's no technical reason I can't map back to all properties whether they're internal or not. – Jimmy Bogard Jul 09 '10 at 15:07
  • Its kinda hackish since it eliminates the opportunity to use fields instead of properties. I don't need fields, but some might. Maybe it would better to have an option to inject a custom property/method/field/etc finder in the configuration process much like the way custom formatters work. Anyway, keep up the good work. – Pat P Jul 09 '10 at 18:07
  • 1
    Does anyone know if this ever found its way into the AutoMapper code base? I'm using v1.1 and have the same problem. Any idea when v1.2 or v2 will be coming? – SonOfPirate Mar 16 '11 at 14:46
  • 1
    Hi Jimmy, will this be included in the next release? It would be nice to be able to use internals and still have AutoMapper installable via NuGet. – avesse Aug 25 '11 at 14:21
0

Use this. It works on private and internal fields just fine.

http://ragingpenguin.com/code/privatefieldresolver.cs.txt

usage:

// in one library
public class Foo
{
internal string _bar = "some value";
}

// in another library
public class FooModel
{
public string Bar { get; set; }
}

Mapper.CreateMap<Foo, FooModel>()
.ForMember(x => x.Bar, o => o.ResolveUsing(new PrivateFieldResolver("_bar")));

Works like a charm.

Drew
  • 227
  • 2
  • 3