Seth
Seth

Reputation: 2796

EF Code First, create new object in collection with a proxy

How can I create+persist+have-a-proxy-to a new instance of a code-first poco using only navigation property collections? In the bellow code, I show how you might want to do this, if using member functions to create POCOs that then create POCOs. You don't have a DbContext, but if you create an object and persist it using DbSet.Add, the returned object isn't a proxy, so you can't in turn use its DbSet.Add to add a different sub-object.

In this code, if you call MailingList.AddIncomingMessage("my message"), you get an exception at the "OOPS" comment, because the created msg isn't a proxy and thus its Message.doodads property is null.

class Doodad {
  public int ID { get; set; }
  public string doodad { get; set; };
}

class Message {
  public int ID { get; set; }
  public virtual MailingList mailingList { get; set; } 
  public virtual ICollection<Doodad> doodads { get; set; }
  public string text { get; set; }

  public void GetDoodadCreateIfNeeded(string doodad) {
    try {
      // won't be found since we just created this Message
      return this.doodads.First(d => d.doodad == doodad);
    } catch (Exception e) {
      Doodad newDoodad = new Doodad() { doodad=doodad };

      // OOPS! this.doodads == null, because its not a proxy object
      this.doodads.Add(newDoodad);
      return newDoodad;
    }
  }
}

class MailingList {
  public int ID { get; set; }
  public virtual ICollection<Message> messages { get; set; }

  public void AddIncomingMessage(string message) {
    var msg = new Message() { text=message };

    // we have no Context, because we're in a POCO's member function
    this.messages.Add(msg);

    var doodad = msg.GetDoodadCreateIfNeeded("bongo drums");
  }
}

EDIT: sorry guys, I forgot to put the property accessors and ID in for this simplified case, but I am using them in the actual code.

Upvotes: 0

Views: 1234

Answers (2)

Ladislav Mrnka
Ladislav Mrnka

Reputation: 364259

It has nothing to do with proxies. It is the same as any other code - if you want to use object / collection you must first initialize it! Your fist command:

return this.doodads.First(d => d.doodad == doodad);

doesn't throw exception because it didn't find doodad but because the doodads is null.

What do you need to do? You need to initialize collections before you first use them. You can do it:

  • Directly in their definition
  • In entity's constructor
  • In property getter (lazy initialization) once they are first needed - that would require to change your fields to properties which is btw. correct way to write classes in .NET
  • In your custom methods you can check if they are null and initialize them

Upvotes: 2

Doug
Doug

Reputation: 6442

Complementary to the navigation property, you need to have a property that is the Id of the foreign key.

So your MailingList will need to have this property:

  [Key] // this attribute is important
  public int Id { get; set; }

and you'll have to change the Message classe to have these properties:

public virtual int mailingListId { get; set; 
public virtual MailingList mailingList { get; set; }

The { get; set; } property is important, so that it is a property, not just a public attribute.

Upvotes: 1

Related Questions