2

I'm using Entity Framework Code First with a TPH layout. My DbSets reference the base (abstract) class, and then I operate on derived classes. EF picks up on this and automatically generates a Discriminator field in the table. I then want to initialize properties in specific ways depending on the child class. I currently do this in the constructor of the child class.

This all works great for saving to the database. However, when retrieving data the child constructors are running before data is loaded, resulting in both the constructor logic and database results showing up in the target collection.

public abstract class Indicator
{
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual ICollection<Annotation> Annotations { get; set; }

    protected Indicator()
    {
        Annotations = new List<Annotation>();
    }
}

public class MyIndicator : Indicator
{
    public MyIndicator()
    {
        Annotations = new List<Annotation>
        {
            new MyAnnotation() { Name = "First" },
            new MyAnnotation() { Name = "Second" },
            new MyAnnotation() { Name = "Third" }
        };
    }
}

public abstract class Annotation
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class MyAnnotation : Annotation {}

After populating a record in the database, attempting to retrieve it results in both the constructor Annotations and the Annotation objects from the database showing up in the collection.

Indicator newI = new MyIndicator { Name = "Custom collection" };
int count = 0;
var names = new List<string> { "Test 1", "Test 2", "Test 3" };
foreach (var a in newI.Annotations)
{ // overwite properties from "default" collection
    a.Name = names[count++];
}
context.Indicators.Add(newI);
context.SaveChanges();

Running: var i = context.Indicators.Single(x => x.Id == 1);

Returns:

Annotations = {
    [0] { Id = 0, Name = "First" } // constructor
    [1] { Id = 0, Name = "Second" }
    [2] { Id = 0, Name = "Third" }
    [3] { Id = 1, Name = "Test 1" } // database entries
    [4] { Id = 2, Name = "Test 2" }
    [5] { Id = 3, Name = "Test 3" }
}

This behavior seems bizarre and the only thing I can figure is that the constructor for the derived class is somehow called by the EF proxy required for lazy loading of the collection.

This is a very simplified version of the problem domain I'm working on. The ultimate goal is to have different types (derived classes) of Annotations initialized within the collection on Indicator based on the derived Indicator class. A factory or service would then fill in additional properties on the model using these "default" values within the Annotation derived class to select data from outside the application.

Update/Further Info

My intent is to store everything in the database. However, the type and number of derived types in the collection are unique to the derived class.

Example: Let's say I have a Car abstract class and a few derived classes: Sedan, Truck, Van. The Car abstract class also has a Collection of Parts; another abstract class with its own set of derived classes that contain "definitions". When I pass a Truck into my factory method, I would like to have a new Car to operate on with defaults according to the type: Truck.

class abstract Car
{
    ICollection<Part> Parts { get; set; }
    decimal Cost { get { return Parts.Sum(c => c.Cost) ?? 0; } }
}
class Truck : Car {
    public Truck() {
        Parts = new ICollection<Parts> {
            new SteeringWheel(),
            new Flatbed()
        }
    }
}

class abstract Part {
    string Material { get; set; }
    decimal Cost { get; set; }
}
class SteeringWheel : Part {
    public SteeringWheel() { Material = "Polyurethane"; }
}
class Flatbed : Part {
    public Part() { Material = "Steel"; }
}

When I new up a Truck in my factory, my Parts Collection contains a Flatbed made of Steel and a SteeringWheel made of Polyurethane. I can then iterate over the new collection to query an external source and return the cost of each item to populate its Cost property. The Sedan may have a SteeringWheel and a SunRoof, and the Van a SteeringWheel, a CargoDoor and a TowHitch.

Without initializing the Parts list with specific derived types, I would have to code all of this information into the factory itself. This becomes unweildy rather quickly as tiers are added. (Imagine a Car that has a collection of Part that has a collection of Material that has collections of Quantity and Dimension.)

2
  • I think you have to choose: either persisted or hard-coded lists. Why don't you store all list items in the database? I don't understand when you need which items, but mixing them seems like a very bad idea. For instance: you also have to prevent that the hard-coded items get persisted. Commented Dec 31, 2014 at 0:27
  • I updated the article to hopefully clear this up a bit. Commented Dec 31, 2014 at 17:36

1 Answer 1

1

Probably because EF can't differentiate between items it added vs those you added. As it adds each item, it probably starts by just making sure the List != null and doesn't create a new List. This allows for some flexibility/control in deriving the List. If you wanted to use some specialized List, but EF always began be create a new empty list before adding items, then it'd blow away your attempt to use some derived List or different type implementing IList/ICollection. I realize that's not what you're trying to do, but explaining why they might have chosen not to begin by clearing your list.

A bit of a hack, but you can create a constructor overload that takes a bogus parameter, since EF will use the default constructor. Do no default initialization in the default constructor. The overload you call explicitly when you want to generate a new entity and it will do the default initialization. If you need to do this up the inheritance hierarchy, then that constructor could use the base(someParam) syntax to call up to the parent class as well.

Sign up to request clarification or add additional context in comments.

4 Comments

I had considered that. However, that precludes me from using a generic factory down the road since a generic type must use the default constructor. Can't have your cake and eat it, too, eh?
@MicahLoffer If you plan to use a factory, then maybe all types implement interface IDefaultable { void InitializeDefaults(); } and your factory always calls this method just after construction to give the type an opportunity to initialize defaults.
This is a decent solution. Remembering to call InitializeDefaults is a little annoying, but certainly preferable to faux constructors.
@MicahLoffer Your factory could abstract that call away. PersonFactory.Create() { var person = new PersonEntity(); base.Create(person); return person; } where abstract virtual BaseFactory.Create(IDefaultable newEntity) { newEntity.InitializeDefaults(); } Doesn't buy you much, but it's a little easier to remember to always call a base method since that's a pretty common practice. That said, if I were in your shoes I'd probably end up refactoring it a couple more times before I was happy with the result and satisfied that it wasn't unnecessarily complex.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.