如何获取导致 except 添加 IEnumerable 的差异?
我正在使用 Enumerable.Except 来检查 DataTable内存中的
与数据库中的表同步。
背景是:这个DataTable
和其他经常使用的表都存储在WebApplication的Cache
中。但与此同时,我确信这不是一个好方法,因为它是难以重现/调试的令人讨厌的错误的根源。
因此,我创建了一个函数来检查数据库和内存是否同步,否则将创建错误日志。这非常有效。如果内存中有一行不在数据库中,则该行将显示在“数据库差异”下方,反之亦然。但是,如果两个数据源中都存在行(PK idRMA
)并且某些值不同,则日志将在两个版本中包含该行(位于“数据库差异”和“数据库差异”< /em>)。乍一看并不容易看出差异。
问:是否可以只选择导致Except
认为第一个序列不在第二个序列的属性?
这是完整的函数(第一行是相关的):
Public Shared Sub CheckRmaMemoryInSyncWithDB()
Dim inSyncText As String
Dim color As Drawing.Color
Dim isInSync As Boolean
Dim daRma As New ERPModel.dsRMATableAdapters.RMATableAdapter
Dim tblRma = daRma.GetData()
Dim memory = (From rma In dsRMA.RMA
Where Not rma.IsfiChargeNull
Select rma.IdRMA, rma.fiCharge, rma.IMEI, rma.RMA_Number, rma.ModelID, rma.fiCustomer, SI_DPY = If(rma.IsSI_DPYNull, String.Empty, rma.SI_DPY), rma.fiStatus, rma.HasErrors).ToList
Dim dataBase = (From rma In tblRma
Where Not rma.IsfiChargeNull
Select rma.IdRMA, rma.fiCharge, rma.IMEI, rma.RMA_Number, rma.ModelID, rma.fiCustomer, SI_DPY = If(rma.IsSI_DPYNull, String.Empty, rma.SI_DPY), rma.fiStatus, rma.HasErrors).ToList
Dim notInDatabase = memory.Except(dataBase).ToList
Dim notInMemory= dataBase.Except(memory).ToList
If notInMemory.Any OrElse notInDatabase.Any Then
isInSync = False
inSyncText = "Database and Memory are NOT in Sync! Event-Log created."
Dim ex As New Exception("Database and Memory are NOT in Sync!")
Dim errInfo = String.Empty
If notInMemory.Any Then
errInfo &= "Difference in memory:" & Environment.NewLine
Dim memoryInfo = String.Join(Environment.NewLine, notInMemory)
errInfo &= memoryInfo & Environment.NewLine
End If
If notInDatabase.Any Then
errInfo &= "Difference in database:" & Environment.NewLine
Dim databaseInfo = String.Join(Environment.NewLine, notInDatabase)
errInfo &= databaseInfo & Environment.NewLine
End If
ErrorLog.WriteError(ex, errInfo)
Else
isInSync = True
inSyncText = "Database and Memory are in Sync, all RMA's are identical in both."
End If
Dim master = DirectCast(DirectCast(HttpContext.Current.CurrentHandler, Page).Master, ERPMaster)
color = If(isInSync, Drawing.Color.Green, Drawing.Color.Red)
master.showStatusMessage(inSyncText, True, color)
End Sub
注意:我可以将所有属性相互比较,但我想知道是否有更简单的(LINQ)方法。基本上我正在寻找一种 LINQ 方式来仅获取匿名类型的两个序列的差异(因为我只选择 DataRow 的相关列进行比较)。
编辑:我假设我必须在主键(idRMA
)上加入两者,但我如何选择差异?如果连接未返回结果,则该 ID 不存在于第二个序列中,并且可以记录所有属性。但是如果第二个序列中有适当的记录,我如何选择不同的属性?
Dim diff = From rmaMem In notInMemory
Join rmaDB In notInDatabase
On rmaMem.IdRMA Equals rmaDB.IdRMA
Select ...... ' i only want to select the properties in `rmaMem` that are different in `rmaDB` '
i'm using Enumerable.Except to check if a DataTable
in memory is in sync with the table in database.
The background is: this DataTable
and other frequently used tables are stored in the Cache
of a WebApplication. But meanwhile i'm convinced that this is not a good approach because it's a source for nasty errors that are difficult to reproduce/debug.
Therefore i've created a function that checks if database and memory are in sync, otherwise an error-log will be created. This works perfectly. If there is a row in memory that is not in database, this row will be shown below "Difference in database", the same applies in reverse. But if rows exist in both datasources(the PK idRMA
) and some values differ, the log will contain this row in two versions(below "Difference in database" and "Difference in database"). It is not easy to see the differences on the first sight.
Q: Is it possible to select only the properties that caused Except
to think that first sequence is not in second?
This is the the complete function(the first lines are relevant):
Public Shared Sub CheckRmaMemoryInSyncWithDB()
Dim inSyncText As String
Dim color As Drawing.Color
Dim isInSync As Boolean
Dim daRma As New ERPModel.dsRMATableAdapters.RMATableAdapter
Dim tblRma = daRma.GetData()
Dim memory = (From rma In dsRMA.RMA
Where Not rma.IsfiChargeNull
Select rma.IdRMA, rma.fiCharge, rma.IMEI, rma.RMA_Number, rma.ModelID, rma.fiCustomer, SI_DPY = If(rma.IsSI_DPYNull, String.Empty, rma.SI_DPY), rma.fiStatus, rma.HasErrors).ToList
Dim dataBase = (From rma In tblRma
Where Not rma.IsfiChargeNull
Select rma.IdRMA, rma.fiCharge, rma.IMEI, rma.RMA_Number, rma.ModelID, rma.fiCustomer, SI_DPY = If(rma.IsSI_DPYNull, String.Empty, rma.SI_DPY), rma.fiStatus, rma.HasErrors).ToList
Dim notInDatabase = memory.Except(dataBase).ToList
Dim notInMemory= dataBase.Except(memory).ToList
If notInMemory.Any OrElse notInDatabase.Any Then
isInSync = False
inSyncText = "Database and Memory are NOT in Sync! Event-Log created."
Dim ex As New Exception("Database and Memory are NOT in Sync!")
Dim errInfo = String.Empty
If notInMemory.Any Then
errInfo &= "Difference in memory:" & Environment.NewLine
Dim memoryInfo = String.Join(Environment.NewLine, notInMemory)
errInfo &= memoryInfo & Environment.NewLine
End If
If notInDatabase.Any Then
errInfo &= "Difference in database:" & Environment.NewLine
Dim databaseInfo = String.Join(Environment.NewLine, notInDatabase)
errInfo &= databaseInfo & Environment.NewLine
End If
ErrorLog.WriteError(ex, errInfo)
Else
isInSync = True
inSyncText = "Database and Memory are in Sync, all RMA's are identical in both."
End If
Dim master = DirectCast(DirectCast(HttpContext.Current.CurrentHandler, Page).Master, ERPMaster)
color = If(isInSync, Drawing.Color.Green, Drawing.Color.Red)
master.showStatusMessage(inSyncText, True, color)
End Sub
Note: I could compare all properties with each other, but i wonder if there is an easier(LINQ) way. Basically i'm looking for a LINQ-way to get only the differences of two Sequences of anonymous types(because i'm only selecting relevant columns of DataRow for this comparison).
Edit: I assume that i have to join both on the primarykey(idRMA
), but how can i select the differences? If the join does not return a result, the ID does not exist in second sequence and it's ok to log all properties. But if there is an appropriate record in second sequence, how can i select the properties that are different?
Dim diff = From rmaMem In notInMemory
Join rmaDB In notInDatabase
On rmaMem.IdRMA Equals rmaDB.IdRMA
Select ...... ' i only want to select the properties in `rmaMem` that are different in `rmaDB` '
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
只要没有人知道更好的方法,我就会继续遵循笨拙的方法。只需再次比较它们并仅显示不同的值,否则“相等”。
生成以下示例性输出:
顺便说一句,这种看似不一致的情况很容易被强迫。在 VisualStudio(Cassini)中与生产服务器并行启动应用程序,并删除/创建一些测试记录或更改一些值。
I stay with following clumsy approach as long as nobody knows a better way. Simply compare them again and show only the values that are different otherwise "equal".
Generates following exemplary output:
By the way, this seeming inconsistency was easy to force. Started the application within VisualStudio(Cassini) parallel to the production server and delete/create some test records or change some values here and there.
这没有回答您的问题,因此如果没有帮助,请否决,但您可能需要更多地考虑您的设计。
这是一个危险信号。
DataTable
不是线程安全的,因此您可能永远不应该修改Cache
中的数据表。在没有同步的情况下这样做肯定会给您带来难以调试的令人讨厌的错误。即使正确实现了同步,我也会认为这样的设计很脆弱,因为它可能很容易被不太熟练的维护程序员破坏。另一方面,如果
Cache
中的 DataTable 是只读的,我建议您不要将其与从数据库加载的数据进行比较,而只需将其替换为数据库中的数据 -即定期刷新缓存。您的应用程序设计应该预料到有时可能会在缓存中找到不“新鲜”的数据。This is not answering your question, so downvote away if it's unhelpful, but you might want to think some more about your design.
This is a red flag. A
DataTable
is not thread-safe, so you should probably never modify one that is inCache
. Doing so without synchronization will certainly give you nasty errors that are difficult to debug. And even if synchronization is properly implemented, I'd consider such a design fragile, as it may be easily broken down the line by a less skilled maintenance programmer.On the other hand, if the DataTable in
Cache
is read-only, I'd suggest that, rather than comparing it with data loaded from the database, you simply replace it by the data from the database - i.e. refresh the cache periodically. And your application design should expect that it may sometimes find data in the Cache which is not "fresh".我认为您需要创建一个自定义相等比较器,如此处所述。最终,您必须以某种方式比较属性值,并且由于您使用的是
Except()
,因此您可以保持一致并创建自定义比较器。除此之外,您可以编写一个单独的函数来为您完成这项工作。只是您喜欢什么才重要。
编辑:
这是非常粗糙的代码,但希望可以帮助您:
我不是反射专家,所以如果其他人可以对我的代码发表评论,那就太好了。
最终,我不确定缓存数据是最好的方法。您可能需要考虑将信息存储在数据库中。缓存的问题在于,网站随时可能被回收(或重新启动),并且您可能会丢失内存中的数据。
将数据存储在数据库中的另一个好处是,您可以直接在数据库上运行查询,以了解哪些属性已更改。在你看来,这就是我要做的 - 在数据库中创建一个表来存储你在缓存中的信息。
I think you will need to create a custom equality comparer as described here. Ultimately you will have to compare the property values somehow and since you're using
Except()
you could keep things consistent and create a custom comparer.Other then that, you could write a separate function to do the work for you. It's just what you prefer that matters.
EDIT:
This is very rough code, but hopefully might help you out:
I'm not a reflection expert, so if other's could comment on my code, then that would be great.
Ultimately, I'm not sure that caching data is the best method here. You might want to consider storing the information in a database instead. The issue with caching is that, at any moment, the website might get recycled (or restarted) and you can loose your in-memory data.
The other plus of having the data in a database is that you can run a query directly on the database in order to know which properties have changed. In your shoes this is what I'd do - create a table in the database to store the information you have in Cache.