3

I have a db first edmx model. The partial classes it generates have non-virtual simple properties. E.g.

//------------------------------------------------------------------------------
// <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>
//------------------------------------------------------------------------------

    public partial class Entity
    {
     public int Id {get;set;} //not virtual
     public string SomeOtherProperty {get;set;} //also not virtual
     public virtual ICollection<RelatedEntity> RelatedCollection {get;set;} //but 'navigational' properties are virtual.
    }

How to tell the designer to make all properties virtual?

h.alex
  • 902
  • 1
  • 8
  • 31
  • 3
    Probably, nohow. Navigation properties are declared virtual to provide a lazy loading of data. Primitive properties are always loading, so they are non-virtual always. – Mark Shevchenko Jun 01 '15 at 14:11
  • 1
    Oh great, I see Microsoft didn't actually strive to make testing easy.. How difficult would it have been to add this feature? Oh .. Alex.. – h.alex Jun 01 '15 at 14:32
  • 1
    As I see you shouldn't add this feature. EF testing is an integration testing, not an unit, so you don't need mock objects. – Mark Shevchenko Jun 01 '15 at 14:39
  • 1
    Except if you're doing [DDD](https://msdn.microsoft.com/en-us/magazine/dn342868.aspx) and want to assert that methods are called on your entities in which case you want your mock repository to return a mock entity. And then you can't provide values for properties of your entity. – h.alex Jun 01 '15 at 14:42
  • 1
    As I understand, entities are not domain objects, they are data-transfer objects without any logic. You can't make their properties **virtual** or **read-only** or **complexly calculated**. EF should build your domain objects from primitive entities. – Mark Shevchenko Jun 01 '15 at 14:47
  • 1
    Yes, yes, the ancient debate. However, there is no escaping the fact that Microsoft cemented their *opinion* into a supposedly enterprise ORM. – h.alex Jun 01 '15 at 14:56

1 Answers1

9

There is a simple solution.

Expand the model edmx and edit the Model.tt

Inside the file, find the CodeStringGenerator class, lookup this method:

public string Property(EdmProperty edmProperty)
{
    return string.Format(
        CultureInfo.InvariantCulture,
        "{0} {1} {2} {{ {3}get; {4}set; }}",
        Accessibility.ForProperty(edmProperty),
        _typeMapper.GetTypeName(edmProperty.TypeUsage),
        _code.Escape(edmProperty),
        _code.SpaceAfter(Accessibility.ForGetter(edmProperty)),
        _code.SpaceAfter(Accessibility.ForSetter(edmProperty)));
}

One simple edit will be enough:

public string Property(EdmProperty edmProperty)
{
    return string.Format(
        CultureInfo.InvariantCulture,
        "{0} {1} {2} {{ {3}get; {4}set; }}",
        //make properties virtual.
        AccessibilityAndVirtual(Accessibility.ForProperty(edmProperty)),
        _typeMapper.GetTypeName(edmProperty.TypeUsage),
        _code.Escape(edmProperty),
        _code.SpaceAfter(Accessibility.ForGetter(edmProperty)),
        _code.SpaceAfter(Accessibility.ForSetter(edmProperty)));
}

And that's it, for reference of future generations.

h.alex
  • 902
  • 1
  • 8
  • 31
  • Clean and Elegant. Is there any known drawbacks for modifying all the property as virtual? – Jeffery Lee Oct 18 '17 at 10:45
  • Depending on the inheritance design/strategy applied to the class. One applied here is pretty loose. If one wants to design the inheritance tree with care so that only the required properties are override-able they obviously shouldn't use this. Anyway, it's a common pattern famously used e.g. in NHibernate which allows for proxies to hook in. There is a small performance impact: https://stackoverflow.com/a/530822/1144090, but that shouldn't be of concern in 2017 imo. – h.alex Nov 05 '17 at 21:23
  • 1
    A quick conditional to target this to certain types and properties: `if(edmProperty.DeclaringType.ToString() == "MyNamespace.MyModelName" && new string[]{ "MyPropertyName1", "MyPropertyName2" }.Contains(_code.Escape(edmProperty)))` – MikeTV Aug 17 '18 at 16:55