reg*_*gex 5 c# architecture soa caching
在一句话中,我最终需要知道的是如何在中间层函数之间共享对象,而不需要应用程序层来传递数据模型对象.
我正在为我正在为之工作的公司在当前环境中构建中间层.目前,我们主要使用.NET进行编程,并围绕所有各种数据库系统(包括Oracle,OpenLDAP,MSSQL等)构建自定义数据模型.
我遇到了一些问题,试图将我们的模型从应用程序层拉出来并将其转移到一系列中间层库中.我遇到的主要问题是应用程序层有挂到一个缓存的对象在整个过程的持续时间,并根据缓存的数据更新的能力,但中间层的操作没有.
我试图保持模型的对象了应用,尽可能的让我们做出改变的基础数据库结构,我们可以编辑和轻松部署中间层和多个应用程序也不会需要重建.我将简要介绍伪代码中的问题,因为这是我们开发人员最了解的:)
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);
}
}
Run Code Online (Sandbox Code Playgroud)
另外,我考虑过的选项是构建一个自行开发的缓存层,这需要花费大量时间,这是一个非常难以向管理层出售的概念.使用具有内置的缓存支持,例如NHibernate的一个不同的建模层:这也将是很难出售给管理层,因为这个选项也将需要很长的时间撕开我们整个定制模型和替换它瓦特/第三方解决方案.此外,并非许多供应商都支持我们的大量数据库.例如,.NET具有LINQ to ActiveDirectory,但不具有LINQ to OpenLDAP.
无论如何,对小说感到抱歉,但它更像是一个企业架构类型的问题,而不是一个简单的代码问题,比如"如何获得.NET中的当前日期和时间?"
对不起,我忘了在原帖中添加一些非常重要的信息.我感觉非常糟糕,因为Cheeso经历了很多麻烦,写了一个非常深入的回复,这可以解决我的问题,因为没有更多的问题(我愚蠢地没有包括).
我面临当前问题的主要原因是数据复制.第一个函数对一个服务器进行写操作,然后下一个函数从另一个尚未收到复制数据的服务器进行读取.基本上,我的代码比数据复制过程更快.
我可以通过总是读取和写入同一个LDAP服务器来解决这个问题,但我的管理员可能会因此而谋杀我.专门设置一个仅用于写入的服务器,然后设置4个其他服务器,位于负载均衡器后面,仅用于读取.我不是LDAP管理员,所以我不知道这是否是标准程序.
您正在描述一个非常常见的问题。
解决这个问题的常规方法是使用乐观并发控制。
如果这听起来像是晦涩难懂的书,其实不然。这是一个非常简单的想法。该术语的并发部分指的是记录数据发生更新,并且这些更新是同时发生的。可能有很多作家。(您的情况是一种退化情况,其中单个作家是问题的根源,但基本思想相同)。我稍后会谈到乐观的部分。
问题
当有多个写入器时,两个更新的读+写部分可能会交错。假设您有 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
Run Code Online (Sandbox Code Playgroud)
此类查询可能会检索数据库中给定日期、月份或其他时间的所有博客文章。
通过简单的乐观并发,您可以使用如下 SQL更新一行:
UPDATE blogposts Set Headline = @NewHeadline, LastUpdated = @NewLastUpdated
WHERE ix=@ix AND LastUpdated = @PriorLastUpdated
Run Code Online (Sandbox Code Playgroud)
仅当索引匹配(并且我们假设这是主键)并且 LastUpdated 字段与读取数据时的字段相同时,更新才会发生。另请注意,您必须确保每次更新该行时都更新 LastUpdated 字段。
更严格的更新可能会坚持认为没有任何列被更新。在这种情况下根本没有时间戳。像这样的东西:
UPDATE Table1 Set Col1 = @NewCol1Value,
Set Col2 = @NewCol2Value,
Set Col3 = @NewCol3Value
WHERE Col1 = @OldCol1Value AND
Col2 = @OldCol2Value AND
Col3 = @OldCol3Value
Run Code Online (Sandbox Code Playgroud)
为什么叫“乐观”呢?
OCC 用作保持数据库锁的替代方案,这是保持数据一致性的严厉方法。数据库锁可能会阻止任何人在持有数据库行时读取或更新该行。这显然会对性能产生巨大影响。因此,OCC 放松了这一点,并“乐观”地行事,假设当需要更新时,表中的数据不会同时更新。但当然这并不是盲目乐观——你必须在更新之前检查一下。
在实践中使用乐观 Cancurrency
你说你使用.NET。我不知道您是否使用数据集进行数据访问,强类型或其他方式。但是 .NET DataSet,或者特别是 DataAdapter,包含对 OCC 的内置支持。您可以为任何 DataAdapter 指定并手动编码 UpdateCommand,这就是您可以插入一致性检查的地方。这在 Visual Studio 设计体验中也是可能的。
(来源:asp.net)
如果出现违规,更新将返回一个结果,显示已更新 0 行。您可以在DataAdapter.RowUpdated 事件中检查这一点。(请注意,在 ADO.NET 模型中,每种数据库都有一个不同的 DataAdapter。其中的链接是 SqlDataAdapter,它与 SQL Server 一起使用,但对于不同的数据源,您需要不同的 DA。)
在 RowUpdated 事件中,您可以检查受影响的行数,然后在计数为零时采取某些操作。
概括
在写入更新之前,请验证数据库的内容是否未更改。这称为乐观并发控制。
其他链接: