wpfentity-frameworkef-core-3.0wpf-corewpf-core-3.0

ef.net core, properties instead of fields


So, we are porting an old wpf app into .net core wpf. As part of the effort we are also moving the data layer to EF.net core.

  1. Since we use Prism MVVM, within the DI service context we keep a live data context which tracks the entities being shown and edited by user through the wpf front end and via service.
  2. In order to implement "undo" functionality, we track the objects using entity.HasChangeTrackingStrategy(ChangeTrackingStrategy.ChangingAndChangedNotificationsWithOriginalValues);

On edit, the object gets updated, on Reload() it gets reverted from tracked original values. Object wise, the operations are ok, however, the wpf side of things is not working ok. It seems the ef.net core is a very maladjusted citizen of the wpf application, assuming its the sole beneficiary of INotifyPropertyChanged mechanism. Which is, of course, not true in the slightest for WPF.

  1. EF.net does not seem to use properties when loading, reloading etc. It works directly on underlying fields. As a consequence, NotifyPropertyChanged never fires so grid never refreshes with new data, on say dbContext.Entry(c).Reload();. So question #1, is there a way to force ef.net to work with properties and not fields?
  2. The ef.net implementation of INotifyPropertyChanged seems to be flawed. If you invoke PropoertyChanged event on a property, without actually changing it, it will mark the entity as Modified, even though it can easily compare original and current value. This prohibits us from manually raising property changed events to control WPF rendering. Not sure how to solve this, or if its even solvable because it only applies to this tracking strategy. I am open to any suggestion on how I can notify WPF elements without riling up ef.net.
  3. Determining the state of the object is rather convoluted operation involving dbContext.Entry(entry).State which exists outside of any WPF bindings within DataContext hierarchy. It makes binding on state virtually impossible (for example to change the background of dirty entries or show Save button, etc). We "solved" it by inheriting all our entity classes from NotifySetterObject base class which handles INotify* implementation and also keeps [NotMapped] bool IsDirty flag. Needless to say, I dislike this heavily, keeping two unlinked state flags sounds like an accident waiting to happen. I am wondering if you encountered similar problem and if you devised a better, more sane, solution.

EF.net seems to have undergone a breaking change transformation from framework ef.net in terms of being only well suited for detached context scenarios (like asp.net). Are we wasting our time here trying to fit this shoe on wpf? Should we maybe focus on a different OR engine more suited to live contexts?


Solution

  • Before trying to address your questions, let me point out some things:

    1. You are using EF Core 3.0, which currently is in preview (beta), so many things might not work as expected.

    2. If by ef.net you mean EF6, i.e. the old project you are porting was using EF6, it's not strongly necessary to switch to EF Core - EF6 is also getting .Net Core support - a preview version is available here.

    Now regarding your concrete questions:

    1. EF Core can use properties, field or both. This is configurable via fluent API at context, entity and property level. Just the EF Core 3.0 default has changed - you can see that in Breaking Changes - Backing fields are used by default. The link also shows how the get the desired behavior by using

      .UsePropertyAccessMode(PropertyAccessMode.FieldDuringConstruction)
      

    on model (all entities), entity (all entity properties) or property (specific entity property) builders.

    1. The behavior is explained in the ChangeTrackingStrategy documentation:

    Original values are recorded when the entity raises the PropertyChanging. Properties are marked as modified when the entity raises the PropertyChanged event.

    So it is by design, hence there is nothing you can do about it. But solving #1 eliminates the need of manually raising PropertyChanged event, and the two-way binding seem to work flawlessly.

    1. I'm not quite sure what exactly are you after here. But starting with v2.1, EF Core provides 2 State change events on ChangeTracker - Tracked and StateChanged, which (especially the second) can be used to provide state change notifications. But the actual mechanism for that is left for you. For instance, you can use EF Core 2.1+ entity constructor service injection feature to inject DbContext into entity, which in turn can subscribe to StateChanged event and raise PropertyChanged for its own unmapped State property etc.