4

I have a DbContext with ProxyCreationEnabled set to true (actually it's the default value).

As far as I remember, this enables EF to load proxy entities from database, so any change we make to properties are recognized by the change tracker, and we can call SaveChanges() like this:

using (var db = new MyDbContext())
{
    var people = db.People.Where(p => p.Status = PersonStatus.New).ToList();
    foreach (var person in people)
    {
        person.Name = "Something";
    }
    db.SaveChanges();
}

The problem is: why would EF not use the proxy for a specific class, even though ProxyCreationEnabled is true? The class is not sealed, so it should be able to use proxy.

Here is my sample class:

public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
    public DateTime RegisterDate { get; set; }
    public PersonStatus Status { get; set; }
}
Alisson Reinaldo Silva
  • 10,009
  • 5
  • 65
  • 83
  • It looks correct. Could it just be the p.Status =, it should be two equal signs. I would try with First() instead of tolist to test a single record. – Thomas Koelle Jan 04 '18 at 13:11
  • @ThomasKoelle thanks for your help, actually that was a typo, in the actual code there are two equal signs. Either `First()` or `ToList()` return the class instead of the proxy. – Alisson Reinaldo Silva Jan 04 '18 at 13:19
  • *"The problem is: ...*" As soon as the query is not *no tracking* (your is not), you don't rely on lazy loading and `AutoDetectChangesEnabled` is `true`, the lack of proxy should not be a problem at all. – Ivan Stoev Jan 04 '18 at 13:21
  • @IvanStoev if I understood correctly, EF should be able to update any changes made to entities, even though they are not proxies? – Alisson Reinaldo Silva Jan 04 '18 at 13:32
  • 1
    Correct. Because EF `DbContext` "tracks" the entity instances internally, including the original and current values. – Ivan Stoev Jan 04 '18 at 13:33
  • 1
    @IvanStoev I didn't know that. In my case, values are not being persisted, but now that you said that, I think that's something else. I'm using `AutoMapper` to update the values from one list of objects (e.g PersonViewModel) to the destination existing entities. I thought it was due to proxies, since `AutoMapper` keeps the destination instances. I'm going to do more tests. Anyway, my question was properly answered by MegaTron, but you pointed something really interesting. – Alisson Reinaldo Silva Jan 04 '18 at 13:43
  • It was indeed `AutoMapper`. It wasn't using the same instance, it was creating new instances. I was using it as explained in [this answer](https://stackoverflow.com/a/2376102/2263507), but it was working. I just discovered when you use a `IEnumerable`, AutoMapper will create new instances. When you call the `Map()` method passing entity by entity (rather than the full enumerable), it preserves the existing instance. I had to do a `foreach` and call `Mapper.Map()` individually, then it worked. Thanks for your help. – Alisson Reinaldo Silva Jan 09 '18 at 10:28

1 Answers1

2

To generate proxy for property it should be virtual

public class Person
{
    public virtual int Id { get; set; }
    public virtual string Name { get; set; }
    public virtual DateTime RegisterDate { get; set; }
    public virtual PersonStatus Status { get; set; }
}

To get change tracking proxies, the basic rule is that your class must be public, non-abstract or non-sealed. Your class must also implement public virtual getters/setters for all properties that are persisted. Finally, you must declare collection based relationship navigation properties as ICollection<T> only. They cannot be a concrete implementation or another interface that derives from ICollection<T> (a difference from the Deferred Loading proxy)

Roman Marusyk
  • 23,328
  • 24
  • 73
  • 116
  • After changing all properties to virtual, it worked. The strange thing is that if I have at least one non-virtual property, EF won't use proxy. But I have other classes with non-virtual properties, and it does use proxy. What's wrong with this specific class? – Alisson Reinaldo Silva Jan 04 '18 at 13:20