我正在使用Enumerable.Except检查内存中的DataTable
是否与数据库中的表同步。
背景是:此DataTable
和其他常用表存储在WebApplication的Cache
中。但同时我确信这不是一个好方法,因为它是难以重现/调试的令人讨厌的错误的来源。
因此我创建了一个检查数据库和内存是否同步的函数,否则将创建错误日志。这非常有效。如果内存中有一行不在数据库中,则该行将显示在“数据库中的差异”下面,反之亦然。但是如果两个数据源中存在行(PK idRMA
)并且某些值不同,则日志将包含两个版本的该行(在“数据库中的差异”和“数据库中的差异”下面) )。第一眼看到差异并不容易。
问:是否可以仅选择导致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` '
答案 0 :(得分:2)
我认为您需要按照here所述创建自定义相等比较器。最终,您必须以某种方式比较属性值,因为您使用Except()
,您可以保持一致并创建自定义比较器。
除此之外,您可以编写一个单独的函数来为您完成工作。这就是你喜欢的重要事项。
编辑:
这是非常粗略的代码,但希望可以帮助你:
class Program
{
static void Main(string[] args)
{
var p1 = new Person();
p1.Name = "A";
p1.Age = 23;
var p2 = new Person();
p2.Name = "Ralph";
p2.Age = 56;
Diff(p1, p2);
}
private static void Diff(Person p1, Person p2)
{
var prop = p1.GetType().GetProperties();
var notMatchingProperties = new List<string>();
foreach(var p in prop)
{
var propName = p.Name;
var propValue1 = p1.GetType().GetProperty(propName).GetValue(p1, null);
var propValue2 = p2.GetType().GetProperty(propName).GetValue(p2, null);
if (!propValue1.Equals(propValue2))
{
notMatchingProperties.Add(propName);
}
}
foreach (var notMatchingProperty in notMatchingProperties)
{
Console.WriteLine(notMatchingProperty);
}
}
}
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
我不是反思专家,所以如果其他人可以评论我的代码,那就太棒了。
最终,我不确定缓存数据是否是最好的方法。您可能需要考虑将信息存储在数据库中。缓存的问题是,在任何时候,网站都可能被回收(或重新启动),您可能会丢失内存中的数据。
在数据库中拥有数据的另一个好处是,您可以直接在数据库上运行查询,以便了解哪些属性已更改。在你的鞋子里,这就是我要做的 - 在数据库中创建一个表来存储你在Cache中的信息。
答案 1 :(得分:2)
这不是在回答你的问题,所以如果没有帮助的话就会失败,但你可能想要更多地考虑一下你的设计。
此DataTable和其他常用表存储在WebApplication的Cache中。但同时我确信这不是一个好方法,因为它是难以重现/调试的令人讨厌的错误的来源。
这是一面红旗。 DataTable
不是线程安全的,因此您可能永远不应修改Cache
中的一个。{1}}。没有同步这样做肯定会给你带来难以调试的令人讨厌的错误。即使同步正确实施,我也会认为这样的设计很脆弱,因为它可能很容易被一个技能较低的维护程序员分解。
另一方面,如果Cache
中的DataTable是只读的,我建议不要将它与从数据库加载的数据进行比较,而只需用数据库中的数据替换它 - 即定期刷新缓存。并且您的应用程序设计应该期望它有时可以在Cache中找到不是“新鲜”的数据。
答案 2 :(得分:0)
只要没有人知道更好的方法,我就会遵循笨拙的方法。只需再次比较它们,只显示不同的值,否则为“相等”。
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 memoryDiff = memory.Except(dataBase).ToList
Dim databaseDiff = dataBase.Except(memory).ToList
If memoryDiff.Any OrElse databaseDiff.Any Then
isInSync = False
inSyncText = "Database and Memory are NOT in Sync! Error-Log created."
Dim ex As New Exception("Database and Memory are NOT in Sync!")
Dim errInfo As String = String.Empty
'following joins both sequences to detect differences
'note: completely missing RMA's will be detected later
Dim diff = From rmaMem In memoryDiff
Join rmaDB In databaseDiff
On rmaMem.IdRMA Equals rmaDB.IdRMA
Select rmaDB.IdRMA _
, fiCharge = If(rmaMem.fiCharge = rmaDB.fiCharge, "equal", String.Format("{0}/{1}", rmaMem.fiCharge, rmaDB.fiCharge)) _
, IMEI = If(rmaMem.IMEI = rmaDB.IMEI, "equal", String.Format("{0}/{1}", rmaMem.IMEI, rmaDB.IMEI)) _
, RMA_Number = If(rmaMem.RMA_Number = rmaDB.RMA_Number, "equal", String.Format("{0}/{1}", rmaMem.RMA_Number, rmaDB.RMA_Number)) _
, ModelID = If(rmaMem.ModelID = rmaDB.ModelID, "equal", String.Format("{0}/{1}", rmaMem.ModelID, rmaDB.ModelID)) _
, fiCustomer = If(rmaMem.fiCustomer = rmaDB.fiCustomer, "equal", String.Format("{0}/{1}", rmaMem.fiCustomer, rmaDB.fiCustomer)) _
, SI_DPY = If(rmaMem.SI_DPY = rmaDB.SI_DPY, "equal", String.Format("{0}/{1}", rmaMem.SI_DPY, rmaDB.SI_DPY)) _
, fiStatus = If(rmaMem.fiStatus = rmaDB.fiStatus, "equal", String.Format("{0}/{1}", rmaMem.fiStatus, rmaDB.fiStatus)) _
, HasErrors = If(rmaMem.HasErrors = rmaDB.HasErrors, "equal", String.Format("{0}/{1}", rmaMem.HasErrors, rmaDB.HasErrors))
If diff.Any Then
errInfo &= "Differences(memory/database):" & Environment.NewLine
errInfo &= String.Join(Environment.NewLine, diff) & Environment.NewLine
End If
Dim memoryIDs = (From rma In memory
Select rma.IdRMA).ToList
Dim databaseIDs = (From rma In dataBase
Select rma.IdRMA).ToList
Dim missingInMemory = databaseIDs.Except(memoryIDs)
Dim missingInDB = memoryIDs.Except(databaseIDs)
If missingInMemory.Any Then
Dim rmaInfo = From rma In dataBase
Join idRMA In missingInMemory
On idRMA Equals rma.IdRMA
Select rma
errInfo &= "Missing RMA's in memory:" & Environment.NewLine
errInfo &= String.Join(Environment.NewLine, rmaInfo) & Environment.NewLine
End If
If missingInDB.Any Then
Dim rmaInfo = From rma In memory
Join idRMA In missingInDB
On idRMA Equals rma.IdRMA
Select rma
errInfo &= "Missing RMA's in database:" & Environment.NewLine
errInfo &= String.Join(Environment.NewLine, rmaInfo)
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
生成以下示例输出:
Differences(memory/database):
{ IdRMA = 25878, fiCharge = equal, IMEI = equal, RMA_Number = equal, ModelID = equal, fiCustomer = equal, SI_DPY = 1207-1104/1206-1105, fiStatus = equal, HasErrors = equal }
Missing RMA's in database:
{ IdRMA = 25882, fiCharge = 1416, IMEI = 004401076362330, RMA_Number = RMA0022725, ModelID = 449, fiCustomer = 49, SI_DPY = , fiStatus = 17, HasErrors = False }
{ IdRMA = 25881, fiCharge = 1416, IMEI = 359419030480338, RMA_Number = RMA0022724, ModelID = 758, fiCustomer = 49, SI_DPY = 1207-1124, fiStatus = 18, HasErrors = False }
顺便说一下,这种看似不一致很容易被逼迫。在VisualStudio(Cassini)中启动与生产服务器并行的应用程序并删除/创建一些测试记录或在此处更改一些值。