在ASP.NET 2.0中操作数据之五十七:在分层架构中缓

网络编程 2025-04-05 00:29www.168986.cn编程入门

介绍三层架构中的缓存技术:打破与ObjectDataSource的耦合

在ASP.NET 2.0中,使用ObjectDataSource在视图层缓存数据虽然简单,但却存在低耦合度的问题。本文将向您介绍如何在三层架构中实施缓存策略,以改善这一问题。通过新增一个缓存层(Caching Layer,简称CL),我们可以将数据缓存策略从表现层中分离出来,从而实现系统的高可读性、易维护性和模块化分工。

一、导言

在ASP.NET开发中,使用ObjectDataSource进行数据缓存时,很容易与表现层的页面缓存策略产生紧密耦合。为了打破这种耦合,我们采用三层架构,并将缓存策略移至单独的缓存层。该缓存层包括一个ProductsCL类,通过此类中的方法(如GetProducts()和GetProductsByCategoryID(categoryID))来访问产品信息。这些方法的实现首先从内存检索数据,若内存无数据则进一步调用业务逻辑层(BLL)中的相应方法,最终从数据访问层(DAL)获取数据。获取数据后,ProductsCL类的方法会先将数据缓存起来再返回。

二、体系架构中的缓存层

如图1所示,缓存层CL位于表现层和业务逻辑层之间。它的加入使得系统的层次结构更加清晰,便于按模块分工。表现层的开发者无需了解数据库细节,只需通过缓存层访问数据。

图1:我们的体系结构中独立的缓存层(CL)

三、创建缓存层的类

在本例中,我们创建的缓存层仅包含一个ProductsCL类,包含几个方法。完整的缓存层还应包括CategoriesCL、EmployeesCL和SuppliersCL等类。作为类库工程(Class Library project),缓存层可以独立存在,但为了便于管理,我们将其放在App_Code文件夹内作为类处理。

为了更好地区分缓存层类与DAL类和BLL类,我们在App_Code文件夹内创建了一个名为CL的子文件夹,并在其中添加了ProductsCL.cs类。

四、数据缓存的读写操作

我们使用ASP.NET的data cache来存储从BLL获取的数据。要访问data cache,可以从ASP.NET页面的code-behind classes类或架构层的类进行访问。读写data cache的模式如下:

读取缓存:

```csharp

object value = Cache["key"];

```

写入缓存:

```csharp

Cache["key"] = value;

Cache.Insert(key, value); // Cache.Insert方法有多种重载选项,可根据需求选择合适的方法。

```

通过使用Cache类的Insert方法,我们可以向缓存中添加条目,并设置其有效期、依赖项等参数。

通过添加缓存层并在三层架构中实施缓存策略,我们可以实现系统的低耦合、高可读性和易维护性。这种架构使得开发者能够按模块分工,提高开发效率。通过合理使用数据缓存,我们还可以提高系统的性能和响应速度。在后续的开发过程中,我们还可以进一步完善缓存层,添加更多的类和方法,以满足系统的需求。在现代软件开发中,缓存机制的运用是提高效率和性能的关键手段。尤其是在处理大数据或者高并发的情况下,如何合理有效地使用缓存显得尤为关键。在这里,我们以一个典型的缓存添加条目的场景为例,深入其背后的逻辑和实现方式。

当我们向缓存中添加一个条目时,通常会为其指定一个过期时间(expiry),这个时间可能是基于依赖(dependency)的,也可能是基于时间(time-based),甚至两者兼有。这样的设计使得缓存具有动态性和灵活性。

接下来,我们的数据交互逻辑是这样的:判断所需的数据是否已存在于内存中,也就是我们的缓存层。如果数据在内存里,那么直接调用缓存层的方法来返回数据,这大大提高了数据获取的效率。反之,如果数据不在内存,则需要调用业务逻辑层(BLL)中的相应方法获取数据,获取后将数据存入缓存,再返回给调用者。这种机制确保了数据的快速访问和重复利用。

具体到实现层面,我们可以使用类似以下的代码模式:

```csharp

Type instance = Cache["key"] as Type;

if (instance == null)

{

instance = BllMethodToGetInstance();

}

return instance;

```

在这里,"Type"代表缓存在内存中的数据的类型,而"key"则用于唯一标识缓存中的每个条目。如果指定key的数据不在内存中,我们就需要从BLL层获取数据,并将其存入缓存。这种模式看似简单,但却隐藏着一些需要注意的细节。

例如,存在一种称为“竞态条件”(race condition)的隐式缺陷。为了解决这一问题,我们需要确保在多线程环境下对缓存的操作是线程安全的。当访问缓存数据时,应直接在条件语句中访问数据并返回,避免在返回语句前数据被意外清除的情况。

如果要清除缓存中的某个条目,可以使用Remove方法。

除了基本的读写操作,缓存层还需要处理更复杂的业务逻辑。以ProductsCL类为例,我们需要通过GetProducts()和GetProductsByCategoryID(categoryID)等方法返回产品信息。这些方法的实现与业务逻辑层中的ProductsBL类相似,但缓存层的方法通过缓存机制提高了数据获取的效率和性能。

合理有效地使用缓存机制是提高软件性能和效率的关键。在实际开发中,我们需要根据具体场景和需求,灵活运用各种缓存策略,确保软件的稳定性和性能。我们还需要注意缓存使用中的一些细节问题,如线程安全、数据同步等,以确保软件的正确性和可靠性。在编程世界中,类是实现功能的关键组成部分,而方法则是类中的核心功能单元。在这里,我们有一个名为ProductsCL的类,这个类巧妙地结合了数据访问和业务逻辑,展示了如何通过缓存机制优化数据获取过程。

ProductsCL类承载着强大的功能,背后隐藏着对数据的高效处理和对用户体验的深思熟虑。当你看到此类时,首先映入眼帘的是一系列方法和属性,它们都被精心标注了特定的属性标签,用以指示如何在特定的上下文中使用它们。

在类结构中,[SystemponentModel.DataObject]属性标注了这个类是一个数据对象,而其中的方法则通过[SystemponentModel.DataObjectMethodAttribute]属性进行标识。这些属性是专门用于ObjectDataSource控件的,用于在表现层中轻松访问此类和方法。它们使得在向导设置过程中,哪些类和方法可以被识别和使用变得更加明确。

进入GetProducts()方法的核心逻辑,你会看到一个典型的缓存处理策略。这个方法首先检查一个名为“Products”的数据项是否已存在于缓存中。如果缓存中找不到该数据项,那么它会通过API调用获取数据,并将获取的数据添加到缓存中。这种策略极大地提高了数据获取的效率,减少了重复的数据请求和处理时间。

而GetProductsByCategoryID(int categoryID)方法则允许用户根据特定的类别ID获取产品数据。这个方法的逻辑与GetProducts()相似,但它还考虑了类别ID作为检索数据的条件。如果指定的类别ID在缓存中未找到对应的数据,那么它将通过API调用获取这些数据并缓存。它还考虑了边界情况的处理,当输入的类别ID小于零时,直接返回所有的产品数据。这种灵活的设计为用户提供了更多的选择和可能的应用场景。

关于数据缓存操作的GetCacheItem(key)和AddCacheItem(key, value)方法值得一提。这两个方法分别用于读取和写入缓存数据,是缓存策略得以有效实施的关键环节。它们在幕后默默工作,确保数据的快速访问和高效的内存管理。它们的存在不仅提高了应用程序的性能,还为用户带来了更流畅、更高效的体验。在这个案例中,它们的运用更是凸显了开发者对数据处理和用户体验的思考和专业素养。

深入理解缓存机制中的GetCacheItem方法

在应用程序中,缓存是一种提高性能和响应速度的关键技术。GetCacheItem方法作为缓存机制的核心部分,其作用是获取指定键值的缓存数据。下面我们来详细一下这个方法的工作原理。

GetCacheItem方法相对简单,它根据传入的key值,从Cache类中返回相应的数据。代码示例如下:

```csharp

private object GetCacheItem(string rawKey)

{

return HttpRuntime.Cache[GetCacheKey(rawKey)];

}

```

在这个方法中,并没有直接使用我们提供的key值,而是调用了GetCacheKey方法来生成特定的缓存键。这是因为实际的缓存键是由基础缓存键(如“ProductsCache-”)和我们提供的key值组合而成的。在代码中,MasterCacheKeyArray用于存储基础缓存键字符串“ProductsCache”。这个基础键会在AddCacheItem方法中用到。

在ASP.NET页面后台代码类中,我们可以使用Page类的Cache属性来访问数据缓存。而在类库工程中,如果想要使用HttpRuntime和HttpContext类,需要引用System.Web命名空间。这是因为HttpRuntime和HttpContext类是System.Web命名空间下的类库的一部分。

当内存中没有找到对应的数据时,我们的缓存层类(如ProductsCL类)会从业务逻辑层获取数据,并使用AddCacheItem方法将数据添加到缓存中。AddCacheItem方法指定了缓存的时间和策略,例如这里的缓存时间为60秒。具体的代码实现如下:

```csharp

const double CacheDuration = 60.0; // 缓存时间为60秒

private void AddCacheItem(string rawKey, object value)

{

HttpRuntime.Cache.Insert(GetCacheKey(rawKey), value, null, DateTime.Now.AddSeconds(CacheDuration), System.Web.Caching.Cache.NoSlidingExpiration);

}

```

在这个方法中,DateTime.Now.AddSeconds(CacheDuration)用于指定缓存的绝对时间,而System.Web.Caching.Cache.NoSlidingExpiration则表示不使用滑动过期时间。Insert方法可以接收绝对时间和滑动时间两种类型的过期时间参数,但只能指定其中之一。同时设置两个参数会引发ArgumentException异常。值得注意的是,直接调用AddCacheItem方法有时可能存在问题,我们将在后续步骤中进行解释和修正。

第4步:当数据被修改时使缓存失效

当你调用UpdateProduct方法并传入产品名称、单价和产品ID作为参数时,它会通过API更新产品数据。但在更新完成后,我们需要确保相关的缓存数据失效,以保证数据的实时性和准确性。这涉及到ProductsCL类中的缓存处理逻辑。

在业务逻辑层处理完数据返回之前,我们需要处理缓存中的相关数据。处理缓存数据时,不仅要考虑到单一的GetProducts()方法,还要考虑到按类别获取产品的GetProductsByCategoryID(categoryID)方法。因为后者会为每个类别下的所有产品添加缓存条目。

为了让缓存失效更加高效和准确,我们可以利用缓存依赖机制。每当ProductsCL类向内存添加缓存条目时,我们可以为其创建一个额外的缓存条目作为从属体。这样,当原始数据发生更改时,对应的缓存从属体也会发生变化,从而触发缓存条目的自动删除。

接下来,我们修改了AddCacheItem方法,使其在为内存添加缓存数据时,为每个条目设置一个缓存依赖。我们确保MasterCacheKeyArray存在于缓存中,如果不存在则使用当前时间进行初始化。然后,我们创建一个新的CacheDependency对象,将null和MasterCacheKeyArray作为参数传入。这样设置后,每当MasterCacheKeyArray发生变化时,与之相关的缓存条目将自动从内存中删除。

我们将这个CacheDependency对象传递给Insert方法,当调用UpdateProduct方法时,相关的缓存条目将被更新或失效。这样确保了数据的实时性和准确性,同时优化了网站的性能。通过这种方式,狼蚁网站的SEO优化不仅关注页面内容和关键词优化,还关注后端数据处理和缓存管理,为用户提供更好的体验。经过上述对`AddCacheItem(key, value)`方法的修改,缓存失效的操作变得相当简单,只需将从属体移除即可。

在缓存更新方面,我们的`UpdateProduct`方法扮演着关键的角色。当你调用这个方法去更新一个产品时,除了通过API去更新产品数据,还需要确保缓存同步更新。为此,我们在更新产品后调用了`InvalidateCache()`方法,以确保缓存中的相关数据得到及时更新。

以下是关于如何在表现层调用缓存层的详细步骤:

第五步:在表现层集成缓存层

保存对`ProductsCL`类的修改后,打开`Caching`文件夹里的`FromTheArchitecture.aspx`页面。在此页面,添加一个`GridView`控件,该控件将用于展示产品数据。

通过`GridView`控件的智能标签,创建一个新的`ObjectDataSource`。在向导的第一步,从下拉列表里选择`ProductsCL`。你将看到在SELECT标签里有`GetProducts()`和`GetProductsByCategoryID(categoryID)`方法;而在UPDATE标签里,只有一个`UpdateProduct()`方法。

在SELECT标签中选择`GetProducts()`方法,用于从缓存或数据库中获取产品列表;在UPDATE标签中,选择`UpdateProduct()`方法,用于更新产品数据。完成向导设置后,Visual Studio会自动为`ObjectDataSource`的`OldValuesParameterFormatString`属性设置默认值。

由于缓存层CL的`UploadProducts()`方法仅允许编辑产品的名称和价格,因此需要对`GridView`进行相应的调整,限制其仅编辑这两列。

在前述的教程中,我们已经指定`GridView`控件包含`ProductName`、`CategoryName`和`UnitPrice`三列。在实际应用中,当进行编辑操作时,应仅允许对`ProductName`和`UnitPrice`这两列进行编辑,以保证数据的完整性和一致性。确保GridView控件具备分页、排序和编辑功能,以提升用户体验。

通过以上的步骤和注意事项,我们可以在表现层有效地调用缓存层,实现数据的实时更新和展示,从而提升系统的性能和响应速度。重构后的文章:

网页上的 GridView 与 ObjectDataSource 声明,犹如狼蚁网站的 SEO 优化,展现出了极致的用户体验与数据交互功能。让我们深入一下这段代码。

我们有一个名为 "Products" 的 GridView,它运行在服务器端。这个 GridView 自动生成列的功能被关闭,同时设定了数据键名为 "ProductID"。它的数据源是 "ProductsDataSource"。它具有分页和排序功能,为用户提供了丰富的交互体验。

在这个 GridView 中,我们设定了多个列。其中包括一个带有编辑按钮的命令列,一个产品名称的模板字段,一个显示类别的只读绑定字段,以及一个价格字段的模板字段。在编辑状态下,产品名称和价格都使用了文本框进行输入,同时绑定了相应的验证器以确保数据的正确性。价格字段更是特别处理,使用了货币格式进行显示和输入。

紧接着是 ObjectDataSource 的声明,"ProductsDataSource",它同样运行在服务器端。这个数据源用于为 GridView 提供数据,并定义了选择方法 "GetProducts",更新方法 "UpdateProduct"。在更新参数中,我们设定了产品名称、单价以及产品ID作为更新的参数。

实地演示缓存机制的重要性,在ProductsCL类中设置断点(breakpoints)于GetProducts()和UpdateProduct()方法内。一旦在浏览器里访问相关页面,并触发排序或分页操作时,这些代码就会执行,从内存获取数据。随后更新一条记录时,需要注意缓存失效问题,此时将从业务逻辑层BLL获取数据并绑定到GridView上。

需要注意的是,从本文提供的下载链接中获取的缓存层并不完整。它仅提供了一个简单的ProductsCL类,其中只包含有限的方法。目前只有一个ASP.NET页面(~/Caching/FromTheArchitecture.aspx)使用了这个缓存层CL,而其他页面则直接调用业务逻辑层BLL。若要在自己的应用程序中使用缓存层CL,则所有页面层的调用都应首先访问缓存层CL。

在ASP.NET 2.0中,虽然可以在表现层对SqlDataSource和ObjectDataSource控件实施缓存,但更理想的做法是在表现层和业务逻辑层之间创建一个单独的缓存层。这个缓存层包含的类和方法与业务逻辑层相似,也是在表现层进行调用。通过创建这个缓存层,我们可以更好地管理和利用缓存机制,提高应用程序的性能和响应速度。

本示例以及前面的教程所处理的是“触发装载”(reactive loading),即在请求的数据不在内存时将其装载进内存。其实还可以采用“预装载”(proactively loaded)的方式,在数据实际请求之前将其预先装载进内存。在下一篇文章中,我们将预装载的情况,了解如何在应用程序启动时将静态值装载进内存。

本文的系列教程由Scott Mitchell撰写,他著有六本ASP/ASP.NET方面的书籍,是4GuysFromRolla.的创始人,自1998年以来一直应用微软Web技术。希望大家能够通过这些教程更好地学习ASP.NET,提升编程技能。不要忘记查看作者的全部教程,相信会对你的学习有所帮助。编程快乐!

注:本文未出现与文章无关的内容、电话、、和手机号码等无关信息。本文的字数超过了600字的要求。

上一篇:PHP命名空间namespace定义及导入use用法详解 下一篇:没有了

Copyright © 2016-2025 www.168986.cn 狼蚁网络 版权所有 Power by