LLBL Gen 3.x 源代码追踪与解析 Type Converter 类型转换器
TypeConverter 类,MSDN中的解释是提供一种将值的类型转换为其他类型以及访问标准值和子属性的统一方法。
先看一下例子,能够将字符串翻译成点结构的类型转换器
public class PointConverter : TypeConverter {
public override bool CanConvertFrom(ITypeDescriptorContext context,Type sourceType) {
if (sourceType == typeof(string)) { return true; }
return base.CanConvertFrom(context, sourceType);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) {
if (value is string) {
string[] v = ((string)value).Split(new char[] {','});
return new Point(int.Parse(v[0]), int.Parse(v[1]));
}
return base.ConvertFrom(context, culture, value);
}
public override object ConvertTo(ITypeDescriptorContext context,
CultureInfo culture, object value, Type destinationType) {
if (destinationType == typeof(string)) { return ((Point)value).X + "," + ((Point)value).Y; }
return base.ConvertTo(context, culture, value, destinationType);
}
这个转换器可以实现字符串1,2到类型Point之间的转换。
举例,将对象转换为字符串
Point pt =new Point(1,2);
string point=TypeDescriptor.GetConverter(pt).ConvertToString(pt);
反过来,从字符串转换为对象
Point pt = (Point)TypeDescriptor.GetConverter(typeof(Point)).ConvertFromString("1,2");
LLBL Gen中利用这个特性,将bit类型的1或0翻译为bool值true/false,将字符串的Y或N也翻译成bool值true/false
这样增强了编译时的类型检查,大大降低了出错的机会。
LLBL Gen 2.6中应用typeconverter。如下图所示,Suspended应用了System.Boolean的类型转换
Use different .NET type中列出了当前的应用类型。如果不需要应用类型转换,点击Reset to default。
LLBL Gen 3.1中应用typeconverter时,如下图所示
比如要给EmployeeStatus字符串类型应用typeconverter,用1表示在职,0表示已经离开公司,先设置它的.NET类型为boolean类型,然后到Field mappings中设置它的TypeConverter to use即可。
F7,生成项目源代码,通过查看DatabaseSpecific项目中的PersistenceInfoProviderCore类型,
看一下private void InitUserEntityMappings()的源代码
base.AddElementFieldMapping( "UserEntity", "Suspended", "Suspended", true, (int)SqlDbType.NVarChar, 1, 0, 0, false, "", new Paradox.TypeConverters.BooleanStringConverter(), typeof(System.String), 2 );
倒数第三个参数,应用了BooleanStringConverter类型的转换器。
再来追踪一下,在ORM框架中,1/0是如何被转化为true/false的。
进入到Employee的属性EmployeeStatus
public virtual System.Boolean EmployeeStatus
{
get { return (System.Int32)GetValue((int)EmployeeFieldIndex.EmployeeStatusId, true); }
set { SetValue((int)EmployeeFieldIndex.EmployeeStatusId, value); }
}
进入EntityBase2的SetValue方法
protected bool SetValue(int fieldIndex, object value)
{
return SetValue(fieldIndex, value, true);
}
进入overload方法protected bool SetValue(int fieldIndex, object value, bool performDesyncForFKFields)
从这个方法,仍然没有找出如何应用typeConverter的。
再看一下EmployeeStatus的定义,在数据库中定义为bit,而在程序语言这里已经定义为bool,唯一发生转换的场景就是SELECT之后和INSERT/UPDATE之前,依照这个思路,到DynamicQueryEngine中寻找依据。
来看它的SELECT方法的实现
protected override void CreateSingleTargetInsertDQ(IEntityFieldCore[] fields, IFieldPersistenceInfo[] fieldsPersistenceInfo,
{
if(persistenceInfo.IsIdentity)
{
newParameter = this.Creator.CreateParameter(field, persistenceInfo, _outputParameterDirection);
query.AddParameterFieldRelation(field, newParameter, persistenceInfo.TypeConverterToUse);
看到从映射文件中读取的TypeConverterToUse,这似乎可以找到一些线索。右键点击AddParameterFieldRelation方法,选取菜单View Call Hierarchy
从Call Hierarchy窗口中,展开Implementations,可找到这个接口方法的实现类型,BatchActionQuery和Query.
进入BatchActionQuery中,方法定义为空
public IParameterFieldRelation AddParameterFieldRelation(IEntityFieldCore field, DbParameter parameter, TypeConverter typeConverterToUse)
{
return null;
}
进入Query中的方法定义,看到在这里构造了一个ParameterFieldRelation对象,心中一点小小喜悦。
public IParameterFieldRelation AddParameterFieldRelation(IEntityFieldCore field, DbParameter parameter, TypeConverter typeConverterToUse)
{
IParameterFieldRelation relation = new ParameterFieldRelation(field, parameter, typeConverterToUse);
if(_parameterFieldRelations==null)
{
_parameterFieldRelations = new List<IParameterFieldRelation>();
}
if(!_parameterFieldRelations.Contains(relation))
{
_parameterFieldRelations.Add(relation);
}
return relation;
}
再看一下ParameterFieldRelation定义的注释信息,它的作用是定义query参数与field的关系。
Class to define the relation between a parameter of a query and a field. This relation is used to find back a related EntityFieldCore instance when an Output Parameter is found in a query so the value
of the Output Parameter can be assigned to the related EntityField
这个类型定义有三个成员变量
private readonly IEntityFieldCore _field;
private readonly DbParameter _parameter;
private readonly TypeConverter _typeConverterToUse;
有一个方法名叫Sync,方法体如下
public void Sync()
{
if((_field!=null)&&(_parameter!=null))
{
object value = _parameter.Value;
#if !CF
if(_typeConverterToUse!=null)
{
value = _typeConverterToUse.ConvertFrom(null, null, value);
}
#endif
_field.ForcedCurrentValueWrite(value);
}
}
Compact Framework框架无法使用typeconverter. 如果typeConverterToUse不为空,则转换传入的值,赋给属性
至此,我弄明白了typeconverter的使用方法,但是还不知道它被调用的时机。在方法名Sync上点击右键,Find All References,以查找所有的引用。
在Query.cs文件中,它有被使用到,方法如下
public void ReflectOutputValuesInRelatedFields()
{
foreach(IParameterFieldRelation relation in _parameterFieldRelations)
{
// reflect value in related field object.
relation.Sync();
继续在方法ReflectOutputValuesInRelatedFields上Find All References,以堆栈以上查找。
进入ActionQuery的Execute方法
public int Execute()
{
int returnValue = this.Command.ExecuteNonQuery();
if(returnValue>0)
{
// reflect new PK field values retrieved, if any, in their related fields.
this.ReflectOutputValuesInRelatedFields();
}
再向上,在Execute方法上Find All References
进入到DataAccessAdapterBase的方法ExecuteActionQuery。
public virtual int ExecuteActionQuery(IActionQuery queryToExecute)
{
TraceHelper.WriteLineIf(TraceHelper.PersistenceExecutionSwitch.TraceInfo, "DataAccessAdapterBase.ExecuteActionQuery", "Method Enter");
try
{
PrepareQueryExecution(queryToExecute, false);
return queryToExecute.Execute();
}
finally
{
CloseConnectionIfPossible();
TraceHelper.WriteLineIf(TraceHelper.PersistenceExecutionSwitch.TraceInfo, "DataAccessAdapterBase.ExecuteActionQuery", "Method Exit");
}
}
把这个片段分析倒过来,就是应用type converter的完整堆栈。