views:

763

answers:

4

I would like to extend a class generated by Linq To Sql to initialize when a new object (=row) is created.

I want to add rows to a child table when a parent row is created.

I was hoping to use the Oncreated (partial) method do something like this:

partial class Product {
    partial void OnCreated() {
        // Fill default rows for FK relations
        this.Columns.Add(new ProductColumn {
            Name = "Name", ColumnType = 1
        });
        this.Columns.Add(new ProductColumn {
            Name = "Producer", ColumnType = 2
        });
    }
}

The OnCreated is called every time from the constructor. So also if the object will be loaded from the database after the call to OnCreated. And if the object is loaded from the database, do not want to execute the code.

So where can I add logic to my model to initialize an object(-graph)?

A: 

If you are using the designer -- and I presume you are because you are using partial classes -- you can add the association in the designer and the appropriate columns will get added to your object automatically. MSDN has a reference on how to create associations using the designer work surface.

tvanfosson
The association (relation) is already created, but now I want an initial fill of the child table when the parent row is created.
GvS
+1  A: 

I think you'd be better off in this instance to use a Factory to create your instances; So anywhere you current have code like:

Product p = new Product();

You would instead have:

Product p = ProductFactory.CreateProduct();

And your CreateProduct() method would look something like:

public Product CreateProduct()
{
    var p = new Product();
    p.Columns.Add(new ProductColumn {
        Name = "Name", ColumnType = 1
    });
    p.Columns.Add(new ProductColumn {
        Name = "Producer", ColumnType = 2
    });
    return p;
}
Chris Shaffer
The problem is that LINQ-to-SQL will ignore the factory
Marc Gravell
Like Marc says, Linq2Sql is not aware of the factory, so everywhere you use the class you have to remember to use the factory. But you cannot make the constructor private because then L2S will not work anymore. So you cannot "force" to use the factory.
GvS
Linq-to-SQL ignoring the factory is the point - The code will not be executed when Linq-to-SQL loads an object, only when a new object is instantiated.
Chris Shaffer
In some cases you do not want to bypass the factory. For instance when you are creating a new object via databinding. ... The factory is probably the way go.
GvS
+3  A: 

There's no built in method for it in the Linq to SQL generated classes. They call OnLoaded specifically when called from the DB, but they do not call anything when it's not. I suspect because there's no way to know for sure...

I'd recommend using the factory approach that Chris suggested.

Paul
+2  A: 

With LINQ-to-SQL, the entities are largely unaware of the parent data-context - only lazy-loaded members like EntitySet<T> know about this detail. You can identify data-context aware entity-sets by looking at .IsDeferred (assuming it is deferred) - this will be true if a data-context is involved, and false otherwise - but there are further problems:

  • LINQ-to-SQL attaches the data-context (for deferred execution) after the constructur (and OnCreated) has executed
  • If you add items manually in OnCreated/.ctor(), it breaks when LINQ-to-SQL attempts to attach the data-context
  • LINQ-to-SQL uses the default constructor, meaning that generics/new() etc will also share this code

So unfortunately; no, I don't think you can do this in OnCreated in any sensible way. You could:

  • have a second constructor for this use, that adds the extra items
  • use a factory method, i.e. a static .Create() method that builds items correctly

(since LINQ-to-SQL won't use either of the above itself)

Marc Gravell