走进Linq-Linq to SQL源代码赏析 DataContext的初始化
2008-08-14 12:02 横刀天笑 阅读(4136) 评论(12) 编辑 收藏 举报
话说DataContext是Linq to SQL的入口点,那么我们就从DataContext开始我们的赏析之旅吧。
DataContext的构造函数(先看我们用的最多的那个):
public DataContext(string fileOrServerOrConnection)
{
this.objectTrackingEnabled = true;
this.deferredLoadingEnabled = true;
if (fileOrServerOrConnection == null)
{
throw Error.ArgumentNull("fileOrServerOrConnection");
}
this.InitWithDefaultMapping(fileOrServerOrConnection);
}
this.objectTrackingEnabled = true;
this.deferredLoadingEnabled = true;
这两个设置非常重要,但是这里先不说了。
现在我们到InitWithDefaultMapping方法里:
private void InitWithDefaultMapping(object connection)
{
this.Init(connection, new AttributeMappingSource());
}
调用的是Init方法,看看Init方法:
private void Init(object connection, MappingSource mapping)
{
MetaModel model = mapping.GetModel(base.GetType());
this.services = new CommonDataServices(this, model);
this.conflicts = new ChangeConflictCollection();
if (model.ProviderType == null)
{
throw Error.ProviderTypeNull();
}
Type providerType = model.ProviderType;
if (!typeof(IProvider).IsAssignableFrom(providerType))
{
throw Error.ProviderDoesNotImplementRequiredInterface(providerType, typeof(IProvider));
}
this.provider = (IProvider)Activator.CreateInstance(providerType);
this.provider.Initialize(this.services, connection);
this.tables = new Dictionary<MetaTable, ITable>();
this.InitTables(this);
}
接受两个参数:一个连接对象,从前面可以看出这个连接对象可能是一个连接串,也可能是SQL Server Express的文件路径,还可以是一个IDbConnection对象。第二个参数是一个MappingSource类型的对象,这里就有点猫腻了。MappingSource(它是一个抽象类)是用来构建我们映射模型的,在Linq to SQL里提供两种映射方式:基于Attribute的和使用外部的XML配置文件。那么对应的,MappingSource就有两个子类:AttributeMappingSource和XmlMappingSource(我们还可以提供自己的映射方式)。
再回去看Init方法,它首先使用MappingSource的GetModel方法获得一个MetaModel对象。如果你使用Reflector展开System.Data.Linq.Mapping命名空间,你会发现这里有很多以Meta开头的抽象类:MetaModel,MetaTable,MetaFunction,这是干什么用的呢?还记得使用Attribute的时候有Database,Table,Function么?一个MetaModel就是描述一个数据库和一个DataContext类型(你可以继承这个类型)之间的映射模型,同理,MetaFunction就是存储过程或用户自定义函数与.NET的方法之间的映射模型。
我们来看看GetModel方法,它的内容很多,实际就一句话:
model = this.CreateModel(dataContextType)
调用MappingSource的另外一个方法CreateModel创建MetaModel对象,CreateModel是一个抽象方法,它是在AttributeMappingSource和XmlMappingSource里实现的。大家还可以看到MetaModel是个抽象类,它有各种实现。
关于源代码中设计模式的旁白
这里使用了什么设计模式呢?模板方法?好像有,工厂方法?好像也有。
MappingSource里有两个方法,GetModel方法是一个模板方法,定义了获得MetaModel对象的流程,实际上是调用抽象的CreateModel方法来实现的,CreateModel的实现在子类里。
再看下面这个图:
CreateModel是一个工厂方法,他们推迟到子类实现,在子类创建不同的产品,AttributeMappingSource里的CreateModel创建AttributeMetaModel类,XmlMappingSource里的CreateModel创建MappedMetaModel类。AttributeMetaModel和MappedMetaModel都继承自MetaModel类。
继续赏析我们的源代码
Type providerType = model.ProviderType;
Linq to SQL本是可以支持多种数据库的,对于这种场景当然是使用Provider Pattern了,这里的providerType就是数据库提供者,从model那里获取,我们就来看看AttributeMetaModel的构造函数里如何得到这个ProviderType:
ProviderAttribute[] customAttributes = (ProviderAttribute[])this.contextType.GetCustomAttributes(typeof(ProviderAttribute), true);
if ((customAttributes != null) && (customAttributes.Length == 1))
{
this.providerType = customAttributes[0].Type;
}
else
{
this.providerType = typeof(SqlProvider);
}
从DataContext类上Attribute,看看是否加了ProviderAttribute这个特性,该特性就是指定数据库提供者的,如果没有指定就默认的使用SqlProvider。我看到这个代码很兴奋,微软太开放了,扩展性这么好,呵呵。继续看Init中的代码:
//判断你指定的数据库提供者是否实现了IProvider接口,但是
//微软却将IProvider设为internal的
if (!typeof(IProvider).IsAssignableFrom(providerType))
{
throw Error.ProviderDoesNotImplementRequiredInterface(providerType, typeof(IProvider));
}
//使用反射实例化一个提供者对象
this.provider = (IProvider)Activator.CreateInstance(providerType);
//初始化提供者
this.provider.Initialize(this.services, connection);
你指定的Provider必须实现了IProvider接口,但是,但是,可恶的微软居然把IProvider设为internal的,我气愤啊,我无助啊。
Init最后一句就是初始化数据库中的表了,不过这个方法只在强类型的DataContext中才有用,如果直接使用微软提供的DataContext是不会起作用的。
不知道微软上次发布的.NET Framework的源代码中有没有Linq的,反正我没找到,所以我用Reflctor还原了代码,然后自己建立了工程,这样你就可以借助VS快速的浏览代码了,不过不能编译。
我会从前到后把Linq to SQL的源代码都说一遍,这个过程是迭代进行的,每篇文章我会附带有文章涉及的代码,并加了我自己的注释。