views:

304

answers:

5

I know you can customise entities through partial classes, but what is the general approach for modifying properties on entities? What if i want to change the logic in the get/set? What about adding attributes? I don't want to edit the auto generated code - how do people get around this?

+1  A: 

Don't autogenerate code at all. You can build your own linq to sql classes without using the designer.

Edit: My lazy way of doing this would be to autogenerate the code, copy paste it into the non generated file, and delete the designer files. Up to you though.

marr75
so how do you use Linq2SQL then? you cant easily divorce the entities fom the supporting framework / datacontext, at least not as far as i can see... ?
MalcomTucker
There are other ways to generate your entities, if you prefer not to use the built-in designer; you can either run SqlMetal.exe on the command-line, or use a template-driven approach like CodeSmith templates (www.plinqo.com) or Damien Guard's T4 templates for Linq2SQL. The the extensibility story really stays the same.
marc_s
Damien Guard's T4 templates are at http://l2st4.codeplex.com/
marc_s
Voted up for recommending sqlmetal. I always forget the name of that. Why name applications mage, sqlmetal, etc. when so many other tools are named more descriptively?
marr75
+1  A: 

The Linq2SQL classes are partial classes, which means you can easily extend them by adding your own separate file and declaring another part of the partial class in there.

In that file, you can customize the class as needed - and since it's a separate file, the code generation will not overwrite it.

If you look at e.g. the "Contact" class in the AdventureWorks database, Linq2SQL will generate this in your AdventureWorks.designer.cs file:

[Table(Name="Person.Contact")]
public partial class Contact : INotifyPropertyChanging, INotifyPropertyChanged
{

Now you can add a "Contact.cs" file to your project, and extend that partial class, e.g. by introducing a new property "DisplayName":

public partial class Contact 
{
       public string DisplayName
       {  
          get { return string.Format("{0} {1}", FirstName, LastName); }
       }
    }

At compile time, these two parts of the class are merged together.

The other part are the partial methods - methods that are available for you to implement, but if they're not implemented, calls to them are being optimized out by the compiler.

For each object class in Linq2SQL, a whole slew of partial methods (a new feature in .NET 3.0) are being created - up to you to implement those!

partial void InsertContact(Contact instance);
partial void UpdateContact(Contact instance);
partial void DeleteContact(Contact instance);

partial void OnLoaded();
partial void OnValidate(System.Data.Linq.ChangeAction action);
partial void OnCreated();

partial void OnFirstNameChanging(string value);
partial void OnFirstNameChanged();

partial void OnLastNameChanging(string value);
partial void OnLastNameChanged();

Plenty of extension points!

Marc

marc_s
Yeah, sorry, I understand the concept of partial classes and how they work. My question is how you customise properties. You cannot define a property in a partial file with the same name as an autogenerated property - so the question remains, how do you customise properties in Linq2SQL entities? In your example, if DisplayName is a column in the db, you have no way that I can see of customising the encapsulated behaviour inside that property...
MalcomTucker
@Malcolm: no, existing properties can't be "overridden" or tweaked. In this case, you probably should look at using a template-based approach to create your Linq objects, and tweak the template to make the properties virtual so you can override them in a derived class.
marc_s
ahh ok, thanks, do you have any decent links to working with L2S in a more advanced way? templates for example?
MalcomTucker
ok, just seen your comment above, thanks :)
MalcomTucker
You could, however, set "Access" for the field to "Private" and then create your own property that wraps it. i.e. Set DisplayName as private and create a property "DispName" that is public that wraps the private DisplayName.
Jacob Proffitt
@Jacob - yes you could do that - not very pretty, a bit of a hack really. The *REAL* solution would be to make the properties in the generated LINQ-2-SQL classes virtual so they can be overridden
marc_s
Associations can be made virtual, why didn't they let us change the accessmodifier on the properties to virtual aswelll?
rotary_engine
A: 

As Linq2Sql Entities are not sealed you could derive from a Linq2Sql class and do your changes in the derived class.

In my projects, I wouldn't do any changes to a Linq2Sql class. Instead I build my own set of POCO's which I can tailor to my needs. Then I use Linq2Sql only to fill / persist my POCO's using a repository pattern.

Rob Connery has a great webcast series on his blog called ASP.NET MVC Storefront. In one of the first webcasts he covers using Linq2Sql as a repository in conjunction with POCO's

Andre Kraemer
A: 

Check this out:

It's WAY better than sqlmetal (which is what the designer uses to generate your code)

http://plinqo.com/default.aspx?AspxAutoDetectCookieSupport=1

Requires a Codesmith license though, but IMHO is very worth it.

A: 

Modifying the logic in get/set of properties in a class that isn't marked virtual isn't possible without some serious hacks (look into mocking frameworks for examples of how to get around those limitations). That is the whole point of marking something virtual, making it expendable at the cost of a virtual call and a performance hit.

The Metadatatype tag, however, will allow you to annotate an existing class with attributes that you can't modify:

Note: stolen from this related question

[MetadataType (typeof (BookingMetadata))]
public partial class Booking
{
 // This is your custom partial class     
}

public class BookingMetadata
{
 [Required] [StringLength(15)]
 public object ClientName { get; set; }

 [Range(1, 20)]
 public object NumberOfGuests { get; set; }

 [Required] [DataType(DataType.Date)]
 public object ArrivalDate { get; set; }
}
Alan Jackson