Unit of Work pattern vs. Transaction Scope for Linq-To-Sql?
I would like to mimic the Repository approach that is widely used in DDD with Linq-2-Sql in my portal app. So far I have this:
public class LinqToSqlDal<DC,T>: IDisposable, IRepository<T>
where T: LinqEntity, new(),
where DC: DataContext, new()
{
private DC unitOfWork = null;
public LinqToSqlDal(string connectionString)
{
this.unitOfWork = Activator.CreateInstance(typeof(DC), connectionString) as DC;
}
public LinqToSqlDal(string connectionString, DataLoadOptions loadOptions): this(connectionString)
{
this.unitOfWork.LoadOptions = loadOptions;
}
public virtual void SubmitChanges() {
this.unitOfWork.SubmitChanges();
}
public virtual List<T> Get(Expression<Func<T,bool>> query)
{
return this.unitOfWork.GetTable<T>().Where(query);
}
public virtual void Delete(Expression<Funct<T, bool>> query)
{
this.unitOfWork.GetTable<T>().DeleteAllOnSubmit(this.unitOfWork.GetTable<T>().Where(query));
}
public virtual T GetByID<T>(Expression<Funct<T, bool>> query)
{
return this.unitOfWork.GetTable<T>().Where(query).SingleOrDefault();
}
public virtual object Add(T entity, string IDPropertyName)
{
this.unitOfWork.GetTable<T>().InsertOnSubmit(entity);
this.SubmitChanges();
var ID = (string.IsNullOrEmpty(IDPropertyName)) ? null :
entity.GetType().GetProperty(IDPropertyN开发者_JAVA技巧ame).GetValue(entity, null);
return ID;
}
public virtual void SubmitChanges()
{
this.unitOfWork.SubmitChanges();
}
public void Dispose()
{
this.unitOfWork.Dispose();
}
}
So now I can use this with any Entity and DataContext that entity belongs to. My question is - would passing or instantiating a TransactionScope inside this little repository benefit? So far I have only one DataContext but can have multiple going forward, what can be done to the current design to ensure transactions across multiple data contexts?
Is this a good approach to wrap the context with using generics and let the clients dispose of it?
I would definitely create some sort of centralized but externally-controlled transaction control. Personally, I favor UnitOfWork, because you can design it to be an abstract that is tied only to the Repository model and not to any implementation-specific details.
Currently, your unit of work is implementation-specific. Your developers know that the object named unitOfWork is actually a DataContext for Linq2SQL. Knowing that, they can bypass the Repository completely and use a UnitOfWork to make their DB calls. It would be a bad idea for them to do so, but the fact they can suggests a need to more fully encapsulate the specific details behind a better abstract.
I would make UnitOfWork a token class; the token is merely an abstract placeholder that refers to the atomic set of operations. Behind the scenes, you can use UnitsOfWork to key a collection of DataContexts, and use the Context for a particular UnitOfWork whenever that token is presented to the Repository by a calling method (it would be passed as a parameter). When the UnitOfWork is discarded by external code, dispose of the DataContext. Designing your Repository like this means that consuming code does not require any knowledge of the implementation details. If you later decide that Linq2SQL isn't meeting your needs and you want to switch to NHibernate, the changes end at the Repository boundary; your consuming code doesn't give a flying flip whether the UnitOfWork refers to a DataContext or an ISession.
精彩评论