2

I'm using Entity Framework 6.0.2 to map some simple hand-coded models to an existing database structure. The primary model at the moment is:

public class Occurrence
{
    public int ID { get; set; }
    public Guid LegacyID { get; set; }
    public string Note { get; set; }
    public virtual ICollection<OccurrenceHistory> History { get; set; }
}

(The OccurrenceHistory model isn't really relevant to this, but that part is working fine whereby EF loads up the child records for this model.)

The mapping is simple, and I try to be as explicit as I can be (since as the application grows there will be some less-intuitive mapping):

public class OccurrenceMap : EntityTypeConfiguration<Occurrence>
{
    public OccurrenceMap()
    {
        ToTable("Occurrence");
        HasKey(o => o.ID);
        Property(o => o.ID).IsRequired().HasColumnName("ID");
        Property(o => o.LegacyID).IsRequired().HasColumnName("LegacyID");
        Property(o => o.Note).IsUnicode().IsOptional().HasColumnName("Note");
    }
}

But if I add a private property to the model, EF tries to map it to the database. Something like this:

private OccurrenceHistory CurrentHistory { get; set; }

(Internal to the model I would have some logic for maintaining that field, for other private operations.) When EF generates a SELECT statement it ends up looking for a column called CurrentHistory_ID which of course doesn't exist.

I can make the property public and set the mapping to ignore it:

Ignore(o => o.CurrentHistory);

But I don't want the property to be public. The model is going to internally track some information which the application code shouldn't see.

Is there a way to tell EF to just ignore any and all private members? Even if it's on a per-map basis? I'd particularly like to do this without having to add EF data annotations to the models themselves, since that would not only be a bit of a leaky abstraction (persistence-ignorant models would then have persistence information on them) but it would also mean that the domain core assembly which holds the models would carry a reference to EntityFramework.dll everywhere it goes, which isn't ideal.

David
  • 208,112
  • 36
  • 198
  • 279
  • 1
    You could use the `[NotMapped]` attribute on the property. – EkoostikMartin Feb 17 '14 at 21:00
  • @EkoostikMartin: I'm kind of hoping not to have to add a dependency on `DataAnnotations` in the assembly which has the models. It feels like a leaky abstraction, since the models are then required to hold persistence information. – David Feb 17 '14 at 21:03
  • If you don't want to use data annotations you should be able to ignore a private property in the same way you can configure private properties - http://romiller.com/2012/10/01/mapping-to-private-properties-with-code-first/ – Pawel Feb 17 '14 at 21:11
  • @Pawel: It looks like that puts the EF mapping logic inside the model itself, unless I'm misinterpreting something. The assembly which holds the models in this case really shouldn't have a dependency on Entity Framework, since it's going to be shared among lots of other components and applications which have nothing to do with EF (perhaps even on platforms which don't include or support it). – David Feb 17 '14 at 21:14
  • DataAnnotation is completly independent from EF, it happends that EF conveniently use it for its purpose as you could too. – E-Bat Feb 17 '14 at 21:25
  • @Elio.Batista: Maybe I'm mistaken, but it looks like `NotMapped` is inside `EntityFramework.dll`, no? – David Feb 17 '14 at 21:26
  • 1
    @David - no, its in `System.ComponentModel.DataAnnotations.dll` – EkoostikMartin Feb 17 '14 at 21:27
  • [System.ComponentModel.DataAnnotations.Schema.NotMappedAttribute](http://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.schema.notmappedattribute(v=vs.110).aspx). – Erik Philips Feb 17 '14 at 21:29
  • @David - I have not tried that but I think you could create a partial class living in the same assembly as DbContext and this way you could avoid the dependency in the assembly that holds the model. – Pawel Feb 17 '14 at 21:29
  • @Pawel: Partial classes can't cross assemblies, unfortunately. (Though for very valid reasons.) I'm currently looking into why my reference to `System.ComponentModel` doesn't have this `NotMapped` attribute though, because according to the documentation it seems like it should... – David Feb 17 '14 at 21:31
  • @David - what version of the .Net framework are you using? – EkoostikMartin Feb 17 '14 at 21:34
  • @EkoostikMartin: 4.0, which I was really hoping wouldn't be an issue in this case. I'm not sure 4.5 is going to be an option on my client's servers. I'm also limited to Visual Studio 2010 in this client's environment. – David Feb 17 '14 at 21:35
  • Try adding the reference manually. – E-Bat Feb 17 '14 at 21:38
  • 1
    @David that is unfortunate - see this post - http://stackoverflow.com/questions/21162638/how-well-does-entity-framework-6-support-net-4-0?lq=1 – EkoostikMartin Feb 17 '14 at 21:40

1 Answers1

3

A colleague pointed me to a blog post that led to a very practical approach.

So what I have is a private property:

private OccurrenceHistory CurrentHistory { get; set; }

The core of the problem is that I can't use that in my mapping:

Ignore(o => o.CurrentHistory);

Because, clearly, the property is private and can't be accessed in this context. What the blog post suggests is exposing a public static expression which references the private property:

private OccurrenceHistory CurrentHistory { get; set; }
public static readonly Expression<Func<Occurrence, OccurrenceHistory>> CurrentHistoryExpression = o => o.CurrentHistory;

I can then reference that in the mapping:

Ignore(Occurrence.CurrentHistoryExpression);

As with anything, it's a mix of pros and cons. But in this case I think the pros far outweigh the cons.

Pros:

  • The domain core assembly doesn't need to carry a reference to EntityFramework.dll.
  • The persistence mapping is entirely encapsulated within the DAL assembly.

Cons:

  • Models need to expose a little information about their inner workings.

The con breaks encapsulation, but only slightly. Consuming code still can't access that property or its value on instances, it can only see that the property exists statically. Which, really, isn't a big deal, since developers can see it anyway. I feel that the spirit of encapsulation is still preserved on any given instance of the model.

David
  • 208,112
  • 36
  • 198
  • 279