using System;
using System.Collections.Generic;
using System.Linq;
using System.Data.Linq;
using System.Linq.Expressions;
namespace System.Data.Linq
{
///
/// Simple class to provide CRUD functionality for LINQ-to-SQL objects
///
public abstract class DatabaseBase where TContext : DataContext, new()
{
protected DatabaseBase() { }
#region DataContext Helpers
public static List Query(Func> f)
{
using (var dc = new TContext { ObjectTrackingEnabled = false }) return f(dc).ToList();
}
public static T Query(Func f)
{
using (var dc = new TContext { ObjectTrackingEnabled = false }) return f(dc);
}
public static void Use(Action f)
{
using (var dc = new TContext()) f(dc);
}
#endregion
#region Tables
protected static TableHelper CreateTable(Func> tableSelector)
where TItem : class
{
return new TableHelper(tableSelector);
}
/// Provides an easy way to create a specific TableBase.
/// A func that selects the needed table.
/// dc => dc.Accounts
/// An expression to select the table's key.
/// a => a.AccountId
protected static TableHelper CreateTable(Func> tableSelector, Expression> keySelectorExp)
where TItem : class
{
return new TableHelper(tableSelector, keySelectorExp);
}
protected static TableHelper CreateTable(Func> tableSelector,
Expression> key1SelectorExp, Expression> key2SelectorExp)
where TItem : class
{
return new TableHelper(tableSelector, key1SelectorExp, key2SelectorExp);
}
///
/// Class to provide CRUD functionality for a specific table. This is the base for TableHelper `TItem`TKey and TableHelper `TItem`TKey`TKey
///
public class TableHelper where TItem : class
{
public TableHelper(Func> tableSelector)
{
this.tableSelector = tableSelector;
}
protected Func> tableSelector;
#region Basic querying
public List Where(Expression> predicate)
{
return DatabaseBase.Query(dc => tableSelector(dc).Where(predicate));
}
public List Query(Func, IQueryable> f)
{
return DatabaseBase.Query(dc => f(tableSelector(dc)));
}
public TItem SingleOrDefault(Expression> predicate)
{
return DatabaseBase.Query(dc => tableSelector(dc).SingleOrDefault(predicate));
}
#endregion
#region Insert/Update
protected static List listApply(IEnumerable items, Func f) { return items.Select(f).ToList(); }
public List Insert(IEnumerable items)
{
return listApply(items, Insert);
}
public TItem Insert(TItem item)
{
return DatabaseBase.Query(dc => {
dc.ObjectTrackingEnabled = true;
tableSelector(dc).InsertOnSubmit(item);
dc.SubmitChanges();
return item;
});
}
public List Update(IEnumerable items)
{
return listApply(items, Update);
}
public TItem Update(TItem item)
{
return DatabaseBase.Query(dc => {
dc.ObjectTrackingEnabled = true;
tableSelector(dc).Attach(item, true);
dc.SubmitChanges();
return item;
});
}
#endregion
}
public class TableHelper : TableHelper where TItem : class
{
public TableHelper(
Func> tableSelector,
Expression> keySelectorExp
)
: base(tableSelector)
{
this.keySelectorExp = keySelectorExp;
this.compiledKeySelector = keySelectorExp.Compile();
}
Expression> keySelectorExp;
Func compiledKeySelector;
// Here we add the single key specific stuff
private Expression> createKeyPredicate(TKey key)
{
var itemParam = Expression.Parameter(typeof(TItem), "item");
var equalExp = Expression.Equal(
Expression.Constant(key, typeof(TKey)),
Expression.Invoke(keySelectorExp, itemParam));
return Expression.Lambda>(equalExp, itemParam);
}
public TItem SelectByKey(TKey key)
{
var pred = createKeyPredicate(key);
return SingleOrDefault(pred);
}
public void Delete(IEnumerable keys)
{
foreach (var key in keys) Delete(key);
}
public IList Delete(IEnumerable items)
{
return listApply(items, Delete);
}
public TItem Delete(TItem item)
{
TKey key = compiledKeySelector(item);
Delete(key);
return item;
}
public void Delete(TKey key)
{
// We have to select first to make sure the item exists
// This provides the same semantics as update
// A transaction is needed to enforce this guarantee
DatabaseBase.Use(dc => {
using (var txScope = new System.Transactions.TransactionScope()) {
var table = tableSelector(dc);
var pred = createKeyPredicate(key);
var item = table.SingleOrDefault(pred);
if (item == null) throw new ChangeConflictException("Row not found.");
table.DeleteOnSubmit(item);
dc.SubmitChanges();
txScope.Complete();
}
});
}
}
public class TableHelper : TableHelper where TItem : class
{
public TableHelper(
Func> tableSelector,
Expression> key1SelectorExp,
Expression> key2SelectorExp
)
: base(tableSelector)
{
this.key1SelectorExp = key1SelectorExp;
this.compiledKey1Selector = key1SelectorExp.Compile();
this.key2SelectorExp = key2SelectorExp;
this.compiledKey2Selector = key2SelectorExp.Compile();
}
Expression> key1SelectorExp;
Func compiledKey1Selector;
Expression> key2SelectorExp;
Func compiledKey2Selector;
// Dual key specific stuff
private Expression> createDualKeyPredicate(TKey1 key1, TKey2 key2)
{
var itemParam = Expression.Parameter(typeof(TItem), "item");
var key1EqExp = Expression.Equal(
Expression.Constant(key1, typeof(TKey1)),
Expression.Invoke(key1SelectorExp, itemParam));
var key2EqExp = Expression.Equal(
Expression.Constant(key2, typeof(TKey2)),
Expression.Invoke(key2SelectorExp, itemParam));
var and = Expression.And(key1EqExp, key2EqExp);
return Expression.Lambda>(and, itemParam);
}
public TItem SelectByKey(TKey1 key1, TKey2 key2)
{
var pred = createDualKeyPredicate(key1, key2);
return SingleOrDefault(pred);
}
public void Delete(IEnumerable> keys)
{
foreach (var key in keys) Delete(key.A, key.B);
}
public IList Delete(IEnumerable items)
{
return listApply(items, Delete);
}
public TItem Delete(TItem item)
{
var key1 = compiledKey1Selector(item);
var key2 = compiledKey2Selector(item);
Delete(key1, key2);
return item;
}
public void Delete(TKey1 key1, TKey2 key2)
{
// We have to select first to make sure the item exists
// This provides the same semantics as update
// A transaction is needed to enforce this guarantee
DatabaseBase.Use(dc => {
using (var txScope = new System.Transactions.TransactionScope()) {
var table = tableSelector(dc);
var pred = createDualKeyPredicate(key1, key2);
var item = table.SingleOrDefault(pred);
if (item == null) throw new ChangeConflictException("Row not found.");
table.DeleteOnSubmit(item);
dc.SubmitChanges();
txScope.Complete();
}
});
}
}
#endregion
}
}