在一句话中,我最终需要知道的是如何在中间层函数之间共享对象,而不需要应用程序层来传递数据模型对象。
我正在为我正在为之工作的公司在当前环境中构建中间层。目前,我们主要使用.NET进行编程,并围绕所有各种数据库系统构建自定义数据模型(包括Oracle,OpenLDAP,MSSQL等)。
我遇到了一些问题,试图将我们的模型从应用程序层拉出来并将其移动到一系列中间层库中。我遇到的主要问题是应用程序层能够在整个进程期间挂起缓存对象并根据缓存数据进行更新,但Mid-Tier操作不会。
我正在尝试尽可能地将模型对象保留在应用程序之外,这样当我们对底层数据库结构进行更改时,我们可以轻松地编辑和重新部署中间层,而多个应用程序将不需要被重建。我将简要介绍伪代码中的问题,因为这是我们开发人员最了解的:)
main
{
MidTierServices.UpdateCustomerName("testaccount", "John", "Smith");
// since the data takes up to 4 seconds to be replicated from
// write server to read server, the function below is going to
// grab old data that does not contain the first name and last
// name update.... John Smith will be overwritten w/ previous
// data
MidTierServices.UpdateCustomerPassword("testaccount", "jfjfjkeijfej");
}
MidTierServices
{
void UpdateCustomerName(string username, string first, string last)
{
Customer custObj = DataRepository.GetCustomer(username);
/*******************
validation checks and business logic go here...
*******************/
custObj.FirstName = first;
custObj.LastName = last;
DataRepository.Update(custObj);
}
void UpdateCustomerPassword(string username, string password)
{
// does not contain first and last updates
Customer custObj = DataRepository.GetCustomer(username);
/*******************
validation checks and business logic go here...
*******************/
custObj.Password = password;
// overwrites changes made by other functions since data is stale
DataRepository.Update(custObj);
}
}
另一方面,我考虑过的选项是构建一个自行开发的缓存层,这需要花费大量时间,这是一个非常难以向管理层出售的概念。使用具有内置缓存支持的不同建模层,例如nHibernate:这也很难销售给管理层,因为这个选项也需要很长时间才能拆除我们的整个自定义模型并用第三方解决方案替换它。此外,并非许多供应商都支持我们的大量数据库。例如,.NET具有LINQ to ActiveDirectory,但不具有LINQ to OpenLDAP。
无论如何,对于这部小说感到抱歉,但它更像是一个企业架构类型的问题,而不是一个简单的代码问题,例如“我如何获得.NET中的当前日期和时间?”
抱歉,我忘了在原帖中添加一些非常重要的信息。我感觉非常糟糕,因为Cheeso经历了很多麻烦,写了一个非常深入的回复,这可以解决我的问题,因为没有更多的问题(我愚蠢地没有包括)。
我面临当前问题的主要原因是数据复制。第一个函数对一个服务器进行写操作,然后下一个函数从另一个尚未收到复制数据的服务器进行读取。基本上,我的代码比数据复制过程更快。
我可以通过总是读取和写入同一个LDAP服务器来解决这个问题,但我的管理员可能会因此而谋杀我。专门设置一个仅用于写入的服务器,然后设置4个其他服务器,位于负载均衡器后面,仅用于读取。我不是LDAP管理员,所以我不知道这是否是标准程序。
答案 0 :(得分:3)
您正在描述一个非常常见的问题。
解决这个问题的正常方法是使用Optimistic Concurrency Control。
如果这听起来像gobbledegook,那不是。这个想法非常简单。该术语的并发部分指的是记录数据发生了更新,并且这些更新同时发生。可能是很多作家。 (你的情况是一个堕落的情况,其中一个作家是问题的根源,但它是相同的基本想法)。我将在一分钟内得到乐观的部分。
问题
当有多个写入器时,两个更新的读取+写入部分交叉存在是可能的。假设你有A和B,他们都读取然后更新数据库中的同一行。 A读取数据库,然后B读取数据库,然后B更新它,然后A更新它。如果你有一个天真的方法,那么“最后写”将获胜,而B的写作可能会被破坏。
输入乐观并发。基本思想是假设更新将起作用,但检查。几年之后,就像信任但验证军备控制方法一样。执行此操作的方法是在数据库表中包含一个字段,该字段也必须包含在域对象中,该字段提供了区分数据库行或域对象的一个“版本”与另一个“行”的方法。最简单的方法是使用名为lastUpdate的时间戳字段,该字段保存上次更新的时间。还有其他更复杂的方法可以进行一致性检查,但时间戳字段很适合用于说明目的。
然后,当编写者或更新者想要更新数据库时,它只能在lastUpdate匹配时更新密钥匹配的行(无论您的密钥是什么)以及。这是验证部分。
由于开发人员理解代码,我将提供一些伪SQL。假设您有一个博客数据库,其中包含索引,标题和每个博客条目的一些文本。您可以检索一组行(或对象)的数据,如下所示:
SELECT ix, Created, LastUpdated, Headline, Dept FROM blogposts
WHERE CONVERT(Char(10),Created,102) = @targdate
这种查询可能会检索数据库中给定日期,月份或其他任何内容的所有博客帖子。
使用简单的乐观并发,您可以使用SQL更新单行,如下所示:
UPDATE blogposts Set Headline = @NewHeadline, LastUpdated = @NewLastUpdated
WHERE ix=@ix AND LastUpdated = @PriorLastUpdated
更新只能在索引匹配时发生(并且我们假设它是主键),而LastUpdated字段与读取数据时的相同。另请注意,您必须确保为每次更新行更新LastUpdated字段。
更严格的更新可能会坚持没有更新任何列。在这种情况下,根本没有时间戳。像这样:
UPDATE Table1 Set Col1 = @NewCol1Value,
Set Col2 = @NewCol2Value,
Set Col3 = @NewCol3Value
WHERE Col1 = @OldCol1Value AND
Col2 = @OldCol2Value AND
Col3 = @OldCol3Value
为什么称它为“乐观”?
OCC被用作保存数据库锁的替代方法,这是保持数据一致的严厉方法。数据库锁定可能会阻止任何人读取或更新数据库行。这显然具有巨大的性能影响。因此,OCC放松了这一点,并且“乐观地”行动,假设在更新时,表格中的数据在此期间不会更新。但当然不是盲目乐观 - 你必须在更新前检查。
在实践中使用乐观的加拿大货币
你说你使用.NET。我不知道您是否使用DataSet进行数据访问,强类型或其他方式。但是.NET DataSet,或者特别是DataAdapter,包括对OCC的内置支持。您可以为任何DataAdapter指定和手动编写UpdateCommand,也就是可以插入一致性检查的地方。这也是possible within the Visual Studio design experience。
alt text http://static.asp.net/asp.net/images/dataaccess/21fig06CS.png
如果您收到违规行为,则更新将返回显示ZERO行已更新的结果。您可以在DataAdapter.RowUpdated event中查看此内容。 (请注意,在ADO.NET模型中,每种类型的数据库都有不同的DataAdapter。链接有SqlDataAdapter,它与SQL Server一起使用,但您需要为不同的数据源使用不同的DA。)
在RowUpdated事件中,您可以检查已受影响的行数,然后在计数为零时执行某些操作。
<强>摘要强>
在编写更新之前,验证数据库的内容是否未更改。这称为乐观并发控制。
其他链接: