Building N-Tier Applications
In most of the cases, you create client applications whose control is directly bound to domain classes managed by an OpenAccessContext. While this might be a reasonable approach for small applications, when you need to architect enterprise applications this tightly bound design could falls apart quickly.
When you build large applications, it is a good practice to separate your logic so that the UI is responsible for UI tasks (e.g. displaying data, collecting user actions), business logic is handled by business classes, and data access is handled by classes that are responsible specifically for data access.
A client-side application such as Windows Forms or WPF does offer the benefit of allowing you use a long-running context to manage the domain classes/objects while the end user is working on them. Note that this is different from disconnected client applications that consume services, whether that is a Silverlight application or even a disconnected Windows application.
Not all applications can be created in to a single domain. In fact, in this constantly evolving networked world, many application architectures have both the classic logical layers of presentation (UI), and data, but also are physically deployed across multiple PCs. While logical separation of layers can be accomplished into a single process (application domain) without many concerns about proxies, serialization, network protocols, marshalling, etc., applications that span from a client to servers in a data center, need to take all these into account. Fortunately, Telerik Data Access together with technologies like WCF Services is well suited for these n-tier scenarios.
In this topic, the basic create, read, update, and delete operations you will use in virtually all your n-tier applications will be covered.
Performing CRUD Operations in Disconnected Environments
Suppose, you have a sample WCF Service and you want to perform CRUD operations over your entities. The service interface is shown in the code snippet below:
[ServiceContract]
public interface IDemoService
{
[OperationContract]
Car LoadCarById(int id);
[OperationContract]
void InsertCar(Car newCar);
[OperationContract]
void DeleteCar(Car carToDelete);
[OperationContract]
void UpdateCar(Car carToUpdate);
}
<ServiceContract()>
Public Interface IDemoService
<OperationContract()>
Function LoadCarById(ByVal id As Integer) As Car
<OperationContract()>
Sub InsertCar(ByVal newCar As Car)
<OperationContract()>
Sub DeleteCar(ByVal carToDelete As Car)
<OperationContract()>
Sub UpdateCar(ByVal carToUpdate As Car)
End Interface
The LoadCarById method is a simple read method, which purpose is to load a car by id and return it to the service. The important moment here is to ensure that the car object is not changed tracked. Note that CreateDetachedCopy<T>(T entity) does a flat detach of a single entity without considering any reference fields. If any reference properties are loaded beforehand, they are ignored during the detached operation. All reference collections will be empty and the reference properties will be null.
public Car LoadCarById( int id )
{
using ( FluentModel dbContext = new FluentModel() )
{
Car car = dbContext.Cars.FirstOrDefault( c => c.CarID == id );
return dbContext.CreateDetachedCopy( car );
}
}
Public Function LoadCarById(ByVal id As Integer) As Car
Using dbContext As New FluentModel()
Dim _car As Car = dbContext.Cars.FirstOrDefault(Function(c) c.CarID = id)
Return dbContext.CreateDetachedCopy(_car)
End Using
End Function
The InsertCar method is pretty straightforward. It inserts a new car into the database by using the Add and SaveChanges method of the context.
public void InsertCar( Car newCar )
{
using ( FluentModel dbContext = new FluentModel() )
{
dbContext.Add( newCar );
dbContext.SaveChanges();
}
}
Public Sub InsertCar(ByVal newCar As Car) Implements IDemoService.InsertCar
Using dbContext As New FluentModel()
dbContext.Add(newCar)
dbContext.SaveChanges()
End Using
End Sub
The DeleteCar method deletes the car entity from the database. In the implementation of this method, the passed car object is first attached to the context. Calling the Delete method marks the object for deletion. SaveChanges deletes the car from the database.
public void DeleteCar( Car carToDelete )
{
using (FluentModel dbContext = new FluentModel())
{
Car attachedCar = dbContext.AttachCopy(carToDelete);
dbContext.Delete( attachedCar );
dbContext.SaveChanges();
}
}
Public Sub DeleteCar(ByVal carToDelete As Car) Implements IDemoService.DeleteCar
Using dbContext As New FluentModel()
Dim attachedCar As Car = dbContext.AttachCopy(carToDelete)
dbContext.Delete(attachedCar)
dbContext.SaveChanges()
End Using
End Sub
Respectively, the purpose of the UpdateCar method is to update the passed car object in the database. In the implementation of this method, the passed car object is first attached to the context. Calling SaveChanges will update the car in the database.
public void UpdateCar( Car carToUpdate )
{
using ( FluentModel dbContext = new FluentModel() )
{
dbContext.AttachCopy( carToUpdate );
dbContext.SaveChanges();
}
}
Public Sub UpdateCar(ByVal carToUpdate As Car) Implements IDemoService.UpdateCar
Using dbContext As New FluentModel()
dbContext.AttachCopy(carToUpdate)
dbContext.SaveChanges()
End Using
End Sub