摘要: 读《重构-改善既有代码的设计》过程中简单整理的表格,主要记录了各种坏味道的特征、各种情况的解决方式阅读全文
posted @ 2011-09-16 18:41 karoc 阅读(2000) 评论(3) 编辑
刚写的热乎乎的代码:
代码
/// <summary>
    
/// 单选按钮列
    
/// </summary>
    
/// <remarks>
    
/// 如果没有设置GroupName,则使用GridView的ID作为GroupName
    
/// 如果没有设置DataField,则使用RowIndex作为Value
    
/// 允许设置DataFormatString格式化数据
    
/// </remarks>
    [AspNetHostingPermission(SecurityAction.Demand,
      Level 
= AspNetHostingPermissionLevel.Minimal)]
    
public class RadioButtonSelectField : DataControlField
    {
        
/// <summary>
        
/// 单选按钮组名,默认去GridView的ClientID
        
/// </summary>
        [Description("单选按钮组名,默认去GridView的ClientID")]
        
public string RadioButtonGroupName
        {
            
get
            {
                
return this.ViewState["RadioButtonGroupName"as string;
            }
            
set
            {
                
this.ViewState["RadioButtonGroupName"= value;
            }
        }

        
/// <summary>
        
/// 单选按钮的样式
        
/// </summary>
        [Description("单选按钮的样式")]
        
public string RadioButtonCssClass
        {
            
get
            {
                
return this.ViewState["RadioButtonCssClass"as string;
            }
            
set
            {
                
this.ViewState["RadioButtonCssClass"= value;
            }
        }

        
/// <summary>
        
/// 要绑定的数据表达式
        
/// </summary>
        [Description("要绑定的数据表达式")]
        
public string DataField
        {
            
get
            {
                
return this.ViewState["DataField"as string;
            }
            
set
            {
                
this.ViewState["DataField"= value;
            }
        }

        
/// <summary>
        
/// 要绑定的数据表达式格式
        
/// </summary>
        [Description("要绑定的数据表达式格式")]
        
public string DataFormatString
        {
            
get
            {
                
return this.ViewState["DataFormatString"as string;
            }
            
set
            {
                
this.ViewState["DataFormatString"= value;
            }
        }

        
/// <summary>
        
/// return self;
        
/// </summary>
        
/// <returns></returns>
        protected override DataControlField CreateField()
        {
            
return this;
        }

        
/// <summary>
        
/// 添加控件
        
/// </summary>
        
/// <param name="cell"></param>
        
/// <param name="cellType"></param>
        
/// <param name="rowState"></param>
        
/// <param name="rowIndex"></param>
        public override void InitializeCell(DataControlFieldCell cell, DataControlCellType cellType, DataControlRowState rowState, int rowIndex)
        {
            
base.InitializeCell(cell, cellType, rowState, rowIndex);

            
if (cellType == DataControlCellType.DataCell)
            {
                var literal 
= new Literal();

                
if (string.IsNullOrEmpty(this.DataField))
                {
                    SetLiteralHtml(literal, rowIndex.ToString());
                }
                
else
                {
                    literal.DataBinding 
+= new EventHandler(literal_DataBinding);
                }
                
                cell.Controls.Add(literal);
            }
        }

        
void literal_DataBinding(object sender, EventArgs e)
        {
            
if (string.IsNullOrEmpty(this.DataField))
            {
                
return;
            }

            var literal 
= sender as Literal;
            
if (literal == null)
            {
                
return;
            }
            var cell 
= literal.Parent as TableCell;
            
if (cell == null)
            {
                
return;
            }
            var container 
= literal.NamingContainer;
            
if (container == null)
            {
                
return;
            }
            
bool foundDataItem;
            var dataItem 
= DataBinder.GetDataItem(container, out foundDataItem);
            
if (!foundDataItem)
            {
                
return;
            }

            var dataValue 
= null as string;
            
if (this.DataField.Contains('.'))
            {
                dataValue 
= DataBinder.Eval(dataItem, this.DataField, this.DataFormatString);
            }
            
else
            {
                dataValue 
= DataBinder.GetPropertyValue(dataItem, this.DataField, this.DataFormatString);
            }

            SetLiteralHtml(literal, dataValue);
        }

        
private void SetLiteralHtml(Literal literal, string dataValue)
        {
            var groupName 
= this.RadioButtonGroupName;
            
if (string.IsNullOrEmpty(groupName))
            {
                groupName 
= literal.Parent.Parent.Parent.Parent.ID;
            }
            var cssClass 
= this.RadioButtonCssClass;
            
if (!string.IsNullOrEmpty(cssClass))
            {
                cssClass 
= string.Format("class=\"{0}\""this.RadioButtonCssClass);
            }

            var selected 
= false;
            var selectedValue 
= literal.Page.Request[groupName];
            
if (string.IsNullOrEmpty(selectedValue) == false)
            {
                
if (string.Compare(selectedValue, dataValue, true== 0)
                {
                    selected 
= true;
                }
            }

            var rbHtml 
= string.Format("<input type=\"radio\" name=\"{0}\" value=\"{1}\" {2} {3} />",
                groupName,
                dataValue,
                cssClass,
                selected 
? "checked" : string.Empty);

            literal.Text 
= rbHtml;
        }
    }

 

posted @ 2010-02-25 14:27 karoc 阅读(239) 评论(0) 编辑

译自http://msdn.microsoft.com/en-us/library/bb648778.aspx

在Project的生命周期中,Project 2007使用复杂的计划引擎排定计划,引擎会自动计算一些字段的值,而不是让你手动指定。使用PSI进行开发的开发人员往往为此感到困惑。本篇文章为在Project计划引擎下如何处理任务的开始时间和结束时间的做了一些很好的实践。

TASK_START_DATE和TASK_FINISH_DATE属性都属于上述计算域。在ProjectDataSet对象中修改它们将会抛出运行时异常,表明两个字段是只读的,Project计划引擎将自动计算它们的值,而不允许开发人员通过Project Data Set修改它们。

许多因素可以影响的一个任务的开始时间和完成时间。

  • 为新建任务设定开始和结束时间

为新建任务设定开始和结束时间,并使用PSI保存后,在PWA查看,该任务的开始和结束时间不一定是在程序中设定的时间,而是和项目开始时间相同。这是因为计划引擎在排定日程时,发现没有影响该任务从项目开始时间开始的因素,所以自动修改了任务的开始时间,也重新计算了其完成时间。

  • 当存在任务依赖关系的情况

任务依赖是指在两个任务的开始、完成时间上建立的联系。具有依赖关系的两个任务互称为前置任务和后置任务。使用LINK_TYPE属性可以为任务设定4种类型的依赖关系,分别是FinishFinish(FF)、FinishStart(FS)、StartFinish(SF)和StartStart(SS)。FF指后置任务不能完成直到前置任务完成。FS指后置任务不能开始,直到前置任务完成,这是默认的依赖关系。SF指后置任务不能完成,直到前置任务开始。SS指后置任务不能开始,直到前置任务开始。

当使用编程的方法修改任务之间的依赖关系后,计划引擎将重新排定任务的起至时间。

还可以通过修改依赖关系的LINK_LAG属性,设定间隔时间,从而影响到后置任务的开始时间。

  • 使用任务约束(限制)

可以为任务的开始时间或结束时间设定限制条件,每个任务只能设定一个限制条件。如果对开始时间做了限定,则计划引擎将根据限制条件及其他信息自动计算完成时间,反之亦然。

可以通过任务的TASK_CONTSTAINT_TYPE属性为任务设定8种类型的限制条件,包括AsLateAsPossible(越晚越好)、AsSoonAsPossible(越早越好)、FinishNoEarlierThan(不早于...完成)、FinishNoLaterThan(不晚于...完成)、MustFinishOn(必须完成于...)、MustStartOn(必须开始于...)、StartNoEarlierThan(不早于...开始)、StartNoLaterThan(不晚于...开始)。

  • 理解任务类型、人工驱动(Effort-driven Scheduling)和资源分配

任务日程排定同样被任务类型、人工驱动和资源分配所影响。Project 2007中,有3种任务类型,它们是固定工期、固定工时、固定单位(默认)。Project 2007计算任务的工期、工时和人力时使用以下公式:工期=工时/人力。任务类型决定了公式中的哪个部分是固定的,而其他两个是计算的。使用任务的TASK_TYPE属性为任务设定类型。

默认情况下,Project使用固定单位和人工驱动排定日程,这意味着,当为一个任务增加或减少资源时,完成任务所需的工期将缩短或加长,但是总工时不变。可以通过修改任务属性TASK_IS_EFFORT_DRIVEN,以设定任务是否为人工驱动,也可以通过设置项目的PROJ_OPT_NEW_TASKS_ARE_EFFORT_DRIVEN属性,设定新任务是否为EFFORT_DRIVEN。

需要特别注意的时,当使用PSI修改任务资源时,任务的工期并不会自动重新计算,只有在Project Professional 2007中打开计划并手动重新分配资源时,任务工期才会改变。

另外,修改一个已分配资源的在此任务上的工作时间百分比,也会影响到任务的工期。可以通过修改属性ASSN_UNITS来设定改值。

  • 日历对日程的影响

日历的例外情况同样影响着任务的日程排定,项目日历、任务日历和资源日历都会影响到任务的开始和完成时间。

 

项目日历、任务日历、资源日历都是从标准日历这个模板派生出来的。标准日历定义了企业的标准工作时间和非工作时间。Project 2007自带的标准日历就定义了从周一到周五、每天的8:00到17:00、除去中午午餐时间作为工作时间。

Project 2007默认提供了3个标准日历,但是根据企业实际的配置,你不一定有权限访问这些日历。

 

项目日历定义了一个项目的工作和非工作日期和时间,作为项目下任务的默认日历。Project 2007使用项目日历去排定没有资源分配的任务的日程,或者类型为固定工期的任务的日程。可以通过指定CAL_UID属性的值,为项目指定标准日历。

 

任务默认使用项目日历,但是可以根据需要为其指定自己的日历。标准日历和任务通过TASK_CAL_UID属性建立关系,但是该属性是只读的,不能通过它为任务指定日历,需要在Project Professional 2007中进行操作。

 

资源日历为一个或一组资源定义了工作和非工作时间。可以通过设置TASK_IGNORES_RES_CAL属性让任务忽略资源日历。

 

Project计划引擎非常复杂,可影响任务开始完成时间的因素很多,以上只是常见的一部分。在Project Professional 2007中,如果想要了解任务排定原因,可以打开菜单里的项目->任务驱动因素来查看。

 

posted @ 2010-01-21 14:09 karoc 阅读(762) 评论(0) 编辑
如果数据库字段为varchar或char类型,ASP.NET控件在可输入汉字的情况下,MaxLength属性不能保证在保存到数据库时不发生截断错误,因此写了一个最大长度验证控件,还可用于多行文本框。
代码
/// <summary>
    
/// TextBox的最大长度验证器,如果是varchar,char字段类型,需要此验证
    
/// </summary>
    [Description("对MaxLength进行验证")]
    [ToolboxData(
"<{0}:MaxLengthValidator runat=server></{0}:MaxLengthValidator>")]
    
public class MaxLengthValidator : BaseValidator
    {
        
/// <summary>
        
/// 获取或设置是否为非nvarchar,nchar字段验证
        
/// </summary>
        [DefaultValue(true)]
        
public bool ValidateForNonNationalField
        {
            
get
            {
                var data 
= this.ViewState["ValidateForNonNationalField"];
                
if (data == null)
                {
                    
return true;
                }
                
else
                {
                    
return (bool)data;
                }
            }
            
set
            {
                
this.ViewState["ValidateForNonNationalField"= value;
            }
        }

        
/// <summary>
        
/// 构造函数
        
/// </summary>
        public MaxLengthValidator()
        {
            
this.ValidateForNonNationalField = true;
        }

        
protected override bool EvaluateIsValid()
        {
            var targetControlName 
= this.ControlToValidate;
            
if (targetControlName == null)
            {
                
//没有目标验证控件,验证通过
                return true;
            }

            var targetControl 
= this.NamingContainer.FindControl(targetControlName) as TextBox;
            
if (targetControl == null)
            {
                
//目标控件不是文本框或不存在,验证通过
                return true;
            }

            var maxLength 
= targetControl.MaxLength;
            var targetValue 
= this.GetControlValidationValue(targetControlName);
            var targetLength 
= 0;
            
if (string.IsNullOrEmpty(targetValue))
            {
                targetLength 
= 0;
            }
            
else
            {
                
if (this.ValidateForNonNationalField)
                {
                    targetLength 
= this.GetLengthForNonNationnal(targetValue);
                }
                
else
                {
                    targetLength 
= targetValue.Length;
                }
            }

            
return targetLength <= maxLength;
        }

        
/// <summary>
        
/// 获得保存为Varchar时,text的长度
        
/// </summary>
        
/// <param name="text"></param>
        
/// <returns></returns>
        public virtual int GetLengthForNonNationnal(string text)
        {
            
if (string.IsNullOrEmpty(text))
            {
                
return 0;
            }

            var length 
= 0;
            
for (var i = 0; i < text.ToCharArray().Length; i++)
            {
                var charCode 
= (int)text[i];
                
                length
++;
                
if (charCode > 255)
                {
                    length
++;
                }
            }
            
return length;
        }

        
protected override void AddAttributesToRender(HtmlTextWriter writer)
        {            
            
base.AddAttributesToRender(writer);

            
if (this.RenderUplevel)
            {
                writer.AddAttribute(
"TargetControlID",
                    
this.GetControlRenderID(this.ControlToValidate));
                writer.AddAttribute(
"ValidateForNonNationalField",
                    
this.ValidateForNonNationalField ? "true" : "false");
                writer.AddAttribute(
"evaluationfunction",
                             
"MaxLengthValidator_Validate");

                var targetControl 
= this.NamingContainer.FindControl(this.ControlToValidate) as TextBox;
                
if (targetControl != null)
                {
                    var maxLength 
= targetControl.MaxLength;
                    writer.AddAttribute(
"maxlength",
                        maxLength.ToString());
                }
            }
        }

        
protected override void OnPreRender(EventArgs e)
        {
            var script 
= @"
function MaxLengthValidator_GetNonNationalLength(text) {
    if (!text){
        return 0;
    }
    var length = 0;
    for (var i = 0; i < text.length; i++) {
        var charCode = text.charCodeAt(i);
        length++;
        if (charCode > 255) {
            length++;
        }
    }
    return length;
}
function MaxLengthValidator_Validate(val) {
    if (!val) {
        return true;
    }    
    var targetID = val.TargetControlID;
    if (!targetID) {
        return true;
    }
    var nonNational = val.ValidateForNonNationalField;
    if (!nonNational) {
        return true;
    } 
    var target = document.getElementById(targetID);
    if (!target) {
        return true;
    }
    var maxLength = val.maxlength;
    if (!maxLength || isNaN(maxLength)) {
        return true;
    }
    var targetValue = target.value;
    if (!targetValue) {
        return true;
    }
    var realLength = targetValue.length;
    if (nonNational == 'true')
    {
        realLength = MaxLengthValidator_GetNonNationalLength(targetValue);
    }
    return realLength <= parseInt(maxLength);
}
";
            ScriptManager.RegisterClientScriptBlock(
this,
                
typeof(MaxLengthValidator),
                
"MaxLengthValidator_Validate",
                script,
                
true);
            
            
base.OnPreRender(e);
        }
    }

 

posted @ 2009-12-25 12:32 karoc 阅读(158) 评论(0) 编辑

版本:3.0.30930

问题:弹出的模式层不在页面正中间,而是在灰色背景的最下面,需要滚动页面才能看到(至少IE8下这样)

解决办法:修改AjaxControlToolkit源码,脚本文件是ModalPopupBehavior.debug.js

第129行:

   //this._foregroundElement.style.position = 'fixed';

修改为:
        // position:fixed; does not work in IE in quirks mode, so need to set to absolute
        if (Sys.Browser.agent == Sys.Browser.InternetExplorer && document.compatMode != "CSS1Compat")
            this._foregroundElement.style.position = 'absolute';
        else
            this._foregroundElement.style.position = 'fixed';

完成后,更新ModalPopupBehavior.js文件即可

 

posted @ 2009-12-25 11:08 karoc 阅读(155) 评论(0) 编辑

所有的博文基本都是技术性的,今天来个特别的,标题借用一下“有家客栈”

新近参与了一个项目,客户是一家跨过公司,全球500强,系统功能块不多,但是业务逻辑比较复杂。

我在其中的角色主要是报表的开发,原因是之前用Reporting Service 2000开发过数量不少的报表的经历,事实上现在使用Reporting Service 2005开发,不少新东西还是需要了解的。另一项职责就是顺便帮忙其他团队成员熟悉使用公司的新开发框架。而我来公司也不到一年,从进门到现在,也没做过多少常规的开发,所以对公司的新老框架其实了解不多,所以主要还是靠着对技术的掌握发挥了一些作用。

客户还是很急的,开发一个月左右的时间就要求前期功能上线试用,我加入的时候,项目经理和其他成员刚刚了解完需求,原始需求资料基本是英文写的,幸亏之前也做过外包的项目,了解国外公司的一些惯用手法,所以需求的了解没有想象的那么困难。

开始开发了,项目经理和另外一个成员都是新来公司不久,素质都还不错,但是显然LINQ、公司框架的学习成本还是产生了一些阻碍,项目开始时进展缓慢,幸亏没有使用SVN来做源代码管理,否则学习成本增大,风险就更大了,我是从其他部门借调来的,所以沟通不足也是一个负面的因素。

20个左右的报表,原定2天1个的速度,现在几天就开发完了,前期由于对需求不熟,慢了一些,后期可以借用前期的中间成果,所以速度上也加快了。但是不幸的事还是发生了,由于项目组需求理解的问题,报表的数据逻辑发生了变化,幸亏做的时候有一些封装,所以修改的工作量减少了很多。不过修改还是引起了不少的问题,加上之前没有和用户充分沟通报表的每个细节,而开发时缺少数据,又没有充分验证,所以测试过程中,bug还是不少。 

 目前,我已基本退出该项目组,总体感觉,项目状况虽然可以,但是不足的地方还是很多,关键的几条体会总结如下:

1、需求一定要和最终用户确认过,最好用原型沟通过,否则后期修改影响会很大

2、需求规格说明要尽可能详细,列表要说明每一列的来源、格式

3、编码完的一定要进行验证,能够单元测试就单元测试,再熟练、再自信的程序员也是一样

4、使用新技术和新框架,要考虑项目成员的学习成本,注意控制风险

5、条件允许,尽量使用更好的源码管理系统,提高生产率

6、沟通至上,一定要尽快打破沟通的障碍

7、建立统一的成品质量标准, 并事先公开给项目成员

 

 

posted @ 2009-05-16 00:00 karoc 阅读(103) 评论(2) 编辑
摘要: 在 .net 环境下 使用 svn开发的同志们,大都会用到 ankhSVN和TortoiseSVN两个软件配合工作,但是由于svn最近升级到 1.6了,所以使用 不同版本的 2个工具会出现 不兼容的情况 ,目前 TortoiseSVN已有支持 1.6的正式版,ankhSVN还没有,所以升级的时候需要注意一下,另外,可能还要看svn服务器的版本《完》阅读全文
posted @ 2009-05-15 15:29 karoc 阅读(147) 评论(0) 编辑
摘要: 开发中的一些小经验总结,不断更新中...阅读全文
posted @ 2009-03-31 13:44 karoc 阅读(178) 评论(0) 编辑
摘要: 就是dbml里对象名称后面,英文版的默认会在表名后加s,中文版的默认不加s 设置“Tools->Options->Database Tools->O/R Designer->Pluralization of names”可以影响它 阅读全文
posted @ 2009-03-30 14:40 karoc 阅读(639) 评论(2) 编辑
摘要: AjaxTookit的ModulPopup在DocType设置不好的情况下会显示不正确,找到一篇英文版的解决方案,大概是下载源代码,然后修改里面的脚本后,重新编译后,再引用。阅读全文
posted @ 2009-03-20 11:43 karoc 阅读(156) 评论(1) 编辑