Subsonic,在小型项目应用中,作为数据库访问层相当方便,而很多快速开发中我们一般会使用GridView + ObjectDataSrouce ,基本上是一拖一放就能解决很多问题,当然如果你平时注意收据一些样式或皮肤文件,那么做出来的界面也很不错,总之一句话,"太方便了!".
一般使用SubStage生成代码后,每张表,如名为:Rule的表会生成一个叫RuleController的来,这个来是用来绑定ODS用的,
下面是生成Rule表的Update方法, [DataObjectMethod(DataObjectMethodType.Update, true)] public void Update(int Id,string Name,string ReplaceText,string ReplaceMode,string Pattern,bool? IsRegex,bool? Enabled,string Username) { Rule item = new Rule(); item.MarkOld(); //设置为更新模式,即IsNew=false; IsLoaded=True,这样save时对应的SQL为Update item.IsLoaded = true; //貌似这句跟上面一样 item.Id = Id; item.Name = Name; item.ReplaceText = ReplaceText; item.ReplaceMode = ReplaceMode; item.Pattern = Pattern; item.IsRegex = IsRegex; item.Enabled = Enabled; item.Username = Username; item.Save(UserName); }
可以看到Update方法中的参数列表跟数据库中表的列1,1对应,一般情况下我们不会在gridview中包括全部列,如文章内容列这些不会在gridview中显示,而是放到一个专门的页面上来处理,那么粗看Update代码也许会有这样的困惑:
我在gridview上只双向帮定(bind) Name(参考上面代码),IsRegex 两个列,其他列要么不显示,要么只读(注意Id为主键,页面上的geidview 设置DataKeyNames="ID"),这样每次触发Update时,gridview只把双向帮定的数据跟ID发给ODS,而其他列多为null,那么Update 中的item.Save(UserName)会不会出现把数据库中不需要更新的列设置为null呢?
在使用数据库跟踪后发现没有出现我上面担心问题,subsonic生成的update一般为 update [表名] Set 双向绑定列1=value1,双向绑定列2=value2... Where Id=valueid. 查找项目提供的代码发现,设置属性时(如item.Username=Username)会调用定义在RecordBase.cs中的SetColumnValue(string columnName,object oValue)来进行,代码如下
-------------------------------------
public void SetColumnValue(string columnName, object oValue) { columnSettings = columnSettings ?? new TableSchema.TableColumnSettingCollection();
// add the column to the DirtyColumns // if this instance has already been loaded // and this is a change to existing values if(IsLoaded && !IsNew) { TableSchema.Table schema = GetSchema(); object oldValue = null; string oldValueMsg = "NULL"; string newValueMsg = "NULL"; bool areEqualOrBothNull = false;
try { oldValue = columnSettings.GetValue(columnName); } catch {}
if(oldValue == null && oValue == null) areEqualOrBothNull = true; else { if(oldValue != null) { oldValueMsg = oldValue.ToString(); areEqualOrBothNull = oldValue.Equals(oValue); }
if(oValue != null) newValueMsg = oValue.ToString(); }
TableSchema.TableColumn dirtyCol = schema.GetColumn(columnName);
if(dirtyCol != null && !areEqualOrBothNull) { string auditMessage = String.Format("Value changed from {0} to {1}{2}", oldValueMsg, newValueMsg, Environment.NewLine); TableSchema.TableColumn dirtyEntry = DirtyColumns.GetColumn(columnName); if(dirtyEntry != null) { DirtyColumns.Remove(dirtyEntry); auditMessage = String.Concat(dirtyCol.AuditMessage, auditMessage); }
dirtyCol.AuditMessage = auditMessage; DirtyColumns.Add(dirtyCol); } }
columnSettings.SetValue(columnName, oValue); }
------------------------------
从上面代码可以看到,当传入的oValue为null,并且oldValue也为null时,ActiveRecord是不会把这个列加入到DirtyColumns中的,而DirtyColumns中的列则是生成SQL语句Update 中 Set 后具体更新的列. RuleController(参考上面)中的Update中首先Rule item= new Rule() 这样的结果是全部列的oldValue也为null(因为Rule为参构造函数创建类后不会进行读书记库操作,因此columnSettings中的全部列值多为null,这样就保证Save时不会把null列覆盖到原来的列, 另外要注意的是,为了保证能把null专递到 RuleController的Update中,Update的值类型参数多为可空类型(如?int,?bool...) 这样当gridview没传递相应列值时---没双向绑定或不是主键(也有可能是没在gridview中设置)ODS就会根据配置的参数列表传递一个null过去. 另外你可以在ODS的Updateing ,Selecting事件中设置具体的参数值,在gridview + ods + 数据访问类 这样的三层结构中,数据需要层层传递,同时也意味着一个修改或说控制可以安排在这三层的任意一层,前面两层一般使用控件提供的事件而最后一层是自己编写的类.
最后一般注意下ODS的OldValuesParameterFormatString 设置,在使用subsonic时设置为{0}, 下面为MSDN的描述:
OldValuesParameterFormatString 格式字符串只应用于主键,例如,由相关联的数据绑定控件的 DataKeyNames 属性所标识的主键,或者用在以下删除方案和更新方案中:ConflictDetection 属性设置为 CompareAllValues 值,并且一组 oldValues 值传递给了相应的数据方法。这种情况下,此格式字符串应用于 oldValues 集合中的每个参数名。
在以下两种常见方案中,您可能要更改 OldValuesParameterFormatString 属性:
在更新中区别旧值和新值。ConflictDetection 属性设置为 CompareAllValues 值时,原始值和新值的参数都被添加到 UpdateParameters 集合中。如果没有设置字符串的格式,将为每个数据字段创建两个同名参数。通过更改原始值参数的名称,可以将数据与原始数据源进行比较,以检测冲突并比较键值。
某些可视化设计器实现了特定的原始值和键命名方案。