此文是我在项目中应用linq to sql 的一些总结, 它正在随项目进行不断修正中,这里应该没有最正确的一说 有两个决策会影响你的应用程序结构和编码 首先,你是否打算支持remoting或是webservice ,其次,你是否打算支持批量更新. 其中第一点尤为重要. 支持remoting/webservice的应用结构 项目通常划分为 myapplication.services 提供远程服务的调用 myapplication.models 依赖myapplication.services myapplication.views 依赖myapplication.models myapplication 启动应用 不支持remoting/webservice应用结构 myapplication.models 直接使用datacontext myapplication.views 依赖myapplication.models myapplication 启动应用 myapplication.models 通常有包括linq to sql 的数据类并分离类 ,所有的业务逻辑将在分离类中,并使用ddd 开发,或许你的应用程序现在不打算支持remoting/webservice,但需要考虑以后支持remoting/webservice,有以下几点建议 1. 在view层,确保只使用models 层提供的方法 2. 在view层,最好不使用关系 3.总是使用ToList,ToArray等返回的结果集 当使用关系导航时,虽然简洁,但依赖datacontext,因此在远程环境中windows forms比web环境更为糟糕, 因为web环境通常在一台机器上,还有个request范围可用 . DataContext的使用建议 无论是否打算在remoting/webservice环境,保持在一次请求中使用和放弃DataContext是一个好的想法,如果你不想这样做,linq to sql 固有的问题也不会让使用一个长久的DataContext.比方说下面的代码(特别的,使用了自定义的关键字值 Department d=new Department(); d.Id="61"; db.Departments.Add(d); db.SubmitChanges(); db.Departments.Remove(d); db.SubmitChanges(); Department d2=new Department(); d2.Id="61"; db.Departments.Add(d2); db.SubmitChanges() ; //这里出现重复的关键字错误,注意这个错误不是由数据库 触发的,因为数据库中本来就已经把早先的61给删除了,这应该是datacontext内部的identity map 出错了 可以学习spring/spring.net 的template机制,创建一个DataContextTemplate类 , 让service引用这个类,如果不考虑remoting/webservice 也可以在entity基类 中并入DataContextTemplate的方法,前者的分离度会更高些,但总是需要确保entity类不会直接访问dataContext 下面是DataContextTemplate的接口设计 public delegate void DoInDataContext(DataContext db); public delegate object DoInDataContextWithResult(DataContext db); public interface DataContextTemplate{ void Find(DoInDataContext doInDataContext); IList<TEntity> GetObjects<TEntity>(DoInDataContextWithResult callback) where TEntity : class; TEntity GetObject<TEntity>(DoInDataContextWithResult callback) where TEntity : class; IList<TEntity> FindAll<TEntity>() where TEntity : class ; IList<TEntity> FindByLimit<TEntity>(int limit) where TEntity:class; TEntity FindById<TEntity>(System.Linq.Expressions.Expression<Func<TEntity, bool>> predicate) where TEntity:class; IList<TEntity> FindRelationById<TEntity>(System.Linq.Expressions.Expression<Func<TEntity, bool>> predicate) where TEntity:class; IList<TEntity> FindByCondition<TEntity>(string condition,string ob,params object[] conditionParams) where TEntity:class; void Execute(DoInDataContext doInDataContext); void SubmitChanges(ChangeSet changeSet); ChangeSet GetChangeSet(Type type); }
models的设计 下面总结一些共性的东西 1. parital 类应该实现IDataErrorInfo接口,建议将其实现为基类 2. 使用OnxxxChanging或是OnxxxChanged partial 方法进行数据验证 3. 使用OnxxxChanged partial method 进行关联更新 3. 如果不考虑远程,可在entity的基础类 中实现一个DataContextTemplate 的变量,如db,这样,子类就可以直接调用它 4. 以下是一些公共方法的签名,这些方法可以简单的借助DataContextTemplate来实现 FindAll 返回全部记录 FindById 返回该id的记录 FindByCondition 执行动态查询 FindByLimit 执行top查询 FindByRelation 执行关系端查询,Relation可用具体的关系端名来替代 范型方法例子(todo) 关系 在linq to sql 中,使用EntityRef,EntitySet来维系关系,这是因为关系端的获取可能有left outer join和lazy load等优化方式,当你放弃linq to sql的关系后 ,意味着你自己需要用传统的类型来做关系的管理. 其实,Table<Enttiy>.Attach适合远程这类环境,但 在实际的编程中,要么用要么不用,否则,编码没有一致性,会导致一些bug和以后的维护问题 创建changeset 当使用批量更新并在每次需求间请求和放弃DataContext时,客户端维护自身的ChangeSet就相当重要,ChangeSet保存了新增,修改和删除的对象,在合适的时候,比方说用户点击保存时提交. 当新建,改变(可跟踪bindingSource的ListChanged事件),删除时,将改变的对象写入changeset 更新时,从changeset 取出 对象进行更新
更新顺序 同数据集一样,依次处理插入,更新,删除 ,并处理好关系端,不要过分依赖submitchanges的智能判断 适应ChangeSet的模型方法 master-detail记录 当使用批量更新时,master并没有保存到数据库中, 但detail 通常需要 master 的主键值 |