Implementing Business Logic (LINQ to SQL)
The term "business logic" in this topic refers to any custom rules or validation tests that you apply to data before it is inserted, updated or deleted from the database. Business logic is also sometimes referred to as "business rules" or "domain logic." In n-tier applications it is typically designed as a logical layer so that it can be modified independently of the presentation layer or data access layer. The business logic can be invoked by the data access layer before or after any update, insertion, or deletion of data in the database.
The business logic can be as simple as a schema validation to make sure that the type of the field is compatible with the type of the table column. Or it can consist of a set of objects that interact in arbitrarily complex ways. The rules may be implemented as stored procedures on the database or as in-memory objects. However the business logic is implemented, LINQ to SQL enables you use partial classes and partial methods to separate the business logic from the data access code. The following diagram illustrates the relationship of the business logic to LINQ to SQL classes:
How LINQ to SQL Invokes Your Business Logic
When you generate an entity class at design time, either manually or by using the Object Relational Designer or SQLMetal, it is defined as a partial class. This means that, in a separate code file, you can define another part of the entity class that contains your custom business logic. At compile time, the two parts are merged into a single class. But if you have to regenerate your entity classes by using the Object Relational Designer or SQLMetal, you can do so and your part of the class will not be modified.
The partial classes that define entities and the DataContext contain partial methods. These are the extensibility points that you can use to apply your business logic before and after any update, insert, or delete for an entity or entity property. Partial methods can be thought of as compile-time events. The code-generator defines a method signature and calls the methods in the get and set property accessors, the DataContext constructor, and in some cases behind the scenes when SubmitChanges is called. However, if you do not implement a particular partial method, then all the references to it and the definition are removed at compile time.
In the implementing definition that you write in your separate code file, you can perform whatever custom logic is required. You can use your partial class itself as your domain layer, or you can call from your implementing definition of the partial method into a separate object or objects. Either way, your business logic is cleanly separated from both your data access code and your presentation layer code.
A Closer Look at the Extensibility Points
The following example shows part of the code generated by the Object Relational Designer for the DataContext class that has two tables: Customers and Orders. Note that Insert, Update, and Delete methods are defined for each table in the class.
Partial Public Class Northwnd
Inherits System.Data.Linq.DataContext
Private Shared mappingSource As _
System.Data.Linq.Mapping.MappingSource = New _
AttributeMappingSource
#Region "Extensibility Method Definitions"
Partial Private Sub OnCreated()
End Sub
Partial Private Sub InsertCustomer(instance As Customer)
End Sub
Partial Private Sub UpdateCustomer(instance As Customer)
End Sub
Partial Private Sub DeleteCustomer(instance As Customer)
End Sub
Partial Private Sub InsertOrder(instance As [Order])
End Sub
Partial Private Sub UpdateOrder(instance As [Order])
End Sub
Partial Private Sub DeleteOrder(instance As [Order])
End Sub
#End Region
public partial class MyNorthWindDataContext : System.Data.Linq.DataContext
{
private static System.Data.Linq.Mapping.MappingSource mappingSource = new AttributeMappingSource();
#region Extensibility Method Definitions
partial void OnCreated();
partial void InsertCustomer(Customer instance);
partial void UpdateCustomer(Customer instance);
partial void DeleteCustomer(Customer instance);
partial void InsertOrder(Order instance);
partial void UpdateOrder(Order instance);
partial void DeleteOrder(Order instance);
#endregion
If you implement the Insert, Update and Delete methods in your partial class, the LINQ to SQL runtime will call them instead of its own default methods when SubmitChanges is called. This enables you to override the default behavior for create / read / update / delete operations. For more information, see Walkthrough: Customizing the Insert, Update, and Delete Behavior of Entity Classes.
The OnCreated method is called in the class constructor.
Public Sub New(ByVal connection As String)
MyBase.New(connection, mappingSource)
OnCreated()
End Sub
public MyNorthWindDataContext(string connection) :
base(connection, mappingSource)
{
OnCreated();
}
The entity classes have three methods that are called by the LINQ to SQL runtime when the entity is created, loaded, and validated (when SubmitChanges is called). The entity classes also have two partial methods for each property, one that is called before the property is set, and one that is called after. The following code example shows some of the methods generated for the Customer class:
#Region "Extensibility Method Definitions"
Partial Private Sub OnLoaded()
End Sub
Partial Private Sub OnValidate(action As _
System.Data.Linq.ChangeAction)
End Sub
Partial Private Sub OnCreated()
End Sub
Partial Private Sub OnCustomerIDChanging(value As String)
End Sub
Partial Private Sub OnCustomerIDChanged()
End Sub
Partial Private Sub OnCompanyNameChanging(value As String)
End Sub
Partial Private Sub OnCompanyNameChanged()
End Sub
' ...Additional Changing/Changed methods for each property.
#region Extensibility Method Definitions
partial void OnLoaded();
partial void OnValidate();
partial void OnCreated();
partial void OnCustomerIDChanging(string value);
partial void OnCustomerIDChanged();
partial void OnCompanyNameChanging(string value);
partial void OnCompanyNameChanged();
// ...additional Changing/Changed methods for each property
The methods are called in the property set accessor as shown in the following example for the CustomerID property:
Public Property CustomerID() As String
Set
If (String.Equals(Me._CustomerID, value) = False) Then
Me.OnCustomerIDChanging(value)
Me.SendPropertyChanging()
Me._CustomerID = value
Me.SendPropertyChanged("CustomerID")
Me.OnCustomerIDChanged()
End If
End Set
End Property
public string CustomerID
{
set
{
if ((this._CustomerID != value))
{
this.OnCustomerIDChanging(value);
this.SendPropertyChanging();
this._CustomerID = value;
this.SendPropertyChanged("CustomerID");
this.OnCustomerIDChanged();
}
}
}
In your part of the class, you write an implementing definition of the method. In Visual Studio, after you type partial you will see IntelliSense for the method definitions in the other part of the class.
Partial Public Class Customer
Private Sub OnCustomerIDChanging(value As String)
' Perform custom validation logic here.
End Sub
End Class
partial class Customer
{
partial void OnCustomerIDChanging(string value)
{
//Perform custom validation logic here.
}
}
For more information about how to add business logic to your application by using partial methods, see the following topics:
How to: Add Validation to Entity Classes
Walkthrough: Customizing the Insert, Update, and Delete Behavior of Entity Classes
Walkthrough: Adding Validation to Entity Classes
See Also
Reference
Partial Classes and Methods (C# Programming Guide)
SqlMetal.exe (Code Generation Tool)
Concepts
Partial Methods (Visual Basic)