最近在忙公司的一个项目,在实践的过程中遇到了一系列问题。归根结底,还是自己理论知识不足,没实际做过项目,缺乏项目经验,不知如何把理论运用到实践中。现把项目开发过程中自己遇到的一些问题简单列举如下。
1. 缓存
使用两种基本的缓存机制来提供缓存功能。第一种机制是应用程序缓存,它允许您缓存所生成的数据,如或自定义报表业务对象。第二种机制是页输出缓存,它保存页处理输出,并在用户再次请求该页时,重用所保存的输出,而不是再次处理该页。
1.1 Asp.Net支持的几种缓存类型
1.1.1 页面缓存
使用页面输出缓存就是指内存中缓存asp.net页面的内容,这样每次需要这些内容都无需重新生成,取而代之的是从内存中直接读取,这样节省了页面控件生成这些内容的时间,从而大大地提高了应用程序的性能。如果客户访问的这些页面的内容不经常变化,这些页面的访问量较大,那么就非常适宜使用页面输出缓存。页面缓存又具体的分为全局页面缓存以及页面片断缓存。
1.1.2 全局页面缓存
全局页面缓存是指将整个页面的内容都缓存在内存中供客户端调用
举例 下面的页面就可以使用全局页面缓存
http://localhost/mypage.aspx?categoryId=me
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default"%>
<span style="color:#ff0000;"><%@ OutputCache Duration="10" VaryByParam="categoryid" %></span>
<!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 runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<%Response.Write(System.DateTime.Now);%>
</div>
</form>
</body>
</html>
上面的代码最重要的就只有一行代码:
<span style="color:#ff0000;"><%@ OutputCache Duration="10" VaryByParam="categoryid" %></span>
通过指令设置页面使用缓存机制,设置了页面的失效时间为10秒,当categoryid变化的时候根据该参数重新生成页面。
运行程序,输出内容为3/14/2015 3:24:38 PM。
如果10秒以内不改变categoryId的值,再次刷新页面,则输出内容依然是:3/14/2015 3:24:38 PM 。
如果10秒以内改变categoryId的值,再次刷新页面,则输出内容改变为当前时间:3/14/2015 3:24:48 PM
1.1.3 页面片断缓存
页面片断缓存是指在内存中缓存部分页面的内容,而其他的部分是动态重新生成的。
页面部分缓存的实现包括两种方式:控件缓存和替换缓存。前者也可称为片段缓存,这种方式允许将需要缓存的信息包含在一个用户控件内,然后,将该用户控件标记为可缓存的,以此来缓存页面输出的部分内容。这一方式缓存了页面中的特定内容,而没有缓存整个页面,因此,每次都需重新创建整个页。例如,如果要创建一个显示大量动态内容(如股票信息)的页,其中有些部分为静态内容(如每周总结),这时可以将静态部分放在用户控件中,并允许缓存这些内容。缓存后替换与控件缓存正好相反。这种方式缓存整个页,但页中的各段都是动态的。例如,如果要创建一个在规定时间段内为静态的页,则可以将整个页设置为进行缓存。如果向页添一个显示用户名的Label控件,则对于每次页刷新和每个用户而言,Label的内容都将保持不变,始终显示缓存该页之前请求该页的用户的姓名。
设置页面片段缓存可以采用以下两种方式:
方式1.
<%@ Control Language="C#" ClassName="WebUserControl" %>
<%@ OutputCache Duration="10" VaryByParam="*"%>
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
this.Label1.Text = System.DateTime.Now.ToString("hh:mm:ss");//控件缓存
this.timer.Style.Add("width", (DateTime.Now.Second * 4).ToString() + "px");//替换后缓存
}
</script>
<div id = "timer" runat="server" style="background-color:Cyan;">
<asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>
</div>
方式2.
[PartialCaching(3)]
public partial class WebUserControl3 : System.Web.UI.UserControl
{
protected void Page_Load(object sender, EventArgs e)
{
this.Label1.Text = System.DateTime.Now.ToString("hh:mm:ss");//控件缓存
this.timer.Style.Add("width", (DateTime.Now.Second * 4).ToString() + "px");//替换后缓存
}
}
1.2 应用程序数据缓存
Asp.Net提供了一个功能完整的缓存引擎,页面可使用该引擎通过HTTP请求存储和检索任意对象。ASP.NET缓存对于每个应用程序是私有的并且将对象存储在内存中。缓存的生存期与应用程序的生存期相同,也就是说,当应用程序重新启动时,将重新创建缓存。缓存提供了简单的词典接口,使程序员可以轻松地将对象放到缓存中以及从缓存中检索对象。在最简单的情况下,将某项放到缓存中就像向词典中添加一个词条一样。
示例:建立一个缓存xml文件的应用程序
1 建立default.aspx页面
<%@ Page Trace="true" TraceMode="SortByCategory" Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<html xmlns="http://www.w3.org/1999/xhtml" >
<body>
<form id="form1" runat="server">
<div>
<asp:Button ID="Button1" runat="server" Text="Flush Cache" OnClick="Button1_Click" />
<asp:DataGrid ID="dg1" runat="server"/>
</div>
</form>
</body>
</html>
<?xml version="1.0" encoding="utf-8" ?>
<people>
<person first="Scott" last="stafield"></person>
<person first="jim" last="Green"></person>
<person first="kate" last="Green"></person>
</people>
3 添加后台代码
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
DataSet ds = null;
ds = (DataSet)Cache["names"];
if (ds == null)
{
string path = @"c:\names.xml";
ds = new DataSet();
ds.ReadXml(path);
CacheDependency cd = new CacheDependency(path);//缓存依赖项
Cache.Insert("names", ds, cd);
}
Else
{
Response.Write("Names read from cache");
}
dg1.DataSource = ds;
dg1.DataBind();
}
}
protected void Button1_Click(object sender, EventArgs e)
{
Cache.Remove("names");
Response.Redirect("default.aspx");
}
}
第一次运行运行程序时,DataGrid里的数据是从文件中读取的。以后每次刷新,文件都是从缓存中读取的。点击按钮会清空names缓存,下次仍需从文件中读取。
1.3 缓存的命中率
一个缓存实体在被丢到缓存中后,在这个实体被缓存的期间(这个实体被缓存的生命周期内),如果外部一次都没有使用过它,这个缓存实体的命中率就是0。这个实体被请求的次数越多,它的缓存命中率越高。
提高缓存命中率的一些常见方法
1.小型网站可以全部数据缓存,一般压力也不会很大,可以忽略缓存命中率问题。
2.大型服务无法全部数据缓存,只能部分数据缓存,这时候就需要架构师设计出对该业务逻辑适用的缓存方法,尽可能的提高缓存的命中率。提高命中率的方法大多是跟业务逻辑捆绑的,需要跟具体问题具体分析。
3.对于不能被内存缓存的数据,最简单的提高性能方法就是使用文件缓存。
4.文件缓存可以整个内容缓存成一个静态文件;也可以是整个页面的一个区域被缓存成一个文件,然后被包含;也可以是把一个实体序列化成文件进行缓存。
1.4 缓存依赖
CacheDependency类监视依附性关系,以便在任何这些对象更改时,该缓存项自动移除。
1.4.1 文件依赖
如果向缓存添加一个依赖于另一个对象(如文件或文件数组)的项,则在该对象更改时会自动从缓存中移除该依赖项。例如,假设您基于XML文件中的数据创建一个DataSet对象。可以利用使DataSet依赖于XML文件的CacheDependency对象将该DataSet添加到缓存中。如果该XML文件发生更改,则DataSet从缓存中移除
1.4.2 数据库表依赖
SqlCacheDependency类在所有受支持的SQL Server版本(7.0, 2000, 2005)上监视特定的SQL Server数据库表,以便在该表发生更改时,自动从Cache中删除与该表关联的项数据库表发生更改时,将自动删除缓存项,并向Cache 中添加新版本的项。
2. 存储过程
之前做东西习惯于把一些操作分开写。举一个简单的例子,当用户提交订单时,把用户点击付款操作时分为了即个函数来写:修改用户账户信息、插入一条流水记录、修改商品库存。这三个函数只是简单举例说明,实际中可能不止。如果按我之前的想法就会产生这样一个问题:用户账户的钱已经扣除,但在之后的操作出现了错误。试想这会出现什么情况,用户的钱被扣除了,东西却没买到。这是无论如何也不应该发生的。而使用存储过程利用事务就可以很好的避免此种情况的发生。当出现了前面的错误时,可以利用事务的回滚操作,重新回到操作之前的状态。下面就了解一下存储过程的相关知识。
2.1 概念
存储过程Procedure是一组为了完成特定功能的SQL语句集合,经编译后存储在数据库中,用户通过指定存储过程的名称并给出参数来执行。存储过程中可以包含逻辑控制语句和数据操纵语句,它可以接受参数、输出参数、返回单个或多个结果集以及返回值。由于存储过程在创建时即在数据库服务器上进行了编译并存储在数据库中,所以存储过程运行要比单个的SQL语句块要快。同时由于在调用时只需用提供存储过程名和必要的参数信息,所以在一定程度上也可以减少网络流量、简单网络负担。
2.2 分类
2.2.1 系统存储过程
系统存储过程是系统创建的存储过程,目的在于能够方便的从系统表中查询信息或完成与更新数据库表相关的管理任务或其他的系统管理任务。系统存储过程主要存储在master数据库中,以“sp”下划线开头的存储过程。尽管这些系统存储过程在master数据库中,但我们在其他数据库还是可以调用系统存储过程。有一些系统存储过程会在创建新的数据库的时候被自动创建在当前数据库中。
2.2.2 自定义存储过程
用户根据自己的业务需求定义的存储过程。在定义的过程中有一点一定要注意:存储过程的名字不要以sp开头,否则可能会造成自建存储过程的执行失败。
2.3 语法
USE [FaFaNiu]
GO
/****** Object: StoredProcedure [dbo].[AddF_WithFunding_Info] Script Date: 03/14/2015 13:50:59 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author: <Author,,Name>
-- Create date: <Create Date,,>
-- Description: <Description,,>
-- =============================================
ALTER PROCEDURE [dbo].[AddF_WithFunding_Info]
-- Add the parameters for the stored procedure here
@Id int, --方案Id
@Mid int, --用户Id
@Aid int, --账户Id
@Pid int, --玩法Id
--用户信息
@NickName nvarchar(30),
@TotalacountMoney decimal(18, 2),
@TotalfreezeMoney decimal(18, 2),
@TotalbackupMoney decimal(18, 2),
@TotalBorrowMoney decimal(18, 2),
@IsFirst bit,
--信息
@BorrowMoney decimal(18, 2),
@RiskMoney decimal(18,2),
@ManageFeeRate decimal(18, 3),
@ManageFeeUnit nvarchar(2),
@WarningLine decimal(18, 2),
@Openline decimal(18, 2),
@InvestPoint nvarchar(30),
@ShippingSpace nvarchar(30),
@Distribution nvarchar(30),
@State int,
@IsLoss bit,
@LossMoney decimal(18, 2),
@StartDate datetime,
@InfoAddDate datetime,
@CompleteDate datetime,
@ResultMoney decimal(18, 2),
@ProfitMoney decimal(18, 2),
@NextDeductDate datetime,
@OrderNum nvarchar(50),
@TradeType int,
@InfoTradeType int,
@OrderType int,
@InMoney decimal(18, 2),
@LeftMoney decimal(18, 2),
@LeftFreezeMoney decimal(18, 2),
@Remark nvarchar(500),
@OrderAddDate datetime,
@LeftAccountMoney decimal(18, 2)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
BEGIN TRANSACTION
-- Insert statements for procedure here
insert into dbo.F_WithFunding_Info
(Mid,
Aid,
Pid,
BorrowMoney,
RiskMoney,
ManageFeeRate,
ManageFeeUnit,
WarningLine,
Openline,
InvestPoint,
ShippingSpace,
Distribution,
State,
IsLoss,
LossMoney,
StartDate,
AddDate,
CompleteDate,
ResultMoney,
ProfitMoney,
NextDeductDate,
TradeType)
values(
@Mid,
@Aid,
@Pid,
@BorrowMoney,
@RiskMoney,
@ManageFeeRate ,
@ManageFeeUnit ,
@WarningLine ,
@Openline ,
@InvestPoint ,
@ShippingSpace,
@Distribution ,
@State,
@IsLoss,
@LossMoney ,
@StartDate ,
@InfoAddDate ,
@CompleteDate ,
@ResultMoney ,
@ProfitMoney ,
@NextDeductDate,
@InfoTradeType)
SELECT @Id=@@IDENTITY
update dbo.F_Member set
AccountMoney=@TotalacountMoney,
FreezeMoney=@TotalfreezeMoney,
BackupMoney=@TotalbackupMoney,
BorrowMoney=@TotalBorrowMoney,
isFirst=@IsFirst
where Id=@Mid
insert into dbo.F_WithFunding_MoneyOrder
(
OrderNum,
TradeType,
OrderType,
InMoney,
LeftMoney,
LeftFreezeMoney,
Remark,
Mid,
WiId,
AddDate,
LeftAccountMoney
)
values(
@OrderNum,
@TradeType,
@OrderType,
@InMoney,
@LeftMoney,
@LeftFreezeMoney,
@Remark,
@Mid,
@Id,
@OrderAddDate,
@LeftAccountMoney
)
END
IF @@ERROR=0
BEGIN
COMMIT TRANSACTION
SELECT @Id
END
ELSE
BEGIN
ROLLBACK TRANSACTION
END
上面存储过程的定义主要分为三个部分:声明存储过程的名字以及参数、编写具体的Insert、Update、Delete操作、事务回滚。具体就不再讲述了。
2.4 优点
1、 存储过程允许标准组件式编程 存储过程创建后可以在程序中被多次调用执行,而不必重新编写该存储过程的SQL语句。而且数据库专业人员可以随时对存储过程进行修改,但对应用程序源代码却毫无影响,从而极大的提高了程序的可移植性。
2、 存储过程能够实现较快的执行速度(存储过程在第一次编译时进行语法检查,编译好的存储过程保存在高速缓存中用于调用,这样执行的速度和效率较高)。如果某一操作包含大量的T-SQL语句代码,分别被多次执行,那么存储过程要比批处理的执行速度快得多。因为存储过程是预编译的,在首次运行一个存储过程时,查询优化器对其进行分析、优化,并给出最终被存在系统表中的存储计划。而批处理的T-SQL语句每次运行都需要预编译和优化,所以速度就要慢一些。
3、 存储过程减轻网络流量对于同一个针对数据库对象的操作,如果这一操作所涉及到的T-SQL语句被组织成一存储过程,那么当在客户机上调用该存储过程时,网络中传递的只是该调用语句,否则将会是多条SQL语句。从而减轻了网络流量,降低了网络负载。
4、 存储过程可被作为一种安全机制来充分利用系统管理员可以对执行的某一个存储过程进行权限限制,从而能够实现对某些数据访问的限制,避免非授权用户对数据的访问,保证数据的安全。有利于保护数据库的完整性和完全性。
参考:https://msdn.microsoft.com/zh-cn/library/hefzy0d3(v=vs.80).aspx