Generic base layer for linq CRUD statements
In this post I will attempt to demonstrate building an abstract generic layer for managing a linq datacontext and automatically exposing methods for CRUD(Create, Read, Update, Delete). Essentially this method contains three components, a BaseManager which manages a DataContext, and entity object which is matched to a table using linq, and a DataContext which allows access to the database via the manager.
The premise behind this method is to put an interface on the linq managed object which exposes the primary key for the object. I have seen this done via reflection however I find I would rather implement a simple interface via a partial class to define the primary key because the odds are I will use the partial class for other OO helper methods and custom properties anyway. The interface looks like this:
public interface EntityObject
{
int Id { get; }
}
Then I simply rename the primary key for the object in my dbml to be “Id” and implement the interface on my partial class (using an address object as an example):
public partial class Address : EntityObject
{
}
At that point we have everything we need to know about the object to be able to select all, one, update, delete, and insert.
Observe the base class:
[DataObjectAttribute()]
public class BaseManager<ENTITY_OBJECT ,CONTEXT> where ENTITY_OBJECT : EntityObject where CONTEXT : DataContext
{
...
The manager contains a few different objects:
- An Exception which is set and exposed in case something goes wrong.
- A datacontext, which is set by the parent constructors.
- A private helper function to get the ITable Object base upon the type of EntityObject
- A method to call submit changes on the datacontext with ErrorHandling
The table helper function looks like this:
private ITable GetTable()
{
return this.MyDataContext.GetTable(typeof(ENTITY_OBJECT));
}
The SubmitChanges() method is as follows:
public bool SubmitChanges()
{
try
{
MyDataContext.SubmitChanges();
return true;
}
catch (Exception ex)
{
ErrorHandler handler = new ErrorHandler();
handler.LogError(ex);
Error = ex;
throw ex;
return false;
}
}
The save function is then as follows:
[DataObjectMethod(DataObjectMethodType.Update | DataObjectMethodType.Insert, true)]
public bool Save(ENTITY_OBJECT eObj)
{
if (eObj.Id == 0)
{
GetTable().InsertOnSubmit(eObj);
return SubmitChanges();
}
else
{
return Update(eObj);
}
}
We know that object is new by the primary key not being set. If it is then we attempt an update.
Delete:
[DataObjectMethod(DataObjectMethodType.Delete)]
public bool Delete(ENTITY_OBJECT eObj)
{
GetTable().DeleteOnSubmit(eObj);
return SubmitChanges();
}
Update:
[DataObjectMethod(DataObjectMethodType.Update, true)]
public bool Update(ENTITY_OBJECT eObj, ENTITY_OBJECT eOldObj)
{
GetTable().Attach(eObj, eOldObj);
return this.SubmitChanges();
}
Load:
[DataObjectMethod(DataObjectMethodType.Select)]
public ENTITY_OBJECT LoadEntity(int Id)
{
return (ENTITY_OBJECT)GetTable()
.AsQueryable().OfType<EntityObject>()
.Where<EntityObject>(e => e.Id == Id)
.SingleOrDefault();
}
Load All:
[DataObjectMethod(DataObjectMethodType.Select)]
public IEnumerable<ENTITY_OBJECT> FindAll(){
return GetTable().AsQueryable().OfType<ENTITY_OBJECT>();
}
Then all we need to do when we add a new entity is define the manager for the object, this will use generics to define the datacontext type and the entity type. All other custom linq will also be added to this class and will be used as objectdatasources.
The address example is as follows:
public class AddressManager : BaseManager<Address, UserDataContext>
{
public AddressManager()
{
base.MyDataContext = new UserDataContext();
}
public AddressManager(UserDataContext context)
{
base.MyDataContext = context;
}
}
That’s it, with those lines we then have error handling and your crud methods, also just as important, we have a layer to put custom data access statements and datacontext management. This layer could also be used as an entity factory and/or a wrapper around an entity by allowing a manager to contain the object it is managing.
Happy coding.
Joe.