I've been playing around with various designs to a DAL that uses LINQ to SQL in one of my side projects. Before LINQ, I would usually create a set of data manager classes that would perform CRUD operations on a database and return custom business entities. We have to modify this model when using LINQ to SQL though. First I'll show the example DAL code and usage, when I'll explain my approach for designing the DAL and go through the benefits.
Using Extension Methods on Table<Entity> objects to create DAL Data Manager classes
My approach to writing a DAL for LINQ to SQL is to write Extension Methods on the Table<Entity> properties that are already exposed on your DataContext. These extension methods perform CRUD operations and return IEnumerable<Entity> or Entity where appropriate. These extension methods replace the traditional data manager classes.
Simple Example Usage:
db.Products.GetByID(productID); db.Products.GetPopular(page, pageSize);
using (SlackLabsDataContext db = new SlackLabsDataContext()) { // load with features and sort them by popularity DataLoadOptions options = new DataLoadOptions(); options.LoadWith<Product>(p => p.Features); options.AssociateWith<Product>(p => p.Features.OrderByDescending(f => f.Rank).ThenByDescending(f => f.CreatedOn); db.LoadOptions = options; // popular products ProductsRepeater.DataSource = db.Products.GetPopular(page, pageSize); ProductsRepeater.DataBind(); // get other products with the same load options in the same Data Context // ... }
Example Implementation:
namespace LinqDalExample { public static class ProductExtensions { /// <summary> /// Gets a Product by its ID /// </summary> public static Product GetByID(this Table<Product> products, int productID) { if (productID < 0) throw new ArgumentOutOfRangeException("productID"); return products.Single(p => p.ID == productID); } /// <summary> /// Gets Products sorted by popularity and skip to the correct page /// </summary> public static IEnumerable<Product> GetPopular(this Table<Product> products, int page, int pageSize) { return (from p in products where p.IsPublished orderby p.Rank descending, p.CreatedOn descending select p).Skip(page * pageSize).Take(pageSize); }
Benefits
- Follows a traditional DAL pattern of using a set of "manager" classes to retrieve data in custom business entities or sets of entities
- ex: db.Products.GetPopular();
- The code reads very logically; we are getting popular products from a database
- All LINQ statements that query the database are centralized in these extension classes
- Reuses the Table<Entity> properties that are already exposed on your DataContext
- You can use the same context to make multiple db calls
- Control Eager Loading from the front-end instead of hard-coding it into the DAL since this is where we'll know what data we'll need
- ex: We have 2 web forms that consume the GetPopular method. Only one page needs to get product features. We can set the proper eager loading options on that one page and use a common GetPopular method in the DAL. Otherwise we'd need multiple DAL methods: GetPopular and GetPopularWithFeatures. We wouldn't be able to use the same data context this way.
- Maintain Deferred Execution on LINQ queries
- Database is queried only when the data is needed, ex: on DataBind() or other methods which will call Enumerate on the set
- You can modify the query on the front end. Ex: to implement paging or limit the result set
- Return IEnumerable instead of List or Array for flexibility
by using extension methods on the table class, we are 'querying' on the table, which seems very logical
Feedback
I'd like to hear your opinion on this approach. Has anyone found another pattern that works well for them? Have you found any issues with this approach?
see here
ReplyDeletehttp://java.pakcarid.com//default.aspx?sub=25&Sls=25