最近一段时间,精力都放在将linq to sql 引入项目中的工作,由于框架所采用的ioc是spring.net , 将linq to sql 和spring.net集成也是自然的事情.可以预测的是,将来spring.net 官方必然会做这个事情. 所以,我目前的计划也只是满足自身所需的功能即可. 同时,此文也只是提供一种方法,细节请大家多多斟酌.
对于linq to sql 而言,同spring.net 集成的最大好处是事务机制, 你可以获得spring.net 灵活多变的事务支持,并提供你的非linq to sql 数据访问和linq to sql 运行在同一事务下的可能. 由于DataContext 由于你使用IDbConnection作为创建DataContext的参数,因此,不需要写很多代码就能和spring.data 的ado.net 事务支持集成.
集成主要包括以下部分
1. IDataContextFactory和其缺省实现DataContextFactory
2. LocalDataContextFactoryObject ,其实如果原有的工厂类能符合ioc配置的需要,就没有必要为之提供对象的FactoryObject, 因为FactoryObject 通常需要重复一些配置属性,FactoryObject通常用于无法使用ioc配置的工厂类,通常,它也提供额外的配置检查功能. 我这个实现代码quick and dirty 建议大家不要学:)
3. DataContextTemplate
btw: 本来我不打算实现单独的DataContextFactory,但后来发现,为了符合spring.net 惯用法,这还是有必要的,现在的代码同上午略有不同
例子
TransactionTemplate tt
=
(TransactionTemplate)context.GetObject(
"
transactionTemplate
"
);
tt.Execute(
delegate
(ITransactionStatus status)
{
template.Execute(delegate(DataContext db)
{
Currency c = new Currency();
c.Id = "t";
c.Name = "t";
c.ExchangeRate = 1;
db.GetTable<Currency>().Add(c);
db.SubmitChanges();
return null;
});
template.Execute(delegate(DataContext db)
{
Currency c = db.GetTable<Currency>().FirstOrDefault(item => item.Id == "t");
db.GetTable<Currency>().Remove(c);
db.SubmitChanges();
return null;
});
return null;
}
);
可以在插入后加一行 throw new Exception(); 来看看数据是否已经插入到数据库来检查工作是否正常.
声明
<
objects
default-lazy-init
="true"
xmlns
="http://www.springframework.net"
xmlns:database
="http://www.springframework.net/database"
>
<
database:provider
id
="dbProvider"
provider
="SqlServer-2.0"
connectionString
="server=(local);user id=sa;pwd=;database=xxx"
/>
<
object
id
="dataContextFactory"
type
="SohoWorks.BusinessFramework.Data.Linq.LocalDataContextFactoryObject,SohoWorks.BusinessFramework"
>
<
property
name
="DbProvider"
ref
="dbProvider"
/>
<
property
name
="DataContextType"
value
="xxxx,xxx"
/>
</
object
>
<
object
id
="dataContextTemplate"
type
="SohoWorks.BusinessFramework.Data.Linq.DataContextTemplate,SohoWorks.BusinessFramework"
>
<
property
name
="DataContextFactory"
ref
="dataContextFactory"
></
property
>
</
object
>
<
object
id
="transactionManager"
type
="Spring.Data.Core.AdoPlatformTransactionManager,Spring.Data"
>
<
property
name
="DbProvider"
ref
="dbProvider"
/>
</
object
>
<
object
id
="transactionTemplate"
type
="Spring.Transaction.Support.TransactionTemplate,Spring.Data"
>
<
property
name
="PlatformTransactionManager"
ref
="transactionManager"
/>
</
object
>
</
objects
>
DataContextFactory 是IDataContextFactory的默认实现, 它主要功能就是从一个DataContextType声明实例化DataContext,它只关心使用IDbConnection参数的那两个构造函数, DataContextFactory还可以提供额外的属性声明,像这个例子中的DefaultObjectTrackingEnabled之类的,以便你在实例化时进行相应的初始化.
最最关键的代码可能就是ConnectionUtils.GetConnectionTxPair了
IDataContextFactory
public
interface
IDataContextFactory
{
DataContext GetDataContext();
}
DataContextFactory
public
class
DataContextFactory : IDataContextFactory
{
private ConstructorInfo constructorInfo;
private ConstructorInfo mappingSourceConstructorInfp;
private Type _dataContextType;
private IDbProvider dbProvider;
public DataContextFactory(IDbProvider dbProvider, Type dataContextType)
{
this.dbProvider = dbProvider;
constructorInfo = dataContextType.GetConstructor(new Type[] { typeof(IDbConnection) });
mappingSourceConstructorInfp = dataContextType.GetConstructor(new Type[] { typeof(IDbConnection), typeof(MappingSource) });
_dataContextType = dataContextType;
}
public MappingSource MappingSource
{
get;
set;
}
public IDbProvider DbProvider
{
get { return dbProvider; }
set { dbProvider = value; }
}
public bool DefaultDeferredLoadingEnabled
{
get;
set;
}
public bool DefaultObjectTrackingEnabled
{
get;
set;
}
public DataContext GetDataContext()
{
DataContext dc = null;
ConnectionTxPair pair = ConnectionUtils.GetConnectionTxPair(DbProvider);
if (MappingSource != null )
{
dc = (DataContext)constructorInfo.Invoke(new object[] { pair.Connection, MappingSource });
}
else
{
dc = (DataContext)constructorInfo.Invoke(new object[] { pair.Connection });
}
dc.DeferredLoadingEnabled = DefaultObjectTrackingEnabled;
dc.ObjectTrackingEnabled = DefaultObjectTrackingEnabled;
dc.Transaction = (DbTransaction)pair.Transaction;
return dc;
}
}
LocalDataContextFactoryObject 主要用于配置, 它现在提供DataContextFactory的singleton实例,如上面所说,有时它不是必须的,它也会重复一些配置代码
public class LocalDataContextFactoryObject : AbstractFactoryObject
{
public LocalDataContextFactoryObject()
{
DefaultDeferredLoadingEnabled = true;
DefaultObjectTrackingEnabled = true;
}
private Type _dataContextType;
private IDbProvider dbProvider;
public IResource MappingSource
{
get;
set;
}
public IDbProvider DbProvider
{
get { return dbProvider; }
set { dbProvider = value; }
}
public bool DefaultDeferredLoadingEnabled
{
get;
set;
}
public bool DefaultObjectTrackingEnabled
{
get;
set;
}
protected override object CreateInstance()
{
DataContextFactory factory = new DataContextFactory(DbProvider, DataContextType);
if (MappingSource != null && MappingSource.Exists)
{
using (Stream stream = MappingSource.InputStream)
{
factory.MappingSource = XmlMappingSource.FromStream(stream);
}
}
factory.DefaultDeferredLoadingEnabled = DefaultObjectTrackingEnabled;
factory.DefaultObjectTrackingEnabled = DefaultObjectTrackingEnabled;
return factory;
}
public override void AfterPropertiesSet()
{
base.AfterPropertiesSet();
if (DataContextType == null)
throw new ArgumentNullException("DataContextType");
if (DbProvider == null)
throw new ArgumentNullException("DbProvider");
}
public Type DataContextType
{
get { return _dataContextType; }
set
{
if (!typeof(DataContext).IsAssignableFrom(value))
throw new ArgumentException("DataContextType");
_dataContextType = value;
}
}
public override Type ObjectType
{
get { return typeof(IDataContextFactory); }
}
}
DataContextTemplate 是一个重要的类, 它依赖IDataContextFactory, 并常用的数据方法, 如FindById,FindListById,Delete,Save,Update,之类的这里只摘录主要部分
public
delegate
object
DoInDataContext(DataContext db);
public
class
DataContextTemplate
{
private IDataContextFactory dataContextFactory;
public IDataContextFactory DataContextFactory
{
get { return dataContextFactory; }
set { dataContextFactory = value; }
}
public void Execute(DoInDataContext doInDataContext){
using (DataContext db = dataContextFactory.GetDataContext())
{
doInDataContext(db);
}
}
public object Find(DoInDataContext doInDataContext)
{
using (DataContext db = dataContextFactory.GetDataContext())
{
return doInDataContext(db);
}
}
.
}
btw:
1. 我使用spring.net cvs上的代码,目前,如果要像我这样声明DbProvider ,则需要在应用程序启动前 加如下一行
NamespaceParserRegistry.RegisterParser(
typeof
(DatabaseNamespaceParser));
2. IFactoryObject接口是spring的魔术接口之一, 当你从spring ioc获取一个实现该接口的对象时,它返回的并不是对象本身,而是IFactoryObject.GetObject方法返回的具体对象. 如果你要获取对象本身,则需要在对象id前加&