在ASP.NET 2.0中操作數(shù)據(jù)之五十七:在分層架構(gòu)中緩存數(shù)據(jù)
導(dǎo)言:
正如前面章節(jié)所言,緩存ObjectDataSource的數(shù)據(jù)只需要簡單的設(shè)置一些屬性。然而,它是在表現(xiàn)層對數(shù)據(jù)緩存,這就與ASP.NET page頁面緩存策略(caching policies)緊密的耦合(tightly couples)起來。我們對體系機(jī)構(gòu)分層的原因之一便是打破這種耦合。拿業(yè)務(wù)邏輯層為例,將業(yè)務(wù)邏輯從ASP.NET頁面脫離出來;而數(shù)據(jù)訪問層將數(shù)據(jù)訪問的細(xì)節(jié)ASP.NET頁面脫離出來。從某種意義來說,將業(yè)務(wù)邏輯和數(shù)據(jù)訪問細(xì)節(jié)脫離出來是首先,這樣的話使系統(tǒng)更易讀、易維護(hù)、易修改,便于按模塊分工—比如,表現(xiàn)層的開發(fā)者對數(shù)據(jù)庫的細(xì)節(jié)不甚了解也不妨礙其開發(fā)工作。當(dāng)然,將緩存策略從表現(xiàn)層脫離出來也有類似的好處。
本文我們將對層次機(jī)構(gòu)進(jìn)行擴(kuò)充,新添一個(gè)緩存層(Caching Layer,簡稱CL)以實(shí)施緩存策略。該緩存層包括一個(gè)ProductsCL類,該類用類似 GetProducts(), GetProductsByCategoryID(categoryID)等方法來訪問產(chǎn)品信息。調(diào)用這些方法時(shí)先從內(nèi)存檢索數(shù)據(jù),如果內(nèi)存為空則調(diào)用業(yè)務(wù)邏輯層BLL里的ProductsBLL類的相應(yīng)方法,再從數(shù)據(jù)訪問層DAL返回獲取的數(shù)據(jù)。該P(yáng)roductsCL類的方法從業(yè)務(wù)邏輯層BLL獲取數(shù)據(jù)后先對數(shù)據(jù)緩存后再返回。
如圖1所示,緩存層CL位于表現(xiàn)層和業(yè)務(wù)邏輯層。

圖1:在我們的體系結(jié)構(gòu)中緩存層(CL)是單獨(dú)的一層
第一步:創(chuàng)建緩存層的類
在本文,我們創(chuàng)建的緩存層僅僅包含一個(gè)ProductsCL類,它只有幾個(gè)方法。
完整的緩存層還應(yīng)該包含CategoriesCL, EmployeesCL, 和SuppliersCL類。有了業(yè)務(wù)邏輯層BLL和數(shù)據(jù)訪問層DAL,緩存層完全可以當(dāng)成一個(gè)單獨(dú)的類庫工程(Class Library project),不過我們將它作為App_Code文件夾里的一個(gè)類來處理。
為了更好的將緩存層類和DAL類、BLL類區(qū)分開,我們在App_Code文件夾里創(chuàng)建一個(gè)新的子文件夾。在資源管理器里右擊App_Code文件夾,選擇“新文件夾”,命名為CL,在里面添加新類ProductsCL.cs

圖2:添加名為CL的文件夾和名為ProductsCL.cs的類
跟BLL里的ProductsBLL類一樣,ProductsCL類應(yīng)該包含相同的數(shù)據(jù)訪問和修改方法。不過在本文,我們只創(chuàng)建GetProducts()方法(在第3步)和GetProductsByCategoryID(categoryID)方法(在第4步)。你可以在空閑的時(shí)候?qū)roductsCL類進(jìn)行完善,并創(chuàng)建相應(yīng)的CategoriesCL, EmployeesCL和 SuppliersCL類
第二步:對Data Cache進(jìn)行讀和寫
ObjectDataSource的緩存屬性使用ASP.NET data cache來存儲從BLL獲取的數(shù)據(jù)。要訪問data cache,可以從ASP.NET頁面的code-behind classes類或體系結(jié)構(gòu)層(architecture)的類來訪問。要通過ASP.NET頁面的code-behind classes類對data cache進(jìn)行讀寫,可使用如下模式:
// Read from the cache(讀) object value = Cache["key"];
// Add a new item to the cache(寫) Cache["key"] = value; Cache.Insert(key, value); Cache.Insert(key, value, CacheDependency); Cache.Insert(key, value, CacheDependency, DateTime, TimeSpan);
Cache class類的Insert方法可以有很多的重載。Cache["key"] = value 和 Cache.Insert(key, value)是相同的,都是向cache添加一個(gè)條目(item),不過沒有指定expiry(可以理解為緩存持續(xù)時(shí)間)。更具代表性的是,在我們向cache添加條目的時(shí)候指定一個(gè)expiry,它要么是dependency(從屬體),要么是time-based expiry,又或者兩者兼而有之,比如上面的最后2個(gè)表達(dá)式。
如果所需的數(shù)據(jù)存儲在內(nèi)存的話,首先調(diào)用緩存層的方法返回?cái)?shù)據(jù)。如果不在內(nèi)存的話就調(diào)用BLL里相應(yīng)的方法。數(shù)據(jù)先緩存再返回。就像下面的流程表解析的一樣:

圖3:如果數(shù)據(jù)存在于內(nèi)存的話就調(diào)用緩存層的方法。
上圖的流程可用如下的模式:
Type instance = Cache["key"] as Type;
if (instance == null)
{
instance = BllMethodToGetInstance();
Cache.Insert(key, instance, ...);
}
return instance;
其中,Type是緩存在內(nèi)存中的數(shù)據(jù)的類型——具體到本文,也就是Northwind.ProductsDataTable;此外,key用于唯一地標(biāo)識緩存的每一個(gè)條目。如果指定了key值的那個(gè)條目不在內(nèi)存中,那么instance就為null,然后用BLL類的某恰當(dāng)?shù)姆椒▉頇z索數(shù)據(jù),將獲得的數(shù)據(jù)緩存到內(nèi)存。將instance返回后,它將包含一個(gè)對數(shù)據(jù)的引用(reference to the data),數(shù)據(jù)要么來自內(nèi)存,要么是BLL類的返回?cái)?shù)據(jù)。
當(dāng)訪問內(nèi)存時(shí),請務(wù)必使用上述模式。下面的這個(gè)模式,咋一看好像和上面的模式一模一樣,但是有一個(gè)細(xì)微的區(qū)別,它存在一個(gè)race condition(可以理解為不易察覺的隱式缺陷)。race condition很難調(diào)試,因?yàn)樗皇桥紶柊l(fā)生,而且再次發(fā)生的可能性也小。如下:
if (Cache["key"] == null)
{
Cache.Insert(key, BllMethodToGetInstance(), ...);
}
return Cache["key"];
再一個(gè)就是,上述模式不是在局部變量里存儲緩存條目的引用,而是在條件語句里直接訪問數(shù)據(jù),在return語句里直接返回?cái)?shù)據(jù)。設(shè)想這種情況,開始運(yùn)行代碼時(shí)Cache["key"]是non-null的,但在運(yùn)行return語句前,系統(tǒng)將其從內(nèi)存里清除掉,那么代碼就會(huì)返回一個(gè)null值,而不是我們期望的某種類型的對象。
注意:如果僅僅是對data cache進(jìn)行讀或?qū)懺L問,你沒有必要進(jìn)行同步訪問(synchronize thread access);當(dāng)然,如果你需要對內(nèi)存里的數(shù)據(jù)進(jìn)行多重操作(multiple operations),你還是應(yīng)該實(shí)施鎖定(lock),或其它的機(jī)制。
如果要從data cache里清除某個(gè)條目,可以用Remove方法,比如:
Cache.Remove(key);
第三步:從ProductsCL類返回產(chǎn)品信息
在本文,我們要在ProductsCL類里用2個(gè)方法來返回產(chǎn)品信息: GetProducts()和 GetProductsByCategoryID(categoryID). 和業(yè)務(wù)邏輯層里的ProductsBL類相似,緩存層里的GetProducts()方法返回一個(gè)Northwind.ProductsDataTable對象,來獲取所有產(chǎn)品的信息;而GetProductsByCategoryID(categoryID)方法返回的是某個(gè)特定類別的所有產(chǎn)品。
如下的代碼是ProductsCL類里的部分方法:
[System.ComponentModel.DataObject]
public class ProductsCL
{
private ProductsBLL _productsAPI = null;
protected ProductsBLL API
{
get
{
if (_productsAPI == null)
_productsAPI = new ProductsBLL();
return _productsAPI;
}
}
[System.ComponentModel.DataObjectMethodAttribute(DataObjectMethodType.Select, true)]
public Northwind.ProductsDataTable GetProducts()
{
const string rawKey = "Products";
// See if the item is in the cache
Northwind.ProductsDataTable products = _
GetCacheItem(rawKey) as Northwind.ProductsDataTable;
if (products == null)
{
// Item not found in cache - retrieve it and insert it into the cache
products = API.GetProducts();
AddCacheItem(rawKey, products);
}
return products;
}
[System.ComponentModel.DataObjectMethodAttribute(DataObjectMethodType.Select, false)]
public Northwind.ProductsDataTable GetProductsByCategoryID(int categoryID)
{
if (categoryID < 0)
return GetProducts();
else
{
string rawKey = string.Concat("ProductsByCategory-", categoryID);
// See if the item is in the cache
Northwind.ProductsDataTable products = _
GetCacheItem(rawKey) as Northwind.ProductsDataTable;
if (products == null)
{
// Item not found in cache - retrieve it and insert it into the cache
products = API.GetProductsByCategoryID(categoryID);
AddCacheItem(rawKey, products);
}
return products;
}
}
}
首先,注意運(yùn)用到類(class)和方法(methods)上的屬性 DataObject和 DataObjectMethodAttribute ;這些屬性服務(wù)于ObjectDataSource的設(shè)置向?qū)?,指出那些類和方法?yīng)該出現(xiàn)在向?qū)У脑O(shè)置步驟里。因?yàn)镺bjectDataSource控件要在表現(xiàn)層訪問這些類和方法,所以我添加了這些屬性,方便向?qū)гO(shè)置。關(guān)于這些屬性及其作用,請參閱本教程第2章《創(chuàng)建一個(gè)業(yè)務(wù)邏輯層》。
在GetProducts() 和 GetProductsByCategoryID(categoryID)方法里,GetCacheItem(key)返回的數(shù)據(jù)賦值給一個(gè)局部變量。GetCacheItem(key)方法根據(jù)指定的key值在內(nèi)存查找對應(yīng)的緩存條目;如果沒找到,則用ProductsBLL類里相應(yīng)的方法來檢索數(shù)據(jù),并用AddCacheItem(key, value)方法將獲取的數(shù)據(jù)緩存到內(nèi)存。
GetCacheItem(key) 和 AddCacheItem(key, value)方法分別對data cache進(jìn)行讀、寫操作。GetCacheItem(key)相對簡單,它根據(jù)傳入的key值,從Cache類返回?cái)?shù)據(jù),如下:
private object GetCacheItem(string rawKey)
{
return HttpRuntime.Cache[GetCacheKey(rawKey)];
}
private readonly string[] MasterCacheKeyArray = {"ProductsCache"};
private string GetCacheKey(string cacheKey)
{
return string.Concat(MasterCacheKeyArray[0], "-", cacheKey);
}
GetCacheItem(key)并沒有直接使用我們提供的key值,而是調(diào)用GetCacheKey(key)方法,因?yàn)樵摲椒ǜ鶕?jù)“ProductsCache-”返回key;在上述代碼中,MasterCacheKeyArray用于存儲字符串“ProductsCache”。當(dāng)然,AddCacheItem(key, value)方法也會(huì)用到MasterCacheKeyArray,我們稍后會(huì)看到。
在ASP.NET頁面后臺代碼類(code-behind class),我們可以使用Page類的Cache屬性來訪問data cache ,就像我們在第2步里的表達(dá)式:Cache["key"] = value一樣;而在體系結(jié)構(gòu)的類(注:具體到本文,就是緩存層類(ProductsCL),我們可以通過2種方式來訪問:HttpRuntime.Cache 或 HttpContext.Current.Cache ;在Peter Johnson的博客里有一篇文章《HttpRuntime.Cache vs. HttpContext.Current.Cache》(http://weblogs.asp.net/pjohnson/archive/2006/02/06/437559.aspx),探討了HttpRuntim與相對于HttpContext.Current的優(yōu)點(diǎn);在此,我們的ProductsCL類將使用HttpRuntime.
注意:如果你是使用的類庫工程(Class Library projects),一定要記得引用System.Web才能使用HttpRuntime 和 HttpContext類。
如果沒有在內(nèi)存找到數(shù)據(jù),ProductsCL類將從業(yè)務(wù)邏輯層BLL獲取數(shù)據(jù),并使用AddCacheItem(key, value)對數(shù)據(jù)進(jìn)行緩存,可以用下面的代碼向內(nèi)存添加緩存數(shù)據(jù),其緩存時(shí)間為60秒:
const double CacheDuration = 60.0;
private void AddCacheItem(string rawKey, object value)
{
HttpRuntime.Cache.Insert(GetCacheKey(rawKey), value, null,
DateTime.Now.AddSeconds(CacheDuration), Caching.Cache.NoSlidingExpiration);
}
其中,DateTime.Now.AddSeconds(CacheDuration)指定了緩存時(shí)間—60秒;而 System.Web.Caching.Cache.NoSlidingExpiration指明了不存在可變緩存時(shí)間(no sliding expiration).雖然Insert()方法可以包含絕對時(shí)間和可變時(shí)間(absolute and sliding expiry)2種定義緩存時(shí)間的輸入?yún)?shù),但是你只能指定其中一個(gè),如果你同時(shí)指定絕對時(shí)間和可變時(shí)間2個(gè)參數(shù)的話,Insert()方法會(huì)拋出一ArgumentException 異常。
注意:直接執(zhí)行AddCacheItem(key, value)方法會(huì)有一些弊端,我們將在第4步解釋并修正。
第4步:當(dāng)數(shù)據(jù)被修改時(shí)使緩存失效
除了數(shù)據(jù)檢索方法外,緩存層還應(yīng)該包含插入、更新、刪除數(shù)據(jù)的方法。緩存層的數(shù)據(jù)修改方法并不是修改緩存的數(shù)據(jù),而是調(diào)用業(yè)務(wù)邏輯層的相應(yīng)方法,然后使緩存數(shù)據(jù)失效。就像前面章節(jié)探討的那樣,當(dāng)激活ObjectDataSource的緩存屬性時(shí),便可調(diào)用它的Insert, Update或Delete方法。
下面的UpdateProduct方法,說明了如何在緩存層CL執(zhí)行數(shù)據(jù)修改方法:
[System.ComponentModel.DataObjectMethodAttribute(DataObjectMethodType.Update, false)]
public bool UpdateProduct(string productName, decimal? unitPrice, int productID)
{
bool result = API.UpdateProduct(productName, unitPrice, productID);
// TODO: Invalidate the cache
return result;
}
在業(yè)務(wù)邏輯層的方法返回?cái)?shù)據(jù)以前,我們需要將緩存的數(shù)據(jù)失效。不過,這并非易事,無論P(yáng)roductsCL class's GetProducts()還是GetProductsByCategoryID(categoryID)都會(huì)向內(nèi)存添加條目,并且GetProductsByCategoryID(categoryID)方法會(huì)為每種類別添加幾個(gè)條目(因?yàn)槊糠N類別有幾種甚至更多的產(chǎn)品)。
要使緩存數(shù)據(jù)失效,我們需要將ProductsCL類添加的所有條目刪除。為此,在AddCacheItem(key, value)方法里,當(dāng)添加條目時(shí)為其指定一個(gè)緩存從屬體(cache dependency)。一般來說,緩存從屬體可以是內(nèi)存里的另一個(gè)條目;文件系統(tǒng)里的一個(gè)文件;又或者是Microsoft SQLServer database數(shù)據(jù)庫里的數(shù)據(jù)。當(dāng)從屬體發(fā)生改變,或者從內(nèi)存里移除時(shí),其對應(yīng)的緩存條目會(huì)自動(dòng)的從內(nèi)存刪除。在本教程,當(dāng)ProductsCL類向內(nèi)存添加條目時(shí),我們創(chuàng)建一個(gè)額外的條目作為其從屬體。由此,要?jiǎng)h除緩存條目,僅僅移除這些從屬體即可。
我們來更改AddCacheItem(key, value)方法,當(dāng)用該方法向內(nèi)存添加緩存數(shù)據(jù)時(shí),使每個(gè)條目與一個(gè)從屬體(cache dependency)對應(yīng)起來。
private void AddCacheItem(string rawKey, object value)
{
System.Web.Caching.Cache DataCache = HttpRuntime.Cache;
// Make sure MasterCacheKeyArray[0] is in the cache - if not, add it
if (DataCache[MasterCacheKeyArray[0]] == null)
DataCache[MasterCacheKeyArray[0]] = DateTime.Now;
// Add a CacheDependency
System.Web.Caching.CacheDependency dependency =
new CacheDependency(null, MasterCacheKeyArray);
DataCache.Insert(GetCacheKey(rawKey), value, dependency,
DateTime.Now.AddSeconds(CacheDuration),
System.Web.Caching.Cache.NoSlidingExpiration);
}
MasterCacheKeyArray是一個(gè)字符串?dāng)?shù)組,用來存儲“ProductsCache”. 首先檢查MasterCacheKeyArray,如果其為null,用當(dāng)前date和time對其賦值。然后,創(chuàng)建一個(gè)從屬體。CacheDependency類的構(gòu)造器(constructor)可以有很多重載(overloads),本文使用的重載接受2個(gè)字符串?dāng)?shù)組作為輸入?yún)?shù)。第一個(gè)參數(shù)指定文件作為從屬體,但本文我們不大算用文件來做從屬體,所以我們將第一個(gè)輸入?yún)?shù)設(shè)為null;第二個(gè)參數(shù)指定cache keys作為從屬體,本文我們指定為MasterCacheKeyArray。然后將該CacheDependency傳遞給Insert方法。
對AddCacheItem(key, value)方法做了上述修改后,要使緩存失效,很簡單,將從屬體移除即可:
[System.ComponentModel.DataObjectMethodAttribute(DataObjectMethodType.Update, false)]
public bool UpdateProduct(string productName, decimal? unitPrice, int productID)
{
bool result = API.UpdateProduct(productName, unitPrice, productID);
// Invalidate the cache
InvalidateCache();
return result;
}
public void InvalidateCache()
{
// Remove the cache dependency
HttpRuntime.Cache.Remove(MasterCacheKeyArray[0]);
}
第五步:在表現(xiàn)層調(diào)用緩存層
保存對ProductsCL類的修改,打開Caching文件夾里的FromTheArchitecture.aspx頁面,并添加一個(gè)GridView控件。從GridView控件的智能標(biāo)簽里創(chuàng)建一個(gè)新的ObjectDataSource,在向?qū)У牡谝徊剑瑥南吕斜砝镞x擇ProductsCL,如下圖:

圖4:類ProductsCL包含在下拉列表里
選定ProductsCL類后,點(diǎn)Next。我們可以看到在SELECT標(biāo)簽里有2個(gè)選項(xiàng):GetProducts() 和 GetProductsByCategoryID(categoryID)方法;而在UPDATE標(biāo)簽里只有唯一的一個(gè)UpdateProduct()方法。在SELECT標(biāo)簽里選擇GetProducts()方法;而在UPDATE標(biāo)簽里選擇那個(gè)唯一的UpdateProduct()方法,最后點(diǎn)Finish。

圖5:ProductsCL類的方法包含在下拉列表里。
完成向?qū)Ш螅琕isual Studio會(huì)將ObjectDataSource的OldValuesParameterFormatString屬性設(shè)置為original_{0},并向GridView添加相應(yīng)的列。將OldValuesParameterFormatString該為默認(rèn)值{0}, 并啟用GridView控件的分頁、排序、編輯功能。由于緩存層CL的UploadProducts()方法只對產(chǎn)品的name 和 price進(jìn)行編輯,由此需要對GridView做相應(yīng)的修改以限制其只能編輯這2列。
在前面的教程,我們指定GridView控件包含 ProductName, CategoryName,和UnitPrice3列。放心大膽的將其復(fù)制過來,這樣,GridView 和 ObjectDataSource的聲明代碼看起來應(yīng)該像下面的這樣:
<asp:GridView ID="Products" runat="server" AutoGenerateColumns="False"
DataKeyNames="ProductID" DataSourceID="ProductsDataSource"
AllowPaging="True" AllowSorting="True">
<Columns>
<asp:CommandField ShowEditButton="True" />
<asp:TemplateField HeaderText="Product" SortExpression="ProductName">
<EditItemTemplate>
<asp:TextBox ID="ProductName" runat="server"
Text='<%# Bind("ProductName") %>' />
<asp:RequiredFieldValidator ID="RequiredFieldValidator1"
ControlToValidate="ProductName" Display="Dynamic"
ErrorMessage="You must provide a name for the product."
SetFocusOnError="True"
runat="server">*</asp:RequiredFieldValidator>
</EditItemTemplate>
<ItemTemplate>
<asp:Label ID="Label2" runat="server"
Text='<%# Bind("ProductName") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:BoundField DataField="CategoryName" HeaderText="Category"
ReadOnly="True" SortExpression="CategoryName" />
<asp:TemplateField HeaderText="Price" SortExpression="UnitPrice">
<EditItemTemplate>
$<asp:TextBox ID="UnitPrice" runat="server" Columns="8"
Text='<%# Bind("UnitPrice", "{0:N2}") %>'></asp:TextBox>
<asp:CompareValidator ID="CompareValidator1" runat="server"
ControlToValidate="UnitPrice" Display="Dynamic"
ErrorMessage="You must enter a valid currency value with
no currency symbols. Also, the value must be greater than
or equal to zero."
Operator="GreaterThanEqual" SetFocusOnError="True"
Type="Currency" ValueToCompare="0">*</asp:CompareValidator>
</EditItemTemplate>
<ItemStyle HorizontalAlign="Right" />
<ItemTemplate>
<asp:Label ID="Label1" runat="server"
Text='<%# Bind("UnitPrice", "{0:c}") %>' />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
<asp:ObjectDataSource ID="ProductsDataSource" runat="server"
OldValuesParameterFormatString="{0}" SelectMethod="GetProducts"
TypeName="ProductsCL" UpdateMethod="UpdateProduct">
<UpdateParameters>
<asp:Parameter Name="productName" Type="String" />
<asp:Parameter Name="unitPrice" Type="Decimal" />
<asp:Parameter Name="productID" Type="Int32" />
</UpdateParameters>
</asp:ObjectDataSource>
這樣,我們該頁面就使用了緩存層。為實(shí)地演示緩存,在ProductsCL類的GetProducts() 和 UpdateProduct()方法里設(shè)置斷點(diǎn)(breakpoints),在瀏覽器里訪問該頁面,當(dāng)排序或分頁時(shí)就會(huì)執(zhí)行這些代碼,從內(nèi)存獲取數(shù)據(jù)。然后更新一條記錄,注意由于緩存失效,將從業(yè)務(wù)邏輯層BLL獲取數(shù)據(jù)并綁定到GridView。
注意:從本文download鏈接下載的緩存層并不完善。它只包含了一個(gè)ProductsCL類,它只包含幾個(gè)方法。此外,只有一個(gè)ASP.NET頁面(~/Caching/FromTheArchitecture.aspx)使用了緩存層CL,而其它的頁面都是直接調(diào)用業(yè)務(wù)邏輯層BLL。如果打算在你的應(yīng)用程序里使用緩存層CL,那么頁面層的所有調(diào)用都應(yīng)該先訪問緩存層CL。
總結(jié):
雖然可以在ASP.NET 2.0的表現(xiàn)層對SqlDataSource 和 ObjectDataSource控件實(shí)施緩存,但更理想的做法是在體系單獨(dú)分層來達(dá)到緩存的目的。在本文,我們在表現(xiàn)層和業(yè)務(wù)邏輯層之間創(chuàng)建了一個(gè)緩存層,該緩存層包含的類和方法與現(xiàn)有的業(yè)務(wù)邏輯層所包含的類和方法類似。當(dāng)然,也是在表現(xiàn)層調(diào)用。
本示例及前面教程處理的是“觸發(fā)裝載”(reactive loading)—也就是說當(dāng)發(fā)現(xiàn)請求的數(shù)據(jù)沒在內(nèi)存后將數(shù)據(jù)裝載進(jìn)內(nèi)存。其實(shí)數(shù)據(jù)也可以“預(yù)裝載”(proactively loaded)進(jìn)內(nèi)存—也就是說在數(shù)據(jù)實(shí)際請求之前將其預(yù)先裝載進(jìn)內(nèi)存。在下一篇文章我們將看到預(yù)裝載的情形——在應(yīng)用程序啟動(dòng)的時(shí)候如何將靜態(tài)值(static values)裝載進(jìn)內(nèi)存。
祝編程快樂!
作者簡介
本系列教程作者 Scott Mitchell,著有六本ASP/ASP.NET方面的書,是4GuysFromRolla.com的創(chuàng)始人,自1998年以來一直應(yīng)用 微軟Web技術(shù)。大家可以點(diǎn)擊查看全部教程《[翻譯]Scott Mitchell 的ASP.NET 2.0數(shù)據(jù)教程》,希望對大家的學(xué)習(xí)ASP.NET有所幫助。
- 在ASP.NET 2.0中操作數(shù)據(jù)之五十八:在程序啟動(dòng)階段緩存數(shù)據(jù)
- 在ASP.NET 2.0中操作數(shù)據(jù)之五十九:使用SQL緩存依賴項(xiàng)SqlCacheDependency
- 在ASP.NET 2.0中操作數(shù)據(jù)之六十:創(chuàng)建一個(gè)自定義的Database-Driven Site Map Provider
- 在ASP.NET 2.0中操作數(shù)據(jù)之六十一:在事務(wù)里對數(shù)據(jù)庫修改進(jìn)行封裝
- 在ASP.NET 2.0中操作數(shù)據(jù)之六十二:GridView批量更新數(shù)據(jù)
- 在ASP.NET 2.0中操作數(shù)據(jù)之六十三:GridView實(shí)現(xiàn)批量刪除數(shù)據(jù)
- 在ASP.NET 2.0中操作數(shù)據(jù)之六十四:GridView批量添加數(shù)據(jù)
- 在ASP.NET 2.0中操作數(shù)據(jù)之六十五:在TableAdapters中創(chuàng)建新的存儲過程
- 在ASP.NET 2.0中操作數(shù)據(jù)之六十六:在TableAdapters中使用現(xiàn)有的存儲過程
- 在ASP.NET 2.0中操作數(shù)據(jù)之六十七:在TableAdapters中使用JOINs
相關(guān)文章
ASP.NET MVC4入門教程(七):給電影表和模型添加新字段
本文使用Entity Framework Code First來實(shí)現(xiàn)模型類上的操作,使用代碼優(yōu)先的模式,從而使得直接修改代碼,數(shù)據(jù)庫也會(huì)做相應(yīng)的改變。2016-04-04
在ASP.NET 2.0中操作數(shù)據(jù)之十七:研究插入、更新和刪除的關(guān)聯(lián)事件
本文主要講解ASP.NET 2.0中如何在點(diǎn)擊插入、更新、刪除等按鈕時(shí)關(guān)聯(lián)和觸發(fā)事件,以便我們在事件中寫代碼,實(shí)現(xiàn)我們期望的效果。2016-05-05
為Visual Studio手工安裝微軟ReportViewer控件
這篇文章介紹了為Visual Studio手工安裝微軟ReportViewer控件的方法,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06
在ASP.NET 2.0中操作數(shù)據(jù)之四十三:DataList和Repeater數(shù)據(jù)排序(二)
上篇已經(jīng)介紹了DropDownList隱式使用ViewState,本文主要介紹詳細(xì)介紹直接使用ViewState存儲排序的條件,并配合上一頁、下一頁按鈕,實(shí)現(xiàn)DataList和Repeater自定義排序的功能。2016-05-05
ASP.NET MVC4入門教程(九):查詢詳細(xì)信息和刪除記錄
本文主要是MVC實(shí)戰(zhàn),介紹如何查詢和刪除信息,進(jìn)行到這一步,您已經(jīng)有一個(gè)完整的MVC案例了,創(chuàng)建、 讀取、 更新、 刪除和搜索等功能也都做了演示。2016-04-04
基于.net開發(fā)的遵循web標(biāo)準(zhǔn)的個(gè)人站點(diǎn)程序包下載
基于.net開發(fā)的遵循web標(biāo)準(zhǔn)的個(gè)人站點(diǎn)程序包下載...2006-10-10
在ASP.NET 2.0中操作數(shù)據(jù)之三十八:處理BLL和DAL的異常
本文主要介紹如何在BLL和DAL層如何處理異常,以達(dá)到給用戶顯示友好的錯(cuò)誤信息。2016-05-05
Microsoft .Net Remoting系列教程之三:Remoting事件處理全接觸
本文主要講解.Net Remoting中的Remoting事件處理,需要的朋友可以參考下。2016-05-05

