扩展字段主要是针对那些有不固定列的表,而且这些列不是系统运行所必须的。当一个系统或产品已经上线后,不需要修改原来的代码就可以满足客户增加字段的需求。
例如产品表在用户A里需要用到产地这个字段,在用户B里需要条形码这个字段,而这两个或更多的字段在原来的系统设计时并没考虑进去,这时就需要扩展字段。
首先需要建一张扩展字段映射表
View Code
CREATE TABLE [dbo].[ExColumnMapping](
[RowID] [int] IDENTITY(1,1) NOT NULL,
[TableName] [nvarchar](50) NULL,
[ColumnName] [nvarchar](50) NULL,
[MappingName] [nvarchar](50) NULL,
[InputType] [nvarchar](50) NULL,
[Remark] [nvarchar](200) NULL,
[EnableStatus] [int] NULL
CONSTRAINT [PK_ExColumnMapping] PRIMARY KEY CLUSTERED
(
[RowID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
2条存储过程
View Code
create PROCEDURE [dbo].[GetNewMappingName]Create PROCEDURE [dbo].[IsExColExist]
@TableName nvarchar(50)
AS
BEGIN
SELECT top 1 name
FROM syscolumns
WHERE substring(name,1,3)='Col' and
id = (SELECT top 1 id
FROM sysobjects
WHERE name = @TableName order by id)
and name not in
(select MappingName from ExColumnMapping where TableName = @TableName)
END
@TableName nvarchar(50),
@ColumnName nvarchar(50)
AS
BEGIN
select * from ExColumnMapping
where TableName = @TableName
and ColumnName = @ColumnName
END
同时在产品表增加一些备用列 Col1,Col2,Col3.。。。。。。。。
建一个管理扩展字段表的页面:
View Code
<table class="innerform">
<tr>
<th width="20%">
数据表:
</th>
<td>
<select id="sltTableName" runat="server" >
<option value="Customer">客户表</option>
</select>
<asp:Label ID="txtTableName" runat="server" ReadOnly="true" MaxLength="20"></asp:Label>
<asp:Label ID="Label1" runat="server" Text="*" ForeColor="Red"></asp:Label>
</td>
</tr>
<tr>
<th>
字段名:
</th>
<td>
<input id="txtColName" type="text" runat="Server" maxlength="20" />
<asp:Label ID="lbCustName" runat="server" Text="*" ForeColor="Red"></asp:Label>
</td>
</tr>
<tr>
<th>
输入类型:
</th>
<td>
<select id="sltInputType" name="D3" runat="Server">
<option value="文本框">文本框</option>
<option value="下拉框">下拉框</option>
<option value="日历">日历</option>
</select>
</td>
<td>
<input type="button" id="btnAddSourceData" onclick="SetSourceData()" value="源数据" />
</td>
<td>
<input type="hidden" runat="server" id="hdSourceData" value="" />
</td>
</tr>
<tr>
<th>
可见状态:
</th>
<td>
<select id="sltEnable" name="D3" runat="Server">
<option value="1">可见</option>
<option value="2">不可见</option>
</select>
</td>
</tr>
<tr>
<th>
备注:
</th>
<td colspan="3">
<textarea id="taRemark" name="S1" rows="5" runat="Server" maxlength="200" ></textarea>
</td>
</tr>
</table>
在产品管理页面加上<div id="divExCol" style="width:100%" runat = "server"></div>
View Code
/// <summary>
/// 动态生成扩展字段相关控件
/// </summary>
public static void AddExControl(HtmlGenericControl divExCol,List<ExColumnMappingItem> exCols)
{
foreach (ExColumnMappingItem exColItem in exCols)
{
Label lblExCol = new Label();
lblExCol.Width = 130;
lblExCol.Style.Add(HtmlTextWriterStyle.TextAlign, "right");
lblExCol.Text = exColItem.ColumnName + ":";
divExCol.Controls.Add(lblExCol);
switch (exColItem.InputType)
{
case "下拉框":
DropDownList ddlExCol = new DropDownList();
ddlExCol.ID = exColItem.MappingName;
ddlExCol.Width = 300;
ddlExCol.Items.Add("");
foreach (string data in exColItem.SourceData.Split(','))
{
ddlExCol.Items.Add(data);
}
divExCol.Controls.Add(ddlExCol);
break;
case "日历":
TextBox txtDateExCol = new TextBox();
txtDateExCol.ID = exColItem.MappingName;
txtDateExCol.Width = 260;
divExCol.Controls.Add(txtDateExCol);
Button btnExCol = new Button();
btnExCol.Text ="...";
btnExCol.Height = 21;
btnExCol.Width = 40;
btnExCol.OnClientClick = "popUpCalendar(this, " + txtDateExCol.ClientID + ", 'mm/dd/yyyy',-1,-1,true);return false;";
divExCol.Controls.Add(btnExCol);
break;
default:
TextBox txtExCol = new TextBox();
txtExCol.ID = exColItem.MappingName;
txtExCol.Width = 300;
divExCol.Controls.Add(txtExCol);
break;
}
//换行
divExCol.Controls.Add(new LiteralControl("<br>"));
}
divExCol.DataBind();
}
View Code
/// <summary>
/// 将原有数据绑定到扩展字段控件
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="t"></param>
/// <param name="exCols"></param>
/// <param name="divExCol"></param>
public static void BindExColData<T>(T t, List<ExColumnMappingItem> exCols, HtmlGenericControl divExCol)
{
foreach (ExColumnMappingItem exColItem in exCols)
{
object exColValue = t.GetType().GetProperty(exColItem.MappingName).GetValue(t, null);
string oldValue = exColValue == null ? "" : exColValue.ToString();
switch (exColItem.InputType)
{
case "下拉框":
DropDownList ddlExCol = (DropDownList)divExCol.FindControl(exColItem.MappingName);
ddlExCol.Text = oldValue;
break;
default:
TextBox txtExCol = (TextBox)divExCol.FindControl(exColItem.MappingName);
txtExCol.Text = oldValue;
break;
}
}
}
View Code
/// <summary>
/// 从文本框获取扩展字段值
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="t"></param>
/// <param name="exCols"></param>
/// <param name="divExCol"></param>
public static void GetExColData<T>(T t, List<ExColumnMappingItem> exCols, HtmlGenericControl divExCol)
{
foreach (ExColumnMappingItem exColItem in exCols)
{
switch (exColItem.InputType)
{
case "下拉框":
DropDownList ddlExCol = (DropDownList)divExCol.FindControl(exColItem.MappingName);
t.GetType().GetProperty(exColItem.MappingName).SetValue(t, ddlExCol.Text, null);
break;
default:
TextBox txtExCol = (TextBox)divExCol.FindControl(exColItem.MappingName);
t.GetType().GetProperty(exColItem.MappingName).SetValue(t, txtExCol.Text, null);
break;
}
}
}
select into 创建的表属于临时表,判断是否存在的方法
IF OBJECT_ID( 'tempdb..##TEMP_COPTD') IS NOT NULL
Begin
DROP TABLE ##TEMP_COPTD
End
/****** Object: UserDefinedFunction [dbo].[FSysSplit] Script Date: 06/28/2011 09:04:51 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER FUNCTION [dbo].[FSysSplit](@Long_str varchar(8000),@split_str varchar(100))
RETURNS @tmp TABLE(
ID int IDENTITY PRIMARY KEY,
short_str varchar(8000)
)
AS
BEGIN
DECLARE @long_str_Tmp varchar(8000),
@short_str varchar(8000),
@split_str_length int
SET @split_str_length = LEN(@split_str)
IF (CHARINDEX(@split_str,@Long_str)=0 AND @Long_str <> '')
BEGIN
INSERT INTO @tmp (short_str) VALUES (@Long_str)
RETURN
END
IF CHARINDEX(@split_str,@Long_str)=1
SET @long_str_Tmp=SUBSTRING(@Long_str,
@split_str_length+1,
LEN(@Long_str)-@split_str_length)
ELSE
SET @long_str_Tmp=@Long_str
IF CHARINDEX(REVERSE(@split_str),REVERSE(@long_str_Tmp))>1
SET @long_str_Tmp=@long_str_Tmp+@split_str
ELSE
SET @long_str_Tmp=@long_str_Tmp
WHILE CHARINDEX(@split_str,@long_str_Tmp)>0
BEGIN
SET @short_str=SUBSTRING(@long_str_Tmp,1,
CHARINDEX(@split_str,@long_str_Tmp)-1)
DECLARE @long_str_Tmp_LEN INT,@split_str_Position_END int
SET @long_str_Tmp_LEN = LEN(@long_str_Tmp)
SET @split_str_Position_END = LEN(@short_str)+@split_str_length
SET @long_str_Tmp=REVERSE(SUBSTRING(REVERSE(@long_str_Tmp),1,
@long_str_Tmp_LEN-@split_str_Position_END))
IF @short_str<>'' INSERT INTO @tmp SELECT @short_str
END
RETURN
END
需求变更,需求不清
1) 需求变更
需求变更,不可避免
解决:可以通过配置化,好的代码框架,减少调整量
2) 需求不清
非需求变更,事实上客户没有那么多需求变更,往往是没有把握好客户的真正需求导致返工。
解决:明确需求后再开发,不要猜测需求,需求是由实际业务定的,开发时是A就是A,是B就是B,不要猜测,通过沟通明确。
脚本
1) 直接在sqlserver数据库上添加表、字段、存储过程等
没有数据库结构文档,没有脚本管理。
在测试环境测试通过后,在正式环境部署程序后,频繁发生缺少脚本或存储过程错误的错误。
解决:严格遵守脚本管理规范
2) 每个人都随意修改数据库结构,包括字段、类型等。
导致不一致和数据库结构混乱。
解决:涉及数据库结构更改,统一由专人管理
代码逻辑不清晰
开发思路不清晰,要做什么要怎么做都没有清晰,正确与否靠调试。写的代码无法描述出来。
- 逻辑缺陷不周全
考虑的场景少,可能只考虑一种正常的输入或流程,输入非常正常的情况才行否则出错对于异常数据的录入没有做考虑!
解决:要把主线流程,和各个分支都列清楚,考虑周全,逻辑要严密。要周全要逻辑严密一个最好办法就是1234步骤罗列清楚,然后做成代码(存储过程)注释。
- 重复性代码(复制粘贴)
重复性代码太多,开发的时候是做复制粘贴等低级性劳动。
复制代码后,改动不完全,这个情况发生的错误非常高!
复制粘贴导致重复性代码后,后来需要调整规则或修改错误时,修改不完全,有不少地方还没有改,反反复复的出错。
解决:严禁复制粘贴代码,如果存在需要重用,首先考虑是否已经存在相同逻辑的方法,如果没有,则抽取成公共方法。
- 代码没有规范,个性化代码,没有注释
代码太个性化,太随意,且没有注释,时间长了连自己都没有清楚代码的逻辑了,后面维护的时候一调整就出错。
解决:严格遵守代码规范
- 代码结构性差,耦合性强
调整一个地方,涉及一大片,而调整的地方又没有评估影响的地方,导致反反复复的出错。
解决:减少类之间的耦合性。至于设计方法,可以自己了解设计模式等面向对象思想。
- 缺少验证,开发完了没有自己测试
开发完成后或调整完一个缺陷或功能后,认为肯定不会有错,就不测试了或测试路径不够,就发布了,结果就是考虑不周全而导致错误。
- 自己引入一些第三方技术,玩技术。如果技术程序有助于提高开发效率和系统功能,可以一起确定后引入。
系统开发前就已经进行技术选型,其他第三方技术不能随意引入。
- 基础数据和数据操作功能:
没有做严格输入约束和校验,导致基础数据有问题,基础数据有问题,导致整个系统都存在问题,甚至无法跑起来。
- 写脚本的时候,没有事先测试
Sql脚本存在缺陷,甚至会存在误删数据情况(灾难)
- Sql脚本存在性能问题,复杂存储过程没有注释
- 对用户异常操作没有考虑
- 灵活性差,不可通过配置解决
- 程序发布的时候忘了加上或调整配置文件参数
- 没有按分层思想开发。
界面直接出现sql语句等,或界面的展示出现在业务逻辑层,导致重用性差,调整即出问题
1、 设计考虑不周全引起bug,解决办法需要在设计前期准确把握需求,对于不明确的需求要及时与需求人员沟通,务必把需求弄清楚,不要猜测需求。
2、 数据库设计不周,数据库设计不周全引起的bug,例如数据字典前期在其它表中保存的是数据字典里的值而不是ID,后来保存的是ID,导致后期系统出现bug,解决办法是先把数据库设计好,规范数据库,尽可能少变动数据库关系,对于需要修改数据库字段或关系的应预先考虑修改后可能引发的问题。
3、 权限部分引起的bug,权限体系部分关系比较复杂,开发人员由于不太清楚权限体系部分架构应用,导致在开发或配置时考虑不全引起bug,建议对开发和配置人员做一次权限体系部分架构应用的培训。
4、 参数过长引起的bug,在设计数据表和写存储过程时由于字符定义不够长,而在实际中传递的参数的长度大于数据库定义的长度导致产生Bug,还有就是传递的参数长度大于sql server存储过程的参数长度导致引起bug,对于参数过长问题可以在编写存储过程时考虑周全一点,对于参数长大大于sql server的参数长度可以采用多个参数取代一个参数的方法来减少bug。
管理规范
需求
ü 需求先明确。要做什么,有什么规则,有哪些功能点(粒度小),涉及哪些数据表,操作流、数据流是怎么样的;
如果需求上不清楚,马上沟通
ü 思路先理清楚,1234步骤列清楚再开发,最好作为注释到代码上,事半功倍
架构
模块化、组件化、用户控件化、粒度尽可能的小
重用性高、公共代码、减少重复代码
灵活拼装、可配置、
可扩展性
<%@ Page Language="C#" %>
<%@ Import Namespace="System.Collections.Generic" %>
<%@ Register assembly="Ext.Net" namespace="Ext.Net" tagprefix="ext" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title></title>
<link href="http://www.cnblogs.com/http://www.cnblogs.com/resources/css/examples.css" rel="stylesheet" type="text/css" />
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
SiteMapNode siteNode = SiteMap.RootNode;
Ext.Net.TreeNode root = this.CreateNode(siteNode);
TreePanel1.Root.Add(root);
TreePanel1.ExpandAll();
}
//dynamic node creation
private TreeNodeBase CreateNodeWithOutChildren(SiteMapNode siteMapNode)
{
TreeNodeBase treeNode;
if (siteMapNode.ChildNodes != null && siteMapNode.ChildNodes.Count>0)
{
treeNode = new AsyncTreeNode();
}
else
{
treeNode = new Ext.Net.TreeNode();
treeNode.Leaf = true;
}
if (!string.IsNullOrEmpty(siteMapNode.Url))
{
treeNode.Href = this.Page.ResolveUrl(siteMapNode.Url);
}
treeNode.NodeID = siteMapNode.Key;
treeNode.Text = siteMapNode.Title;
treeNode.Qtip = siteMapNode.Description;
return treeNode;
}
//static node creation with children
private Ext.Net.TreeNode CreateNode(SiteMapNode siteMapNode)
{
Ext.Net.TreeNode treeNode = new Ext.Net.TreeNode();
if (!string.IsNullOrEmpty(siteMapNode.Url))
{
treeNode.Href = this.Page.ResolveUrl(siteMapNode.Url);
}
treeNode.NodeID = siteMapNode.Key;
treeNode.Text = siteMapNode.Title;
treeNode.Qtip = siteMapNode.Description;
SiteMapNodeCollection children = siteMapNode.ChildNodes;
if (children != null && children.Count > 0)
{
foreach (SiteMapNode mapNode in siteMapNode.ChildNodes)
{
treeNode.Nodes.Add(this.CreateNode(mapNode));
}
}
return treeNode;
}
</script>
<script type="text/javascript">
var loadPage = function (tabPanel, node) {
var tab = tabPanel.getItem(node.id);
if (!tab) {
tab = tabPanel.add({
id: node.id,
title: node.text,
closable: true,
autoLoad: {
showMask: true,
url: node.attributes.href,
mode: "iframe",
maskMsg: "Loading " + node.attributes.href + "..."
},
listeners: {
update: {
fn: function (tab, cfg) {
cfg.iframe.setHeight(cfg.iframe.getSize().height);
},
scope: this,
single: true
}
}
});
}
tabPanel.setActiveTab(tab);
}
</script>
</head>
<body>
<form id="Form1" runat="server">
<ext:ResourceManager ID="ResourceManager1" runat="server" />
<ext:Viewport ID="Viewport1" runat="server">
<Items>
<ext:BorderLayout ID="BorderLayout1" runat="server">
<West Collapsible="true"
Split="true"
MinWidth="175"
MaxWidth="400"
MarginsSummary="5 0 5 5"
CMarginsSummary="5 5 5 5">
<ext:TreePanel
ID="TreePanel1"
runat="server"
Width="300"
Title=""
Icon="ChartOrganisation">
<Listeners>
<Click Handler="if (node.attributes.href) { e.stopEvent(); loadPage(#{Pages}, node); }" />
</Listeners>
</ext:TreePanel>
</West>
<Center MarginsSummary="0 0 0 0">
<ext:TabPanel ID="Pages" runat="server" EnableTabScroll="true" />
</Center>
</ext:BorderLayout>
</Items>
</ext:Viewport>
</form>
</body>
</html>
Web.sitemap
<?xml version="1.0" encoding="utf-8"?>
<siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0">
<siteMapNode title="一级菜单">
<siteMapNode title="二级菜单">
<siteMapNode title="页面" description="页面" url="Default.aspx" />
</siteMapNode>
</siteMapNode>
</siteMap>
<Listeners>
<Click Handler="if (node.attributes.href) { e.stopEvent(); loadPage(#{Pages}, node); }" />
</Listeners>
当点击树的节点时,如果当前节点可以导航, 就添加或激活一个tab页,
两个日期控件范围
View Code
<ext:DateField ID="Start_Date" runat="server" Width="250" FieldLabel="有效期" Format="yyyy-MM-dd"
Vtype="daterange">
<Listeners>
<Render Handler="this.endDateField ='#{End_Date}'" />
</Listeners>
</ext:DateField>
<ext:DateField ID="End_Date" runat="server" Width="250" FieldLabel="至" Format="yyyy-MM-dd"
Vtype="daterange">
<Listeners>
<Render Handler="this.endDateField ='#{Start_Date}'" />
</Listeners>
</ext:DateField>
int number = new Random().Next(1, 101);
Console.WriteLine("请输入一个1到100的数字!答案是:" + number.ToString());
int InputNumber = int.Parse(Console.ReadLine());
int min = 1; int max = 100;
while(number != InputNumber)
{
if (InputNumber <min || InputNumber > max)
{
Console.WriteLine(string.Format("输入不正确!请输入{0}到{1}", min.ToString(), max.ToString()));
InputNumber = int.Parse(Console.ReadLine());
}
else if (InputNumber > number)
{
if (InputNumber > max)
{
Console.WriteLine(string.Format("输入不正确!请输入{0}到{1}", min.ToString(), max.ToString()));
InputNumber = int.Parse(Console.ReadLine());
}
else
{
max = InputNumber;
Console.WriteLine(string.Format("请输入{0}到{1}", min.ToString(), max.ToString()));
InputNumber = int.Parse(Console.ReadLine());
}
}
else if (InputNumber < number)
{
if (InputNumber < min)
{
Console.WriteLine(string.Format("输入不正确!请输入{0}到{1}", min.ToString(), max.ToString()));
InputNumber = int.Parse(Console.ReadLine());
}
else
{
min = InputNumber;
Console.WriteLine(string.Format("请输入{0}到{1}", min.ToString(), max.ToString()));
InputNumber = int.Parse(Console.ReadLine());
}
}
else
{
Console.WriteLine("你猜对了");
break;
}
}

