​回到目录​​    

  在使用Linq to Sql做为底层ORM时,它为我们提供的数据上下文为DataContext对象,实现上我们通过拖动生成的DBML文件,它们都是继承自 System.Data.Linq.DataContext类型的,所以DataContext就是LINQ数据对象的基类,有时,我们可以通过这种类的多态性来动态创建DB的实例。

     在每个DataContext类中,它有几个实例的构造方法,用来让你创建DataContext的实例,如下:

1     /// <summary>
2 /// 使用默认的连接串创建实现(每拖一次数据库,就会产生一个连接串)
3 /// </summary>
4 public DataClasses1DataContext() :
5 base(global::test.Properties.Settings.Default.EEE114ConnectionString, mappingSource)
6 {
7 OnCreated();
8 }
9 /// <summary>
10 /// 使用指定的连接串,可能配置在config文件里
11 /// </summary>
12 /// <param name="connection"></param>
13 public DataClasses1DataContext(string connection) :
14 base(connection, mappingSource)
15 {
16 OnCreated();
17 }
18 /// <summary>
19 /// 使用使用了IDbConnection接口的对象创建实例
20 /// </summary>
21 /// <param name="connection"></param>
22 public DataClasses1DataContext(System.Data.IDbConnection connection) :
23 base(connection, mappingSource)
24 {
25 OnCreated();
26 }
27 /// <summary>
28 /// 使用连接串和数据库的映射文件来建立实例,mappingSource可能是一个XML文件
29 /// </summary>
30 /// <param name="connection"></param>
31 /// <param name="mappingSource"></param>
32 public DataClasses1DataContext(string connection, System.Data.Linq.Mapping.MappingSource mappingSource) :
33 base(connection, mappingSource)
34 {
35 OnCreated();
36

而我们在实现项目开发中,可能用第二种比较多,即

1 DataClasses1DataContext db=new LINQ.DataClasses1DataContext(System.Configuration.ConfigurationManager.ConnectionStrings["XXB"].ToString())

这样,在开发环境与生成环境只要配置一个CONFIG文件即可。灵活。

而今天的主题是线程共享的DbContext与私有的DbContext,所以开始书归正转了,对于ado.net架构中,我们往往使用一个static全局对象来完成数据访问工作,而在linq to sql中,如果你建立一个static对象,它会出现很多问题,这在实现开发过程中才可以体会到,所以,今天要说的不是static对象。

一 线程共享的DbContext,说清楚一点就是在一个线程内,你的DataContext对象是共享的,是一个对象,不是new出很多个datacontext对象来,这事实上是一种单例模式的体现,这没有问题,它解决了static对象所产生的问题,而又满足了多表关联查询时出现(不能实现不同数据上下文件的引用,linq to sql和Ef都是这样的)的问题。

代码:

datacontext生成工厂:

1     /// <summary>
2 /// 数据库建立工厂
3 /// Created By : 张占岭
4 /// Created Date:2011-10-14
5 /// Modify By:
6 /// Modify Date:
7 /// Modify Reason:
8 /// </summary>
9 internal sealed class DbFactory
10 {
11 #region Fields
12 static System.Timers.Timer sysTimer = new System.Timers.Timer(10000);
13 volatile static Dictionary<Thread, DataContext[]> divDataContext = new Dictionary<Thread, DataContext[]>();
14 #endregion
15
16 #region Constructors
17 /// <summary>
18 /// 类构造方法
19 /// </summary>
20 static DbFactory()
21 {
22 sysTimer.AutoReset = true;
23 sysTimer.Enabled = true;
24 sysTimer.Elapsed += new System.Timers.ElapsedEventHandler(sysTimer_Elapsed);
25 sysTimer.Start();
26 }
27 #endregion
28
29 #region Static Methods
30
31 /// <summary>
32 /// 订阅Elapsed事件的方法
33 /// </summary>
34 /// <param name="sender"></param>
35 /// <param name="e"></param>
36 static void sysTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
37 {
38 List<Thread> list = divDataContext.Keys.Where(item => item.ThreadState == ThreadState.Stopped).ToList();
39 for (int index = 0; index < list.Count; index++)
40 {
41 for (int refer = 0; refer < divDataContext[list[index]].Length; refer++)
42 if (divDataContext[list[index]][refer] != null)
43 {
44 divDataContext[list[index]][refer].Dispose();
45 divDataContext[list[index]][refer] = null;
46 }
47 divDataContext.Remove(list[index]);
48 list[index] = null;
49 }
50 }
51 /// <summary>
52 /// 通过工厂的制造模式获取相应的LINQ数据库连接对象
53 /// </summary>
54 /// <param name="dbName">数据库名称(需要与真实数据库名称保持一致)</param>
55 /// <returns>LINQ数据库连接对象</returns>
56 public static DataContextIntance(string dbName)
57 {
58 return Intance(dbName, Thread.CurrentThread, 1, 0);
59 }
60
61 /// <summary>
62 /// 通过工厂的制造模式获取相应的LINQ数据库连接对象
63 /// </summary>
64 /// <param name="dbName"></param>
65 /// <param name="dbCount"></param>
66 /// <param name="dbIndex"></param>
67 /// <returns></returns>
68 public static DataContextIntance(string dbName, int dbCount, int dbIndex)
69 {
70 return Intance(dbName, Thread.CurrentThread, dbCount, dbIndex);
71 }
72
73 /// <summary>
74 /// 通过工厂的制造模式获取相应的LINQ数据库连接对象
75 /// </summary>
76 /// <param name="dbName">数据库名称(需要与真实数据库名称保持一致)</param>
77 /// <param name="thread">当前线程引用的对象</param>
78 /// <param name="dbCount">linq to sql数据库数量</param>
79 /// <param name="dbIndex">当前索引</param>
80 /// <returns>LINQ对象上下文</returns>
81 public static DataContextIntance(string dbName, Thread thread, int dbCount, int dbIndex)
82 {
83 if (!divDataContext.Keys.Contains(thread))
84 {
85 divDataContext.Add(thread, new DbContext[dbCount]);
86 }
87 if (divDataContext[thread][dbIndex] == null)
88 {
89 divDataContext[thread][dbIndex] = new DbContext(dbName);
90 }
91 return divDataContext[thread][dbIndex];
92 }
93
94 /// <summary>
95 /// 通过工厂的制造模式获取相应的LINQ数据库连接对象
96 /// </summary>
97 /// <param name="dbName"></param>
98 /// <param name="thread"></param>
99 /// <returns></returns>
100 public static DataContextIntance(string dbName, Thread thread)
101 {
102 return Intance(dbName, thread, 1, 0);
103 }
104 #endregion
105
106

具体领域数据对象创建时代码如下:

1 /// <summary>
2 /// XXB数据库基类
3 /// </summary>
4 public class XXB_DataBase : DataBase
5 {
6 private readonly static string _conn;
7 static XXB_DataBase()
8 {
9 if (ConfigurationManager.ConnectionStrings["XXB"] == null)
10 throw new Exception("请设置XXB配置字符");
11 else
12 _conn = ConfigurationManager.ConnectionStrings["XXB"].ToString();
13 }
14 public XXB_DataBase()
15 : base(DbFactory.Intance(_conn, 2, 1))
16 { }
17
18

 

二 私有的DbContext,它要求你为每个表都建立一个repository对象,用户对表进行CURD操作,而它们都继承一个database,在 database里有唯一创建datacontext的入口,这样在做多表关联时,使用的是同一个datacontext对象,所以不会出现“不能实现不同数据上下文件的引用”这种问题,但这样方式感觉很不爽,因为你必须把所有多表关联的业务逻辑,写在DAL层,这是很郁闷的,因为一般我们会把它放在BLL层(更有利于业务的组合与重用)。

代码:

具体领域数据基类:

1  /// <summary>
2 /// XXB数据基类
3 /// </summary>
4 public abstract class XXBBase : DataBase
5 {
6 public XXBBase()
7 : base(new LINQ.DataClasses1DataContext(System.Configuration.ConfigurationManager.ConnectionStrings["XXB"].ToString()))
8 { }
9

统一数据基类:

1   /// <summary>
2 /// 标准数据操作基类
3 /// </summary>
4 public abstract class DataBase : IRepository
5 {
6 /// <summary>
7 /// 数据访问对象(只对子类可见)
8 /// </summary>
9 protected DataContext DB;
10
11 #region Constructors
12 public DataBase(DataContext db)
13 : this(() => { return db; })
14 { }
15 public DataBase(Func<DataContext> func)
16 {
17 this.DB = func();
18 }
19 #endregion
20
21 #region DBContext SubmitChanges
22 /// <summary>
23 /// XXB默认提交【重写时候可能需要写入自定义的类似约束的逻辑】
24 /// </summary>
25 protected virtual void SubmitChanges()
26 {
27 ChangeSet cSet = DB.GetChangeSet();
28 if (cSet.Inserts.Count > 0
29 || cSet.Updates.Count > 0
30 || cSet.Deletes.Count > 0)
31 {
32 try
33 {
34 DB.SubmitChanges(System.Data.Linq.ConflictMode.ContinueOnConflict);
35 }
36 catch (System.Data.Linq.ChangeConflictException)
37 {
38 foreach (System.Data.Linq.ObjectChangeConflict occ in DB.ChangeConflicts)
39 {
40 occ.Resolve(System.Data.Linq.RefreshMode.OverwriteCurrentValues);
41 occ.Resolve(System.Data.Linq.RefreshMode.KeepCurrentValues);
42 occ.Resolve(System.Data.Linq.RefreshMode.KeepChanges);
43 }
44 DB.SubmitChanges();
45 }
46 }
47 }
48
49 #endregion
50
51 #region IRepository 成员
52
53 public virtual void Update<TEntity>(TEntity entity) where TEntity : class
54 {
55 this.SubmitChanges();
56
57 }
58
59 public virtual void Update<TEntity>(IEnumerable<TEntity> list) where TEntity : class
60 {
61 list.ToList().ForEach(entity =>
62 {
63 this.Update<TEntity>(entity);
64 });
65 }
66
67 public virtual void Insert<TEntity>(TEntity entity) where TEntity : class
68 {
69 DB.GetTable<TEntity>().InsertOnSubmit(entity);
70 this.SubmitChanges();
71 }
72
73 public virtual void Insert<TEntity>(IEnumerable<TEntity> list) where TEntity : class
74 {
75 DB.GetTable<TEntity>().InsertAllOnSubmit<TEntity>(list);
76 this.SubmitChanges();
77 }
78
79 public virtual TEntity InsertGetIDENTITY<TEntity>(TEntity entity) where TEntity : class
80 {
81 this.Insert<TEntity>(entity);
82 return GetModel<TEntity>(i => i == entity).FirstOrDefault();
83 }
84
85 public virtual void Delete<TEntity>(TEntity entity) where TEntity : class
86 {
87 DB.GetTable<TEntity>().DeleteOnSubmit(entity);
88 this.SubmitChanges();
89 }
90
91 public virtual void Delete<TEntity>(IEnumerable<TEntity> list) where TEntity : class
92 {
93 DB.GetTable<TEntity>().DeleteAllOnSubmit<TEntity>(list);
94 this.SubmitChanges();
95 }
96
97 public virtual IQueryable<TEntity> GetModel<TEntity>() where TEntity : class
98 {
99 return this.DB.GetTable<TEntity>();
100 }
101
102 public virtual IQueryable<TEntity> GetModel<TEntity>(System.Linq.Expressions.Expression<Func<TEntity, bool>> predicate) where TEntity : class
103 {
104 return GetModel<TEntity>().Where(predicate);
105 }
106
107 public virtual TEntity Find<TEntity>(params object[] keyValues) where TEntity : class
108 {
109 var mapping = DB.Mapping.GetTable(typeof(TEntity));
110 var keys = mapping.RowType.IdentityMembers.Select((m, i) => m.Name + " = @" + i).ToArray();
111 TEntity entityTEntity = DB.GetTable<TEntity>().Where(String.Join(" && ", keys), keyValues).FirstOrDefault();
112 if (entityTEntity != null)
113 DB.Refresh(System.Data.Linq.RefreshMode.OverwriteCurrentValues, entityTEntity);
114 return entityTEntity;
115 }
116
117 #endregion
118

而用户模块User_InfoRepository在做多表关联时,是这样完成的:

1 public class User_InfoRepository : XXBBase
2 {
3 /// <summary>
4 /// 需要把Join的表关系写在这里
5 /// </summary>
6 /// <returns></returns>
7 public IQueryable<User_Info> GetDetailModel()
8 {
9 var linq = from data1 in base.GetModel<User_Info>()
10 join data2 in base.GetModel<User_Profile>() on data1.UserID equals data2.UserID
11 select data1;
12 return linq;
13 }
14 }

回到目录

作者:仓储大叔,张占岭,
荣誉:微软MVP