我有一堆'收集者'枚举来自各种设备的几个数据(cpu-type,ram等)。 这些收集器每天运行一次,并使用简单的Web服务发布其数据。 然后,这个Web服务会触发一些方法,使用EF将这些数据导入我们的sql-db。
以下是获取数据后的部分:
var importPayload = JsonConvert.DeserializeObject(payload.ToString(),
new JsonSerializerSettings {TypeNameHandling = TypeNameHandling.Auto});
//Load off the payload to another thread to quickly return to the caller.
TypeSwitch.On(importPayload)
.Case((List<IDeviceType> x) =>
{
Task.Run(() => { DeviceType.Update(x, sourceName, sourceId, persistInDatabase); });
})
.Case((List<ICpu> x) =>
{
Task.Run(() => { Cpu.Update(x, sourceName, sourceId, persistInDatabase); });
})
.Case((List<IMemory> x) =>
{
Task.Run(() => { Memory.Update(x, sourceName, sourceId, persistInDatabase); });
})
以下是更新sql-db中数据的部分。
1。设备类型:
public static class DeviceType
{
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
private static readonly DateTime Timestamp = DateTime.Now;
internal static void Update(IReadOnlyCollection<IDeviceType> payload, string sourceName, int sourceId,
bool persistInDatabase)
{
if (payload == null || !payload.Any())
{
Log.Error($"Collector '{sourceName}': Payload is 'NULL' or empty. Cannot import payload into CMDB.");
return;
}
using (var context = CMDBEntities.GetCMDBContext(persistInDatabase, sourceName))
{
foreach (var item in payload.Where(a => !string.IsNullOrEmpty(a.Name)))
Update(context, item.HostName, item.Name, sourceId, sourceName);
context.SaveChanges();
}
}
private static void Update(CMDBEntities context, string hostName, string systemType, int sourceId,
string sourceName)
{
Log.Debug($"Collector '{sourceName}', Item '{hostName}': Lookup item in CMDB...");
var ci = context.CMDB_ci.FirstOrDefault(
a => a.name.ToLower().Equals(hostName.ToLower()) &&
a.ci_c_status_id == CiStatus.Active && a.delflag == 0);
if (ci == null)
{
Log.Debug($"Collector '{sourceName}', Item '{hostName}': Item was not found in CMDB. Skipping...");
return;
}
var deviceType =
context.CMDB_ci_c_devicetype.FirstOrDefault(
a => a.name.ToLower().Equals(systemType.ToLower()));
if (deviceType != null)
{
var sourceDeviceTypeId = deviceType.ci_c_devicetype_id;
var cmdbDeviceType = ci.CMDB_ci_devicetype ??
(ci.CMDB_ci_devicetype = new CMDB_ci_devicetype());
var cmdbDeviceTypeId = ci.CMDB_ci_devicetype.ci_c_devicetype_id;
if (cmdbDeviceTypeId != sourceDeviceTypeId)
{
cmdbDeviceType.ci_c_devicetype_id = sourceDeviceTypeId;
cmdbDeviceType.LastModBy = sourceName;
cmdbDeviceType.LastModDateTime = Timestamp;
ci.LastModBy = sourceName;
ci.LastModDateTime = Timestamp;
Log.Debug(
$"Collector '{sourceName}', CI '{ci.name}': Device-Type has changed.");
}
}
}
}
2。 CPU:
public static class Cpu
{
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
private static readonly DateTime Timestamp = DateTime.Now;
internal static void Update(IReadOnlyCollection<ICpu> payload, string sourceName, int sourceId,
bool persistInDatabase)
{
if (payload == null || !payload.Any())
{
Log.Error($"Collector '{sourceName}': Payload is 'NULL' or empty. Cannot import payload into CMDB.");
return;
}
using (var context = CMDBEntities.GetCMDBContext(persistInDatabase, sourceName))
{
foreach (var item in payload)
Update(context, item.HostName, item.Name, item.SpeedInMhz, item.Count, item.Cores, sourceId,
sourceName);
context.SaveChanges();
}
}
private static void Update(CMDBEntities context, string itemName, string cpuName, int? cpuSpeed,
int? cpuCount,
int? cpuCoresPerCpu, int dataSourceId, string sourceName)
{
Log.Debug($"Collector '{sourceName}', Item '{itemName}': Lookup item in CMDB...");
var ci = context.CMDB_ci.FirstOrDefault(
a => a.name.ToLower().Equals(itemName.ToLower()) &&
a.ci_c_status_id == CiStatus.Active && a.delflag == 0 &&
(a.ci_c_type_id == CiType.Server || a.ci_c_type_id == CiType.Workstation));
if (ci == null)
{
Log.Debug($"Collector '{sourceName}', Item '{itemName}': Item was not found in CMDB. Skipping...");
return;
}
var deviceType = ci.CMDB_ci_devicetype ??
(ci.CMDB_ci_devicetype = new CMDB_ci_devicetype{LastModBy = sourceName, LastModDateTime = Timestamp});
InsertOrUpdateCpuEntry(context, deviceType, ci, cpuName, cpuSpeed, cpuCount, cpuCoresPerCpu,
sourceName);
}
private static void InsertOrUpdateCpuEntry(CMDBEntities context, CMDB_ci_devicetype deviceType, CMDB_ci ci,
string cpuName, int? cpuSpeed, int? cpuCount, int? cpuCoresPerCpu, string lastModBy)
{
if (!string.IsNullOrEmpty(cpuName))
{
var cpu = GetOrCreateCpuTypeEntry(context, ci, cpuName, lastModBy);
deviceType.ci_c_hardware_cpu_id = cpu.ci_c_hardware_cpu_id;
}
if (cpuCount != null)
deviceType.cpuCount = cpuCount;
if (cpuCoresPerCpu != null)
deviceType.cpuCoresPerCpu = cpuCoresPerCpu;
if (cpuSpeed != null)
deviceType.cpuSpeed = cpuSpeed;
}
private static CMDB_ci_c_hardware_cpu GetOrCreateCpuTypeEntry(CMDBEntities context, CMDB_ci ci,
string cpuName,
string sourceName)
{
var cpu =
context.CMDB_ci_c_hardware_cpu.FirstOrDefault(
a => a.value.ToLower().Equals(cpuName.ToLower()));
if (cpu != null) return cpu;
//Create new entry using it's own context.
using (var cpuContext = CMDBEntities.GetCMDBContext())
{
if (ci.ci_c_type_id != null)
{
var newCpu = cpuContext.CMDB_ci_c_hardware_cpu.Add(new CMDB_ci_c_hardware_cpu
{
ci_c_type_id = (CiType) ci.ci_c_type_id,
value = cpuName,
alias = cpuName,
delflag = 0,
deprecated = false,
LastModBy = sourceName,
LastModDateTime = Timestamp
});
Log.Debug($"Collector '{sourceName}': Creating new CPU-Entry: '{0}'", cpuName);
cpuContext.SaveChanges();
return newCpu;
}
}
return null;
}
}
第3。 RAM:
public static class Memory
{
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
private static readonly DateTime Timestamp = DateTime.Now;
internal static void Update(IReadOnlyCollection<IMemory> payload, string sourceName, int sourceId,
bool persistInDatabase)
{
if (payload == null || !payload.Any())
{
Log.Error($"Collector '{sourceName}': Payload is 'NULL' or empty. Cannot import payload into CMDB.");
return;
}
using (var context = CMDBEntities.GetCMDBContext(persistInDatabase, sourceName))
{
foreach (var item in payload)
Update(context, item.HostName, item.SizeInGb, sourceId, sourceName);
context.SaveChanges();
}
}
private static void Update(CMDBEntities context, string itemName, long? ramSizeInGb, int dataSourceId,
string sourceName)
{
Log.Debug($"Collector '{sourceName}', Item '{itemName}': Lookup item in CMDB...");
var ci = context.CMDB_ci.FirstOrDefault(
a => a.name.ToLower().Equals(itemName.ToLower()) &&
a.ci_c_status_id == CiStatus.Active && a.delflag == 0 &&
(a.ci_c_type_id == CiType.Server || a.ci_c_type_id == CiType.Workstation));
if (ci == null)
{
Log.Debug($"Collector '{sourceName}', Item '{itemName}': Item was not found in CMDB. Skipping...");
return;
}
var deviceType = ci.CMDB_ci_devicetype ??
(ci.CMDB_ci_devicetype = new CMDB_ci_devicetype{LastModBy = sourceName, LastModDateTime = Timestamp});
InsertOrUpdateRamSizeEntry(deviceType, ramSizeInGb);
}
private static void InsertOrUpdateRamSizeEntry(CMDB_ci_devicetype deviceType, long? ramSize)
{
deviceType.ramSize = ramSize;
}
}
在极少数情况下,这些方法会同时尝试插入新的设备类型条目,因此EF会崩溃并显示错误消息:
2018-02-01 03:54:12.1435 | [错误] | [CMDB.Core.CMDBEntities.SaveChanges]&gt;&gt;&gt;源'CMDB-Collector Discovery(硬件(CPU))':提交对database.System.Data.Entity.Infrastructure.DbUpdateException的更改时发生一般错误更新条目时发生错误。有关详细信息,请参阅内部异常Int32 SaveChanges() UpdateException更新条目时发生错误。有关详细信息,请参阅内部异常Int32更新() SqlException违反PRIMARY KEY约束'PK_CMDB_ci_devicetype'。无法在对象'dbo.CMDB_ci_devicetype'中插入重复键。重复键值为(414002)。
我知道原因,但我不知道什么是避免此错误的最佳方法。我可以在每次修改后保存更改,但这会增加处理时间。 或者我可以在不同的任务上添加一些依赖项,以便webservices检查当前正在运行的更新方法并将下一个更新为队列。
谢谢!
更新表格规范:
CREATE TABLE [dbo].[CMDB_ci_devicetype](
[ci_devicetype_id] [bigint] IDENTITY(1,1) NOT NULL,
[ci_id] [bigint] NOT NULL,
[ci_c_devicetype_id] [bigint] NULL,
[ci_c_hardware_cpu_id] [bigint] NULL,
[cpuCount] [int] NULL,
[cpuCoresPerCpu] [int] NULL,
[cpuSpeed] [int] NULL,
[ramSize] [bigint] NULL,
[cpu_processor_type] [varchar](max) NULL,
[cpu_number_of_sockets] [varchar](max) NULL,
[cpu_populate_sockets] [varchar](max) NULL,
[cpu_core_per_populate_sockets] [varchar](max) NULL,
[cpu_core_per_server] [varchar](max) NULL,
[ram] [varchar](max) NULL,
[controller] [varchar](max) NULL,
[LastModBy] [varchar](max) NOT NULL,
[LastModDateTime] [datetime] NOT NULL,
CONSTRAINT [PK_CMDB_ci_devicetype] PRIMARY KEY CLUSTERED ( [ci_id] ASC )WITH(PAD_INDEX = OFF,STATISTICS_NORECOMPUTE = OFF,IGNORE_DUP_KEY = OFF,ALLOW_ROW_LOCKS = ON,ALLOW_PAGE_LOCKS = ON)ON [PRIMARY] )[主要] TEXTIMAGE_ON [主要]