LINQ to SQL (Visual Studio 2008) 提供 **強類型實體類別、編譯期驗證、IntelliSense、延遲載入** 等現代 ORM 體驗。但每個資料表對應一個實體類,導致 **資料存取邏輯 (Select, Where, Add, Update, Delete) 在每個 Repository 中重複**,違反 DRY 原則,難以構建乾淨的 N 層架構。本文示範利用 **泛型基類 + DataContext.GetTable()** 實作通用資料存取層。
public abstract class RepositoryBase where TEntity : class
{
protected DataContext Context { get; private set; }
protected RepositoryBase(string connectionString)
{
Context = new DataContext(connectionString);
}
// 通用查詢:支援 Lambda 表達式
public virtual IList Where(Func predicate)
{
return Context.GetTable().Where(predicate).ToList();
}
// 通用查詢:支援動態字串謂詞 (System.Linq.Dynamic)
public virtual IList Where(string predicate, params object[] values)
{
return Context.GetTable().Where(predicate, values).ToList();
}
// 取得單一實體 (依主鍵)
public virtual TEntity GetById(object id)
{
var table = Context.GetTable();
var keyProp = table.GetType().GetProperties().First(p => p.Name == "Id"); // 簡化:假設主鍵名為 Id
return table.FirstOrDefault(e => keyProp.GetValue(e).Equals(id));
}
// 新增
public virtual void Add(TEntity entity)
{
Context.GetTable().InsertOnSubmit(entity);
}
// 更新 (LINQ to SQL 自動追蹤變更,呼叫 SubmitChanges 即可)
public virtual void Update(TEntity entity)
{
// Attach 邏輯視情況實作
Context.GetTable().Attach(entity, true); // true = 視為已修改
}
// 刪除
public virtual void Delete(TEntity entity)
{
Context.GetTable().DeleteOnSubmit(entity);
}
// 批次儲存
public virtual void SaveChanges()
{
Context.SubmitChanges();
}
}
針對「執行階段決定篩選條件」 (如使用者自訂報表篩選、動態 Grid 排序分頁),整合 `System.Linq.Dynamic` 套件:
public static class DynamicQueryable
{
public static IQueryable Where(this IQueryable source, string predicate, params object[] values)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (predicate == null) throw new ArgumentNullException(nameof(predicate));
var lambda = DynamicExpression.ParseLambda(source.ElementType, typeof(bool), predicate, values);
return source.Provider.CreateQuery(
Expression.Call(
typeof(Queryable), "Where",
new Type[] { source.ElementType },
source.Expression, Expression.Quote(lambda)));
}
}
// Repository 中暴露
public IList WhereDynamic(string predicate, params object[] values)
{
return Context.GetTable().Where(predicate, values).ToList();
}
用法範例: `repo.WhereDynamic("CategoryID == @0 && UnitPrice > @1", 5, 100.0m);`
public class ProductRepository : RepositoryBase
{
public ProductRepository(string connStr) : base(connStr) { }
// 業務專用查詢:熱銷商品
public IList GetTopSelling(int count)
{
return Where(p => p.UnitsOnOrder > 0).OrderByDescending(p => p.UnitsOnOrder).Take(count).ToList();
}
// 複雜查詢:庫存預警
public IList GetLowStockAlert(int threshold)
{
return WhereDynamic("UnitsInStock <= @0 AND Discontinued == false", threshold);
}
}
public virtual IPagedList GetPaged(int pageIndex, int pageSize, Expression> filter, Expression> orderBy)
{
var query = Context.GetTable().Where(filter).OrderBy(orderBy);
return new PagedList(query, pageIndex, pageSize);
}
以 **泛型基類 + `GetTable()` + 動態查詢** 封裝 LINQ to SQL 通用 CRUD,能有效消除 Repository 樣板代碼,建立乾淨、可測試、可擴充的 N 層資料存取層。核心在於:**識別不變的存取模式 (變數:實體類型、謂詞),提煉為泛型協定;保留變化的業務邏輯於具體 Repository**。此設計模式跨越 ORM 框架,是資料存取層架構的永恆核心。