Mapping private or protected properties with Code First (EFv4, CTP4)
If you’re specifying mapping in Code First in Entity Framework you’re essentially describing it with like this (assuming using EntityConfiguration class).
this.Something(x => x.Foo).Bar();
This is nice, but if the property you wanna to use is either private or protected you are hitting wall as you can’t write such expression. I faced the same problem today. The easiest approach is to have the configuration class nested to entity itself. But that’s not a clean (or may not be doable) and I do want clean code.
Because I know, Entity Framework can use these properties – if you’re using designer it’s just setting some fields in Properties window. So I concluded, that the only problem is to how to push it into Code First. My focus was primarily for MapSingleType method (but it’s doable for i.e. Property method as well).
With the method you can write two types of mapping, one using EntityMap class directly.
this.MapSingleType(x =>
EntityMap.Row(
EntityMap.Column(x.Foo, "FooColumn"),
EntityMap.Column(x.Bar, "BarColumn")))
.ToTable(new StoreTableName("Baz", "dbo"));
Or one using anonymous type.
this.MapSingleType(x => new
{
FooColumn = x.Foo,
BarColumn = x.Bar
})
.ToTable(new StoreTableName("Baz", "dbo"));
With the starting point set I decided the easiest way to specify private or protected properties will be using string. The only task is to create the Expression that’s else generated by compiler. After some juggling with the trees I created this extension method.
public struct ColumnPropertyMapping
{
public string Column { get; set; }
public string Property { get; set; }
public ColumnPropertyMapping(string column, string property)
: this()
{
this.Column = column;
this.Property = property;
}
}
public static EntityMap MapSingleType<TEntity>(this EntityConfiguration<TEntity> configuration, Expression<Func<TEntity, object>> initialMapping, params ColumnPropertyMapping[] additionalMappings)
where TEntity : class
{
if (additionalMappings == null)
throw new ArgumentNullException("additionalMappings");
List<Expression> newParameters = new List<Expression>();
var entity = initialMapping.Parameters[0];
Func<ColumnPropertyMapping, MethodCallExpression> makeColumnCall =
m =>
Expression.Call(
typeof(EntityMap),
"Column",
null,
Expression.Convert(
Expression.MakeMemberAccess(entity, typeof(TEntity).GetMember(m.Property, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).First()),
typeof(object)),
Expression.Constant(m.Column));
var callExpression = (initialMapping.Body as MethodCallExpression);
var newExpression = (initialMapping.Body as NewExpression);
if (callExpression != null)
{
newParameters.AddRange((callExpression.Arguments[0] as NewArrayExpression).Expressions);
}
else if (newExpression != null)
{
newParameters.AddRange(newExpression.Arguments.Select((e, i) => new ColumnPropertyMapping(newExpression.Members[i].Name, (e as MemberExpression).Member.Name)).Select(x => makeColumnCall(x)));
}
else
{
throw new ArgumentException("initialMapping");
}
newParameters.AddRange(additionalMappings.Select(x => makeColumnCall(x)));
var finalMapping = Expression.Lambda<Func<TEntity, object>>(
Expression.Call(
typeof(EntityMap),
"Row",
null,
Expression.NewArrayInit(
typeof(EntityMapColumn),
newParameters)),
entity);
return configuration.MapSingleType(finalMapping);
}
It’s method with similar signature as the original one, but taking extra collection of ColumnPropertyMapping, my helper objects to represent the mapping as strings. I take the input – EntityMap or anonymous object – peck up the important pieces and recreate the expression with added properties. I’m resulting to tree with EntityMap, as it looked easier to create. So now you can create the mapping also for non-public properties (and also dynamic mapping is easier).
this.MapSingleType(x =>
EntityMap.Row(
EntityMap.Column(x.Foo, "FooColumn"),
EntityMap.Column(x.Bar, "BarColumn")),
new ColumnPropertyMapping("SomeColumn", "ImNotPublic"))
.ToTable(new StoreTableName("Baz", "dbo"));
// or
this.MapSingleType(x => new
{
FooColumn = x.Foo,
BarColumn = x.Bar
},
new ColumnPropertyMapping("SomeColumn", "ImNotPublic"))
.ToTable(new StoreTableName("Baz", "dbo"));
Enjoy, if you need it.
I hope the CTP5 will address this “scenario”, thus I’ll not be forced to write it for other methods. And if not, stay tuned, I’ll definitely post it.



There's 9 Comments So Far
November 5th, 2010 at 06:54
I think that it is really bad idea. When you use strings to access properties, you lost refactoring ability. In code first scenario it is crucial. Other thing is you break encapsulation of object and it is code smell.
November 5th, 2010 at 09:19
If you know any other way to access the non-public property without reflection, feel free to comment or email me.
In fact it doesn’t break the encapsulation. We’re here not doing anything with the object or exposing something. We’re here describing metadata. It’s the same as writing for example XML (as BTW EMDX is):
<mapping>
<item><property>Foo</property><column>FooColumn</column></item>
<item><property>Bar</property><column>BarColumn</column></item>
<item><property>ImNotPublic</property><column>SomeColumn</column></item>
</mapping>
November 5th, 2010 at 19:49
The XML can be regenerated easily though? I think this method is fine ONLY IF you have the proper test cases in place
November 5th, 2010 at 19:58
Sure, you could generate the EDMX XML (or better SSDL, MSL and CSDL XMLs) as well, if you wanna do it.
If you’re using this method you need to be careful when refactoring property names – OTOH first you’ll initialize the context/model, you’ll get exception, so it’s not completely hidden and you’ll probably discover the issue pretty soon.
March 7th, 2011 at 02:36
This is entirely possible, even with CTP5. Here’s how:
Make your class a partial with fields and readonly properties…
public partial class Person
{
private int Id;
private string name;
public virtual int Id { get { return id; } }
public virtual string Name { get { return name; } }
}
Take advantage of a neseted exposition class…
public partial class Person
{
public static class Expressions
{
public static readonly Expression<Func> Id = x => x.id;
public static readonly Expression<Func> Id = x => x.name;
}
}
And in your map…
Property( x => x.Expressions.Name )
Done.
March 7th, 2011 at 02:36
Second expression should be named “Name” obviously…
March 7th, 2011 at 07:31
But then your entities and mapping isn’t completely separated. And that’s what you’re often trying to do – use clean entity objects and somewhere else define mapping.
March 17th, 2011 at 15:05
So make it a partial somewhere. I probably wouldn’t do this anyway, but it is the simplist solution to the question asked.
June 8th, 2011 at 14:55
Our solution was to use the [assembly: InternalsVisibleTo("Project1.Infrastructure.Data.EntityFramework")]
attribute in AssemblyInfo.cs. This was you can make your properties protected internal, then still use Lambda expressions in your configuration.
Share your thoughts, leave a comment!