这里为了演示上简单,假设:后台数据库(暂为SqlServer只有用户表User与部门表Department),各表字段相应精简:

User(用户表)
Id 主键
Name 姓名
DeptId 部门编号
其余字段省略......

 

Department(部门表)
Id 主键
Name 名称
Desc 部门描述
其余字段省略......

 

后台数据库:testdb的情况

建立相关的存储过程:

一般我个人也喜欢ORM转换成实体对象(见截图)

(注意:这里增加了DeptTitle属性<部门名称>)

现在就是访问数据库SqlServer类型,封装到SqlserverProvider中。如果将来访问Access数据库,对应访问封装到AccessProvider中。

(Provider这里表示数据访问提供程序)

SqlUserProvier专门实现对SqlServer的表User的操作,AccessUserProvider专门实现对Access的表User的操作,很显然,操作功能都相同(增删改查<CRUD>),因而对不同子类的相同部分抽象出来,形成父类(UserProvider)。

开始着手具体子类实现:SqlUserProvider:UserProvider

(上图数据库连接串错误:单词integrated才是正确的,最后面调试错误后改正。)

我们发现重载的GetUsers方法,大量代码重复,进行方法重构(重复代码重构为方法GetUsersFromReader)!

继续具体实现父类的抽象方法:GetUserById,发现该方法的部分代码与先前的GetUsersFromReader方法中的部分代码又重复了!

发现上图红色部分重复(该图GetUserById方法忘记传递存储过程所需的参数了),再接着方法重构,提炼重复的代码,避免以后改动的多次修改。

接着编写该类后续的方法(增/删/改):(可以打开VS开发环境中的<服务器资源管理器>,连接上对应的数据库后,看存储过程的参数,以免编码遗忘传参)

接着也来看看 类:AccessUserProvider,见下图

上图GetUsers方法中的查询语句没有联合查询,后续会改动。(这里仅仅示范,其似Access是可以类似建立查询表<后台调用类似存储过程方式>)

我们发现UserProvider的两个子类的方法GetUserFromReader和GetUsersFromReader有重复代码(仅仅是方法的参数不同) [想办法抽象出来,放在父类中]

而方法的参数虽然是SqlDataReader与OleDbDataReader,但是查看定义,看到它们有自己的父类:DbDataReader。

public class SqlDataReader : DbDataReader, IDataReader, IDisposable, IDataRecord

public sealed class OleDbDataReader : DbDataReader

改写父类:UserProvider

父类的方法加上修饰符protected,是为了确保只有子类能够访问。

子类便可以直接调用父类的方法了(GetUserFromReader和GetUsersFromReader方法),见截图:

类似的完善SqlDepartmentProvider类和AccessDepartmentProvider类的代码

(父类:DepartmentProvider提供保护方法GetDeparmentFromReader和GetDepartmentsFromReader)

每个具体的子类Provider都重复了属性:ConnString,所以决定建一个父类:DataAccess来存放该属性(UserProvider与DepartProvider都继承自它),实际上DataAccess还可以包含其它的属性和共用方法。

 1 namespace 抽象工厂模式.DAL
2 {
3 public abstract class DataAccess
4 {
5 private string _connString ="";
6
7 public string ConnString
8 { get { return _connString; } }
9 }
10 }


public abstract class UserProvider:DataAccess

public abstract class DepartmentProvider:DataAccess

通常:数据库连接串的内容都是存储在对应的配置文件中,而不硬编码。

桌面应用程序—[app.config],web应用程序---[web.config],这里以app.config示例,数据库连接串先按照SqlServer数据库访问的。

View Code
1 <?xml version="1.0" encoding="utf-8" ?>
2 <configuration>
3 <connectionStrings>
4 <add name="DBConnString"
5 connectionString="SERVER=.\sqlexpress;DATABASE=testdb;INTEGRATED SECURITY=true"/>
6 </connectionStrings>
7 </configuration>

 

一定要手动引用:System.configuration,然后通过ConfigurationManager类来访问连接串。

可以想象,根据数据库的类型不同,实际底层操控的数据提供程序为Sql__Provider或是Access__Provider。

但对于用户调用者(业务逻辑层)只需要操控Provider就可以了。

 

假设我所在城市有两个行政分区(东一区和西二区),有一家“真不错”总店[经营快餐系列的]在这两个区都有连锁店,对外统一电话:1111777。

(设一个总机号码当然方便了,总不至于将来开了10家分店,对外公布10个电话号码,谁能记住啊?)

比如说:我现在饿了,想吃这家提供的“经济型快餐(一素<炒莴苣>一汤<豆腐汤>)”,我只要打电话111177,那边只需要了解我的地址就可以了。(可以想象:知道了我的地址<就能明白所在行政区,然后公司总店去指派所在区的分店来服务>),对于客户我而言:如何指派哪家分店来服务,以及经济型快餐如何制作的,我都不会关心的。我只关心:要好吃,然后要快点(毕竟,饿太久会受不了的。)

回到我们的程序:

  UserProvider好比一个物品蔬菜<莴苣>,DepartmentProvider好比汤菜<豆腐>。Access文件夹[经济型],SqlServer文件夹[商务型] (你会问一个题外问题:有荤菜吗?我的回答是:尽量别吃,如今都是激素喂出来的<现在人们消耗太快了,以前自然方式半年才能长大的动物,如今1个月人工方式就用激素喂成了>。吃多了,身体容易得病)。

只有一个问题:既然BLL(相对于DAL就是客户调用者)只认(UserProvider/DepartmentProvider),又是如何调用实际其作用的子类呢?

这就需要用到设计模式中的<简单工厂模式> (具体选择哪个子类实际上用父类来完成<根据客户配置需求>)

 当然这里的配置文件:数据库连接串和providerType需要匹配好。

父类:UserProvider我们提供静态的Instance,来决定实际的子类(SqlUserProvider或者AccessUserProvider,根据配置文件的ProviderType的value来定)

如果将来出现了OracleUserProvider/DB2UserProvider/MySqlUserProvider/XmlUserProvider,这个蓝色框框仍然需要增加case分支。这就不好了,需要再编码(修改),好的设计方式应该是对扩展开放,对修改封闭。而且这里罗列出了所有的具体子类Provider,其实只需要一个子类Provider,但是其他的子类Provider也被迫出现在一起<大杂烩>(其实子类之间出现了耦合) 所以这种方式不可取,需要解决。

   这里用反射的方式来解决这个问题。

首先约束:ProviderType的赋值需要规范,只能从(Sql/Access/DB2/MySql/Xml)选择一个呢。可以发现:实际的子类名:ProviderType的值+“UserProvider”。

 1 static public UserProvider Instance
2 {
3 get
4 {
5 if (_instance == null)
6 {
7 string providerTypeName=ConfigurationManager.AppSettings["ProviderType"]
8 +"UserProvider";
9 _instance =
10 Activator.CreateInstance(Type.GetType(providerTypeName)) as UserProvider;
11 }
12 return _instance;
13 }
14 }
如果:你的DAL是单独用程序集方式建立的项目(类库),请使用Assembly.Load等方式,这里由于是以文件夹方式组织的(DAL文件夹)<用Activator.CreateInstance可以OK.>

以后客户端(BLL)调用的时候:比如删除用户表的记录,就可以如下调用了:

  UserProvider.Instance.DeleteUser(id)了。//这里BLL已经不知道是由哪个子类(如SqlUserProvider)来实际工作的。

进行一下测试,看是否运行正常!

发现错误:一:数据库连接串需要修改:

二:文件夹SqlServer改成Sql。以前的命名空间对应改动下:

namespace 抽象工厂模式.DAL.Provider.Sql
{
public class SqlDepartmentProvider:DepartmentProvider

………………………………………….

namespace 抽象工厂模式.DAL.Provider.Sql
{
public class SqlUserProvider:UserProvider

………………………………………………………….



三:Type.GetType(需要完整的限定名)

测试通过:但发现没有DeptTitle数据,查找错误发现

public User(int id, string name, int deptId, string deptTitle)
{
this.Id = id;
this.Name = name;
this.DeptId = deptId;
this.DeptTitle = deptTitle; //DeptTitle;
}

 

 

 

 附上:AccessUserProvider的代码如下:

AccessUserProvider代码
 1 using System;
2 using System.Collections.Generic;
3 using System.Text;
4 using System.Data;
5 using System.Data.OleDb;
6 using 抽象工厂模式.DAL;
7 using 抽象工厂模式.DAL.Entity;
8 namespace 抽象工厂模式.DAL.Provider.Access
9 {
10 public class AccessUserProvider:UserProvider
11 {
12
13 public override List<User> GetUsers()
14 {
15 using (OleDbConnection conn = new OleDbConnection(ConnString))
16 {
17 var dbcmd = conn.CreateCommand();
18 dbcmd.CommandText = "GetUsers";
19 dbcmd.CommandType = CommandType.StoredProcedure;
20 conn.Open();
21 return GetUsersFromReader(dbcmd.ExecuteReader());
22 }
23 }
24
25 public override List<User> GetUsers(int deptId)
26 {
27 using (OleDbConnection conn = new OleDbConnection(ConnString))
28 {
29 var dbcmd = conn.CreateCommand();
30 dbcmd.CommandText = "GetUsersByDepartmentId";
31 dbcmd.CommandType = CommandType.StoredProcedure;
32 dbcmd.Parameters.Add("@DeptId", OleDbType.Integer).Value = deptId;
33 conn.Open();
34 return GetUsersFromReader(dbcmd.ExecuteReader());
35 }
36 }
37
38 public override User GetUserById(int id)
39 {
40 using (OleDbConnection conn = new OleDbConnection(ConnString))
41 {
42 var dbcmd = conn.CreateCommand();
43 dbcmd.CommandText = "GetUserById";
44 dbcmd.CommandType = CommandType.StoredProcedure;
45 dbcmd.Parameters.Add("@Id", OleDbType.Integer).Value =id ;
46 conn.Open();
47 return GetUserFromReader(dbcmd.ExecuteReader());
48 }
49 }
50
51 public override bool DeleteUser(int id)
52 {
53 using (OleDbConnection conn = new OleDbConnection(ConnString))
54 {
55 OleDbCommand cmd = new OleDbCommand(
56 "delete from [user] where Id=" + id, conn);
57 conn.Open();
58 return cmd.ExecuteNonQuery() == 1;
59 }
60 }
61
62 public override int InsertUser(User user)
63 {
64 using (OleDbConnection conn = new OleDbConnection(ConnString))
65 {
66 OleDbCommand cmd = new OleDbCommand(
67 @"insert into [user](name,deptId) values(@id,@name);
68 select max(id) from [user] as newid",conn);
69 cmd.Parameters.Add("@id", OleDbType.Integer).Value = user.Id;
70 cmd.Parameters.Add("@name", OleDbType.VarChar).Value = user.Name;
71 conn.Open();
72 return (int)cmd.ExecuteScalar();
73 }
74 }
75
76 public override bool UpdateUser(User user)
77 {
78 using (OleDbConnection conn = new OleDbConnection(ConnString))
79 {
80 OleDbCommand cmd = new OleDbCommand(
81 "update [user] set name=@name,deptId=@deptid where Id=@id" , conn);
82 cmd.Parameters.Add("@name", OleDbType.Integer).Value = user.Name;
83 cmd.Parameters.Add("@deptid", OleDbType.Integer).Value = user.DeptId;
84 cmd.Parameters.Add("@id", OleDbType.Integer).Value = user.Id;
85 conn.Open();
86 return cmd.ExecuteNonQuery() == 1;
87 }
88 }
89 }
90 }

 

后台的testdb.mdb截图:

posted @ 2012-01-10 14:42 net小虫 阅读(2173) 评论(9) 编辑

1.命名参数和可选参数

image

image

您可以创建自己的支持命名参数和可选参数的调用。 请看以下示例:

public void M(int x, int y = 5, int z = 7) { }

在此方法中,为参数 y 和 z 分配了默认值。 对此方法的调用如下所示:

M(1, 2, 3); // M 的普通调用
M(1, 2); // 省略 z,等效于 M(1, 2, 7)
M(1);  // 同时省略 y 和 z,等效于 M(1, 5, 7)
M(1, z: 3); // 通过名称传递 z
M(x: 1, z: 3); // 通过名称同时传递 x 和 z
M(z: 3, x: 1); // 反转实参的顺序

 

 

2.Office示例

功能写入Excel,复制到Word。先看运行效果:

image

a:首先添加对office的word与excel的引用。

image

为了调用方便,使用using关键字。

image

 

 

启动word程序,默认是不可见的。

image

一般我们会新建一份word文档。对应的控制代码:image

这里关键是需要掌握委托的使用方式,lambda方式的匿名委托。

对于Excel特别要注意如果写数据之前,要进行对应的行列定位。(写数据之前,光标先定位一样)。

调用对应单元格的Select()方法。如果不断写数据,可用ActiveCell.Offset方法偏移定位。

具体参考代码:

View Code
 1 using System; 
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using Word = Microsoft.Office.Interop.Word;
6 using Excel = Microsoft.Office.Interop.Excel;
7 namespace office示例
8 {
9 public class Account
10 {
11 public int ID { get; set; }
12 public string Name { get; set; }
13 public double Balance { get; set; }
14 }
15 class Program
16 {
17 static void Main(string[] args)
18 {
19 var getAccounts = new List<Account> {
20 new Account{ ID=123, Name="王芬芬", Balance=2900},
21 new Account{ID=356,Name ="泉清",Balance =-1800}
22 };
23
24 DisplayExcel(getAccounts, (account, cell) => {
25 //自己定义委托的方法主题 (该方法的两个参数 account与cell的类型已经被委托DisplayFunc确定了)
26 cell.Value = account.ID;
27 cell.Offset[0, 1].Value = account.Name;
28 cell.Offset[0, 2].Value = account.Balance;
29
30 //金额若是负数,红色提醒显示
31 if (account.Balance < 0)
32 {
33 cell.Interior.Color = 255;//红色
34 cell.Offset[0, 2].Interior.Color = 255;
35 }
36 });
37
38 //copy到word中
39 var word = new Word.Application();
40 word.Visible = true;//显示
41 word.Documents.Add();//新建文档
42 word.Selection.PasteSpecial();
43 //word.Selection.PasteSpecial(Link: true, DisplayAsIcon: true);
44 }
45 static void DisplayExcel(IEnumerable<Account> accounts,
46 Action<Account, Excel.Range> DisplayFunc /*两个参数的委托*/)
47 {
48 //启动Execl
49 var x1 = new Excel.Application();//启动一个新的Excel程序实例(x1);
50 x1.Workbooks.Add();//新建excel
51 x1.Visible = true;//可以显示看见
52 x1.Cells[1, 1] = "ID"; x1.Cells[1, 2] = "姓名"; x1.Cells[1, 3] = "金额";//完成列标题
53 x1.Cells[2, 1].Select();//定位到第二行
54 //显示每个帐户信息
55 foreach (var ac in accounts)
56 {
57 DisplayFunc(ac, x1.ActiveCell);//执行委托
58 x1.ActiveCell.Offset[1, 0].Select();//定位到下一行,否则会造成覆盖
59 }
60
61 //实现复制
62 x1.Range["A1:C3"].Copy();//Excel显示上看起来有虚线框,为了copy到word中
63 //自动调整列的宽度
64 x1.Columns.AutoFit();
65 }
66 }
67 }

 

posted @ 2012-02-07 11:20 net小虫 阅读(12) 评论(0) 编辑

    后台线程或二级线程可以用于执行长时间运行的任务,不阻碍主UI任务。所需的类位于mscorlib.dll程序集的System.Threading名称空间下。注意创建的Thread对象的Priority属性(优先权)。通常情况下该属性设置为BelowNormal,使后台线程不会在任何程度上影响到主线程的速度。

image

我们希望能指定i的起始值,但是线程委托ts传递的方法Method1只能是无参数无返回类型的方法。image

如何解决呢?我们可以创建一个带有多个属性的类,主线程创建该类的一个实例。

image

再来看看主线程如何调用的?(实例化对象通过构造方法传参)

image

虽然解决了问题,但是使用起来麻烦,因为需要自己定义一个类,以及该类的属性。

framework2.0的版本提供了更简单的方式。ParameterizedThreadStart委托,它指向一个以对象为参数的方法。看代码:

image

 

 

 

 

这种方式最为简单。

    多线程编程最需要慎重对待的一个问题是对共享资源进行同步访问。如果有多个线程同时读写同一个变量而没有锁定或同步这些操作,可能产生一些不可预知的结果和奇怪的行为。

image 

此时,只有一个启动主线程。我们来增加10个线程,同样的读写shareCount共享资源。

image

运行结果:

image

看起来,虽然线程的执行顺序我们无法干预,(比如线程2在线程1的前面),看结果还是没有问题的。我们再改动一下,每个线程(11个)执行的时候阻塞10毫秒。image ,我们来看两次的运行结果:

image

虽然最终结果正确,但是每个线程显示的结果已经不确定,而且每个线程显示的应该是10的倍数。这里已经完全不能保证了。

如何解决呢?在C#中要异步访问这些资源,最简单的方法是通过lock语句。程序一旦进入一个锁定阻塞,就必须在另一个线程进入锁定对同一变量进行操作之前退出。看示例:(为了直观,去掉主线程的显示)

image

image

    但是注意一点,这种方式,即使有另外的线程需要读取shareCount,也会可能被别的线程锁定而阻塞。然而通常情况下应该允许多个线程在同一时间读取同一资源,只有在写操作的时候需要锁定同步。(可以有多个读取操作,但只有唯一的写操作)。可以使用ReaderWriterLock对象来实现这种类型的锁。

 

 

 

 

 

 

 

image

可以看到,写线程锁定就必须释放后才会有下个写线程的锁定。而读线程1_1锁定,读线程2_1仍然可以访问。

image 这次的运行结果:我们发现<读线程1>_1锁定并不影响写线程_1的锁定。同样[写线程]_4锁定时不影响<读线程1>_4锁定。

综合起来:就是读写线程互不影响锁定。读线程之间互不影响。(单独的读线程访问时不受任何其他线程影响,自己是自由的。)

写线程访问时必须确保其他的写线程释放资源后才能锁定资源。

posted @ 2012-01-29 12:01 net小虫 阅读(1220) 评论(1) 编辑

承接上一篇,这里用来实现PollBox.ascx用户控件和页面ArchivedPolls.aspx。

先来看:Admin/ManagePolls.aspx(以后授权administrators角色的用户访问)。

image

下面来实现:PollBox.ascx用户控件,它的功能如下

1.如果检测到用户还未对民意调查进行投票,该控件将显示一列单选按钮及民意调查所包含的答案选项,以及一个投票按钮
2.如果检测到用户已经投票过,那么控件将显示投票结果。显示每个选项的票数百分比,既显示数字,还显示一个彩色的条形图。
  在显示已存档的民意调查时也将显示这些内容。

页面可以根据需要显示多个民意调查的显示:如:

image

用户控件PollBox分为4个部分。第一个表头栏panel[panHeader]。image(包含了一个图片和可配置的文本);可设置该panel是否显示。

第二个Panel[panVote]用来显示选项答案列表及投票按钮。如:

image同样能控制该panel的显示

第三个显示投票后的结果:(由panel[panResults]决定是否显示)

image

第四个是否显示到存档页面的链接。

如果登录的用户已经投票,则显示投票结果panResults;如果没有投票,则显示panVote。

在项目下建立Controls文件夹,在该目录下添加新项-用户控件-PollBox.ascx,设计代码如下:

View Code
 1 <%@ Control Language="C#" AutoEventWireup="true" CodeFile="PollBox.ascx.cs" Inherits="Controls_PollBox" %>
2 <div class="pollbox">
3 <asp:Panel ID="panHeader" runat="server">
4 <div class="sectiointitle">
5 <asp:Image ID="imgArrow" runat="server" ImageUrl="~/images/ArrowR.gif"
6 style="float:left;margin-left:3px;margin-right:3px;" />
7 <asp:Label ID="lblHeader" runat="server"></asp:Label>
8 </div>
9 </asp:Panel>
10 <div class="pollcontent">
11 <asp:Label ID="lblQuestion" runat="server" CssClass="pollquestion"></asp:Label>
12 <asp:Panel ID="panVote" runat="server">
13 <div class="polloptions">
14 <asp:RadioButtonList ID="optlOptions" DataTextField="OptionText" DataValueField="Id" runat="server">
15 </asp:RadioButtonList>
16 <asp:RequiredFieldValidator ID="valRequireOption" runat="server"
17 ErrorMessage="你必须选择一个选项!" ControlToValidate="optlOptions"
18 SetFocusOnError="True" ToolTip="你必须选择一个选项!" ValidationGroup="PollVote">你必须选择一个选项!</asp:RequiredFieldValidator>
19 </div>
20 <asp:Button ID="btnVote" ValidationGroup="PollVote" Text="投票" runat="server"
21 onclick="btnVote_Click" />
22 </asp:Panel>
23 <asp:Panel ID="panResults" runat="server">
24 <div class="polloptions">
25 <asp:Repeater ID="rptOptions" runat="server">
26 <ItemTemplate>
27 <%#Eval("OptionText") %>
28 <small>(<%#Eval("Votes")%>)票-<%#Eval("Percentage","{0:N1}") %></small><br /><div class="pollbar" style='width:<%# Eval("Percentage")%>%'>&nbsp;</div>
29 </ItemTemplate>
30 </asp:Repeater>
31 <br />
32 <b>总票数:<asp:Label ID="lblTotalVotes" runat="server"></asp:Label></b>
33 </div>
34 </asp:Panel>
35 <asp:HyperLink ID="lnkArchive" NavigateUrl="~/ArchivedPolls.aspx" runat="server">归档问卷</asp:HyperLink>
36 </div>
37 </div>
38 <br />


image

对应的后台代码文件:

  自定义属性:

image

除了属性PollId外,其余属性都与服务器控件封装,不需要进行控件状态的保存。这部分内容请参考:控件状态使用

在网站布局中插入PollBox控件,所在页面加入指令:

<%@ Register src="Controls/PollBox.ascx" tagname="PollBox" tagprefix="uc1" %>

image

 

ArchivedPolls.aspx页面:列出所有已存档的民意调查,每行显示一个,当用户单击其中一个时,该行就会展开,显示它的选项和投票结果。

该页面可同时将多个问题展开。如果当前用户是管理员,还会在列表的右侧看到一个删除的命令按钮。

image

image

gvPolls有两列,一列自定义模板字段用来显示问卷文本和封装的PollBox用户控件(包含在一个div中),一列是删除命令按钮!

通过JavaScript函数toggleOptions使得对应的用户控件所在的div显示/隐藏切换。(当然使用jquery可以更好!)

后置代码:

image

再把成员配置系统后台用户管理的功能加上来:

image

这里需要说明一下:

  image

web.config中的<membership>指定了passwordFormat格式为”Encrypted”,需要指明<machineKey>。否则报如下错误:

image

 

代码下载地址:分享代码

posted @ 2012-01-28 15:23 net小虫 阅读(1559) 评论(3) 编辑
摘要: 首先来看Admin文件夹下的ManagePolls.aspx页面:设计视图:这个界面布局非常类似UI(Web版),这里不再详述。只不过,把其中遇到的问题做个小小归纳,或许你也会碰到类似的问题。这个是DAL层的具体数据提供程序SqlPollsProvider,我们要注意的是下滑红线,由于方法跟具体的存储过程对应,其方法参数对应了存储过程的参数名。这里并没有什么问题。(其中GetPolls方法的两个参数对应存储过程的两个bit参数)但是编码到BLL层的时候,我们曾经统一了业务域对象的主键名为Id。并且还把重复的尤其是可能关于审计追踪方面的属性提取到父类BasePoll.cs中。界面中GridVie阅读全文
posted @ 2012-01-27 10:58 net小虫 阅读(1035) 评论(2) 编辑
摘要: 承接上一篇BLL层代码实现继续,我们接着实现缓存的功能。为了减少数据库的交互,我们希望已经查询得到的数据下次需求再显示时,能直接从缓存取出显示,而减少再一次的从数据库获取的过程,从而更好更快地显示到UI,同时也降低了后台数据库系统的负载。这就需要通过缓存实现。经常听到的一句话更好的概括了这点。(以空间换时间)。缓存仅仅是针对查询的数据,如果查询的内容没有变动,用缓存是最佳的。但是,后台的数据一旦真正改动(如增/删/改)如果显示缓存数据,就会存在数据过期的问题。 以往的做法就是对访问频率高的数据进行缓存,并设置一个合理的缓存有效时间,过了这段时间,缓存的数据将失效,重新从后台获取并进入下一拨的.阅读全文
posted @ 2012-01-26 09:47 net小虫 阅读(1260) 评论(1) 编辑
摘要: 接着数据访问层的再次重构,我们实现对应的BLL层编码,其实关于这部分的内容,可以参考BLL层编码实现,这里仅仅简单通过代码而演示。构建民意调查模块的业务域对象(Poll和PollOption)。上图的代码实际上只需从简单实体类PollDetail和PollOptionDetail类中复制过来,改一下类名称即可。从这里可以看到至少业务域对象已经涵盖了简单实体对象的所有。但是还需要增加简单实体对象没有的方法和关联父子对象引用的属性。我们接着继续完善业务域对象。值得一提的是:就上图而言,我们发现属性Id/AddedBy/AddedDate是共有的,Id是主键,Addedby(记录添加者),Adde.阅读全文
posted @ 2012-01-22 12:23 net小虫 阅读(1404) 评论(2) 编辑
摘要: 接着数据访问层DAL的再次重构_2_模块的自定义设置节我们继续实现,到这里,开始后台编码,经历了建立数据库、建表、存储过程、web.config的自定义配置节点后,我们来实现数据访问层的编码。首先:用OOP方式来映射后台的表Polls和PollOptions。分别命名为PollDetail和PollOptionDetail简单实体类。注意一些转换映射的事项:简单实体就是对表的列进行封装。映射为对应的属性时注意赋予初始值。 (如日期字段的复制根据需要选择 DateTime.Now 和 DateTime.MinValue、DateTime.MaxValue)构造方法需要重载(添加带参数的构造方法,阅读全文
posted @ 2012-01-21 17:33 net小虫 阅读(1378) 评论(0) 编辑
摘要: 接着数据访问层DAL的再次重构_1_建库表存储过程,数据库的连接串随着将来移植的问题而变化,所以在web.config中我们使用<connectionStrings>节点来保存该设置,为了以后便于维护这方面。上一篇已经说明了(观察aspnetdb.mdf得到的启发),对于网站而言,提升网站的性能那么缓存将是非常重要的功能。数据缓存自然是不可或缺的。三层体系中的层次很明白了。我们希望DAL层能够支持缓存、设置缓存有效时间(以秒为单位)、以及对数据库连接串的正确访问。那么这些设置我们使用web.config的自定义配置节点来设置。(可能有人会提议用web.config的<appS阅读全文
posted @ 2012-01-21 11:49 net小虫 阅读(1079) 评论(1) 编辑
摘要: 以前已经写过DAL层编码实现(仅仅为了演示),当时介绍的表Depts和Users很简单,这次实现网站的民意调查模块功能,第一表按照真实项目运作(存储过程实际实现过程),二来DAL的代码继续重构完善,并配合web.config自定义配置节和用户成员配置综合起来,BLL层编码实现没有什么变化,UI层Web版实现主题和模板页,民意调查的投票结果用用户控件封装。总之,该模块演示了完整的流程,综合前面博客的文章所有内容。算是一次整体大演练,希望能讲述的清楚。如果涉及的知识点在以前随笔中有了,这里会简单说明,而不再详细展开。(演练过程中的错误会截图说明,总之确保大家也能一道循序渐进的参与。) 民意调查模.阅读全文
posted @ 2012-01-20 21:46 net小虫 阅读(1215) 评论(2) 编辑
摘要: 接下来实现数据分页的效果,界面UI控件的页面请参考UI层实现web版。EnablePaging:指示Select方法是否支持分页。SelectCountMethod:需要总行数时执行的方法。StartRowIndexParameterName:当EnablePaging为true时,此属性表示Select方法的参数,该参数接受要检索的第一行的索引的值。MaxinumRowsParameterName:当EnablePaging为true时,这表示Select方法的参数,该参数接受检索的行数的值。怎么使用呢?1:首先设置EnablePaging=true,运行页面看看什么情况?2:重载GetDe阅读全文
posted @ 2012-01-17 13:33 net小虫 阅读(844) 评论(2) 编辑
摘要: 不多说,还是直接看最终的效果:(没有选中记录的初始效果)选中的效果:切换数据库的效果:对应的设计视图:(Default.aspx主要就是:GridView控件+DetailsView控件+ObjectDataSource控件)实现了部门和用户的管理(增/删/改),而后置代码(default.aspx.cs只有七行代码,就实现了!)可以说,三层的开发尤其是BLL业务域对象的封装,使得UI的开发变得简易多了,由于WebUI控件的数据绑定和相互良好的关联,比起UI桌面版开发更显简单。文章的后面再讲解数据分页的实现功能。最后一个<切换数据库ImageButton> 也是仅仅为了演示方便,实阅读全文
posted @ 2012-01-16 17:55 net小虫 阅读(1889) 评论(6) 编辑
摘要: 来到UI层编写了,由于三层的架构体系,所以UI层只需要和BLL层沟通就好。(BLL层和DAL层交互,DAL层与底层数据库交互),关于DAL层的编码和BLL层编码实现过程,请参考前面的文章。先来看看最终效果:(这里只能抛砖引玉,小虫我在界面美观上没有仔细用功,这一点大家千万不能也如此不重视,好的编程人员在UI上也要起码的让客户看上去舒服,这一点我做得很不够。所以后面的界面效果望大家能见谅!)数据库已经切换了(目前只有Access数据库和SqlServer数据库)实际上就是实现了用户的增删改和按部门查询的功能,加上了一个鸡肋(数据库切换)(对app.config的配置更新还是有了解的必要)。<阅读全文
posted @ 2012-01-14 13:53 net小虫 阅读(2332) 评论(8) 编辑
摘要: 业务类使用DAL类提供对数据的访问,并加强验证规则,约束检查,并且提供数据的面向对象的表达方式和用于处理数据的方法。这样,BLL作为了一个映射层,它使得底层关系数据库中的数据在用户界面的代码中以对象的方式出现。承接上文:DAL层代码实现先来回顾:DAL中的实体类对象:User类和Department类 DAL中的实体类: 它们只是将从数据库中取得的数据象征性地进行封装,与数据库的表(或者视图)是一一对应的关系,没有对数据进行插入、更新、删除和检索的方法。建立BLL文件夹,里面存放的是业务域对象。下图是域对象DepartmentObj的类图。这种类要复杂些,不但要封装数据,还要有对其父对象或子.阅读全文
posted @ 2012-01-11 13:01 net小虫 阅读(1700) 评论(0) 编辑
摘要: 这里为了演示上简单,假设:后台数据库(暂为SqlServer只有用户表User与部门表Department),各表字段相应精简:User(用户表)Id主键Name姓名DeptId部门编号其余字段省略......Department(部门表)Id主键Name名称Desc部门描述其余字段省略......后台数据库:testdb的情况建立相关的存储过程:一般我个人也喜欢ORM转换成实体对象(见截图)(注意:这里增加了DeptTitle属性<部门名称>)现在就是访问数据库SqlServer类型,封装到SqlserverProvider中。如果将来访问Access数据库,对应访问封装到Acc阅读全文
posted @ 2012-01-10 14:42 net小虫 阅读(2173) 评论(9) 编辑
摘要: 以后博客写模式相关内容,多少是有些东施效颦的,因为这方面的资料实在有很多牛人写的太好了。不过,我还是决定写写自己的理解。首先,在这里,做个广告,强烈推荐:《大话设计模式》看过之后,幽默趣味性强,通俗易懂,学起来事半功倍。如果没看过,很值得一看。初学的话,先看附录A。以后谈及模式,会常引用该书内容。阅读全文
posted @ 2012-01-08 12:52 net小虫 阅读(95) 评论(2) 编辑
摘要: 需要在页面上使用文本在线编辑器,找到了kindeditor这款,不多说,按照提供的示例使用。先来看对应的aspx页面对应的部分,代码截图如下:用的DetailsView控件,采用了母版页。先引入脚本文件:<script src="../kindeditor/kindeditor-min.js" type="text/javascript"></script><script type="text/javascript"> KE.show({id:'txtBody'}); </sc阅读全文
posted @ 2012-01-08 10:47 net小虫 阅读(41) 评论(0) 编辑
摘要: 承继上一篇博客:现在添加Controls.skin文件(可以把所有的服务器控件的样式放在此文件中)注意:用于SiteMapPath控件的skin就无法轻松地用CSS样式来代替了,该控件映射了不止一个HTML元素。创建一个实例Default.aspx页面选择Template.master模板页面,在中间的ContentPlaceHolder中放入一些内容:注意:@Page指令添加Theme属性为TemplateMaster,并且masterPageFile指定Template.master。也可以在web.config中操作一次从而实现能对所有页面实现上述效果,如下设置:<?xml ver阅读全文
posted @ 2012-01-02 19:28 net小虫 阅读(31) 评论(0) 编辑
摘要: 建立站点地图:web.sitemap创建模板页面:Template.master下面的代码为Template.master文件定义了标准的HTML元标记和页头。页头:放一些DIV容器,一个用于菜单链接,一个用于登录框,另一个用于选择主题(一个包含了可选主题的下拉框)。这些DIV使用绝对位置,所以可以直接把它们放在想要的位置上。为了同步看到样式效果,创建第一个主题:TmeplateMonster。该主题下创建Default.css。(当前主题文件夹中所有的CSS文件在运行时自动被.aspx页面链接。),该主题的样式需要的图片都存放在images文件夹中。在default.css编写样式:可见:背阅读全文
posted @ 2012-01-02 19:03 net小虫 阅读(34) 评论(0) 编辑
摘要: 管理部分的页面放在文件夹admin中,为保证不被非法访问,在该目录下添加一个web.config文件,其代码如下:View Code 1 <?xml version="1.0"?>2 <configuration>3 <system.web>4 <authorization>5 <allow roles="Administrators"/>6 <deny users="*"/>7 </authorization>8 </system.web>阅读全文
posted @ 2011-12-28 20:36 net小虫 阅读(165) 评论(1) 编辑
摘要: 1、准在母版页上加入导航菜单功能,数据源来自站点地图。web.sitemap内容大致如下:<?xml version="1.0" encoding="utf-8" ?><siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0" enableLocalization ="true" > <siteMapNode title="主页" url="~/Default.aspx&quo阅读全文
posted @ 2011-12-28 11:45 net小虫 阅读(36) 评论(0) 编辑