EF 4.0 - mapping to readonly property with private

2020-02-05 07:12发布

问题:

Is it possible to map following POCO class with EF 4.0?

public class MyClass
{
  private string _myData;

  public MyClass() 
  { }

  public MyClass(string myData)
  {
    _myData = myData;
  }

  public string MyData
  {
    get
    {
      return _myData;
    }
  }
}

In NHibernate I think it is possible when I use Access attribute in mapping like:

<class name="MyClass" table="MyTable">
  <property name="MyData" access="field.camelcase-underscore" column="MyCol" type="string" length="50" />
</class>

I wonder if there is some Access equivalent in EF 4.0? Currently I'm able to map the class only if I add protected setter to the MyData property:

  public string MyData
  {
    get
    {
      return _myData;
    }
    protected set
    {
      _myData = value;
    }
  }

It works but for legacy classes it means update all properties which don't have setter.

Edit:

I have updated the last code example because it also doesn't work with private setter. Setter has to be at least protected. If the setter is private or doesn't exists following exception is thrown:

System.InvalidOperationException: Mapping and metadata information could not be found for EntityType 'MyNamespace.MyClass'.

回答1:

This is for Code First POCO objects, to help out Shimmy and others wondering how all this works with Code First

I think that you may just not be understanding how Entity Framework works. This took me a while to grasp too.

Entity Framework works by Subclassing your POCO objects with a proxy class, which is used to serialize and deserialize your objects. This means that if you have a private set method or property (or it is missing all together), there is no way for the subclassed method to set the property. The setters and the properties that you want Entity framework to use MUST be either protected or public.

If you want your complex properties to be loaded lazily, you must also make those virtual, so Entity Framework can proxy those as well. If you want to eager load them, you must use the Include method.

To fully answer your question, yes, you have to go and add the setter properties to all of your properties that you want Entity Framework to set for you. No, Entity Framework does not provide a way for you to map properties that don't have a setter.

NHibernate works a bit differently in that it overrides all of your properties and I believe that it uses private variables in the subclasses it generates, sets the private variables, and the overridden properties then return the private variables. A.K.A., NHibernate doesn't need a setter on the property itself, whereas Entity Framework actually sets the property. The benefit to Entity Framework doing this is that it returns the actual POCO object that you created, not the subclassed object like NHibernate. The only time that you get a subclassed object is when you are using lazy loaded complex properties, where Entity Framework returns the proxy subclass. When you actually retrieve the data, the proxy sets itself to your POCO class again.

Your setter should be public or protected, like you have it in your question:

public class MyClass
{
    private string _myData;

    public MyClass() { }

    public MyClass(string myData)
    {
        // In case there is specialized logic, you should call the property setter here
        // unless the property is a virtual property. You should never call any virtual
        // methods or properties in your constructor.
        MyData = myData;
    }

    public string MyData
    {
        get
        {
            return _myData;
        }
        protected set
        {
            _myData = value;
        }
    }
}


回答2:

I played with this little bit and my conclusion is:

  • I can't map legacy types which don't have setter at all.
  • I can map types which have private setter but I must set Setter accessibility in EDMX as well. It is not enough to define setter accessibily in POCO class - EDMX must know about it.