1

I have the following entities:

public abstract class Meter
{
    public int MeterId { get; set; }
    public string EANNumber { get; set; }
    public string MeterNumber { get; set; }
    public abstract void AddReading(CounterReading reading);
}

public abstract class ElectricityMeter : Meter { }

public class SingleElectricityMeter : ElectricityMeter
{
    private Counter _counter = new Counter();
    public virtual Counter Counter
    {
        get { return _counter; }
        set { _counter = value; }
    }

    public override void AddReading(CounterReading reading)
    {
        Counter.Readings.Add(reading);
    }
}

public class Counter
{
    public int CounterId { get; set; }

    private ObservableCollection<CounterReading> _readings = new ObservableCollection<CounterReading>();

    public virtual ObservableCollection<CounterReading> Readings
    {
        get { return _readings; }
        set { _readings = value; }
    }


    [NotMapped]
    public CounterReading CurrentReading
    {
        get
        {
            return Readings.MaxBy(m => m.Reading);
        }
    }
}

When I set the Counter relation in runtime, everything works perfect. When I try to load the saved data, my Counter object isn't loaded (it's the default Counter, and not the one with the data from my database).

Is this still because I'm using inheritance in my Meter? My database looks decent enough with foreign keys: enter image description here

Edit Retrieving other nested entities works without problem. enter image description here

Benjamin Diele
  • 1,177
  • 1
  • 10
  • 26
  • Have you tried it without using inheritance in your ElectricityMeter? Also, why are you using ObservableCollection instead of a plain Collection? – danludwig Jan 24 '15 at 17:42
  • @danludwig I haven't yet tried it without inheritance, but I hope the result will stay the same because using inheritance makes my life easier to write this application. I use ObservableCollection because that way I can let the UI update without writing any code myself. – Benjamin Diele Jan 24 '15 at 18:11
  • Can you show us the code that retrieves the SingleElectricityMeter from the database? – Dabblernl Jan 24 '15 at 18:14
  • @Dabblernl I get my `SingleElectricityMeter` via its property on another entity, which I get via `Context.Premises.First()`. – Benjamin Diele Jan 24 '15 at 18:17
  • The related entitities are not loaded automatically, which is a good thing, otherwise you may pull in the entire database with just one query. Do some reading about Lazy or Explicit Loading. 'Context.Premises.Include(premise=>premise.Counter)' should do the trick. – Dabblernl Jan 24 '15 at 18:28
  • @Dabblernl Lazy loading is enabled, because I can retrieve other properties multiple levels deep without any problem. See my update for an example. – Benjamin Diele Jan 24 '15 at 18:35
  • 1
    Iam not sure if this related to your problem, but you now have a setup where a single Counter has Many Meters. Shouldn't that be the other way around? Or, if a Meter has a single Counter, why don't you inline it as a Complex Type? – Dabblernl Jan 24 '15 at 18:57
  • @Dabblernl How can a Counter have multiple Meters? And what is inlining it as a complex type? – Benjamin Diele Jan 25 '15 at 07:49
  • Remove the Id from the Counter and the foreign key to it from the Meter. This will turn the Counter into a Complex type. This too must not be initialized unless you deal with a new Meter. – Dabblernl Jan 26 '15 at 11:43

1 Answers1

3

SingleElectricityMeter.Counter is a (reference) navigation property. In your code it is always initialized by

private Counter _counter = new Counter();

But you shouldn't initialize reference properties. EF will think they're loaded and won't load them from the database. So just remove the initialization. But be careful...

The encapsulation of adding readings in the top-level class is a good idea from an OO point of view, but in the context of Entity Framework (or any ORM) it comes with a caveat. In order to make this work you always have to ensure that a Meter is read from the database including the Counter and its Readings:

db.Meters.Include(m => m.Counter.Readings)

As long as you do this, EF will be tracking all objects it needs in order to notice that Readings causes a change it should store in the database.

Also, by doing so it is safe to access Counter.CurrentReading. Otherwise this might trigger lazy loading (may not be bad, but can be inefficient) or cause an error if this happens after the context has been disposed.

Community
  • 1
  • 1
Gert Arnold
  • 105,341
  • 31
  • 202
  • 291