Hi Aaron, I tried exactly as you said and a couple of other things to no avail. However, on closer inspection I noticed your entity history tables were full of non-nullable UserId's. I changed this line to force UserId = 1 as the standard host admin:
using (_abpSession.Use(tenantId, 1))
{...
<ins>AbpEntityChangeSets</ins> That now gives me a record in AbpEntityChangeSets with a null value in BrowserInfo, ClientIpAddress, _ClientName_and Reason(as can be expected). I think I should be able to populate the Reason column using IEntityChangeSetReasonProvider, I'm not sure how I could insert values for ClientIpAddress and ClientName, which could be useful. <ins>AbpEntityChanges</ins> I have a full set of data in AbpEntityChanges which is great. <ins>AbpEntityPropertyChanges</ins> Herein lies the problem. The AbpEntityPropertyChanges has not been populated at all which kind of makes the other two entries pointless in that we know something has changed but we don't know what.
Can you help me track the problem a little bit further? Sorry to be a pain but hopefully the above should give a few pointers?
Aaron, Forever in your debt. I will take a look at this later, at least now I can see it can be done. Now it's a matter of finding out how to do it! Thanks for your help again, it really is much appreciated. Cheers, Bob
That's a bit like telling me to replace the wimwam on the universal transmogrifier. Can you point me to the github code where the save command is?
Hi Aaron, Someone mentioned table bloat after running Entity History for a while on Azure. It would be nice to cut into the code somewhere and serialise the EntityChangeSet along with the related EntityChanges and EntitypropertyChanges and then simply throw them into a document database. After all, the data are immutable and, as such, are perfect for this functionality. Of course it would also mean cutting into code to deserialise but I don't think that would be problem. I will come back to this later, where should I start? Cheers, Bob
Hi Aaron, I think you overestimate my programming skills - I wouldn't have a clue how to do that! I go to an initial trial later this week for a fortnight or so so I would like to have some idea of what to do because the audit trail is a major USP, what with GDPR and all that. Is your source code embedded in a DLL or can I follow it through by placing a break point in Zero somewhere?
Hi Aaron, Any thoughts or pointers on this?
Thanks for staying with me Aaron, here's the full method which is a work in progress.
[UnitOfWork]
private DataProviderUpdateTotals UpdateEntities(List<DataProviderEntityDto> entities, int tenantId)
{
var totals = new DataProviderUpdateTotals(0, 0, 0);
if (entities != null)
{
using (_abpSession.Use(tenantId, null))
{
foreach (var entity in entities)
{
var dataProviderEntityAttributes = JsonConvert.DeserializeObject<Dictionary<string, string>>(entity.ExtensionData);
var dataProviderId = dataProviderEntityAttributes
.Where(m => m.Key == "DataProviderId")
.Select(m => m.Value)
.FirstOrDefault();
//error exceptions due to duplicatedResidentId's in ResiData
var ncEntityDataProviderIdXRef = new NcEntityDataProviderIdXRef();
var ncEntity = new NcEntity.NcEntity();
try
{
ncEntityDataProviderIdXRef = _ncEntityDataProviderIdXRefRepository.FirstOrDefault(m => m.DataProviderId == Convert.ToInt32(dataProviderId));
if (ncEntityDataProviderIdXRef == null)
{
//add new entity...
var organizationUnitId = _organizationUnitAttributeRepository.FirstOrDefault(m => m.Value == entity.OrganisationalUnitXRef).OrganizationUnitId;
CreateAndSaveNcEntity(entity, tenantId, organizationUnitId).GetAwaiter().GetResult();
totals.EntitiesAdded++;
break;
}
}
catch (Exception ex)
{
Logger.Error("Error in ncEntityDataProviderIdXRef during DataProviderAPIProvider.UpdateEntities, values: TenantId; " + tenantId.ToString() + " DataProviderId;" + dataProviderId.ToString(), ex);
break;
}
try
{
ncEntity = _ncEntityRepository.Get(ncEntityDataProviderIdXRef.NcEntityId);
}
catch (Exception ex)
{
Logger.Error("Duplicate in ncEntity during DataProviderAPIProvider.UpdateEntities, values: TenantId; " + tenantId.ToString() + " DataProviderId;" + dataProviderId.ToString(), ex);
break;
}
var systemOrganizationUnit = _organizationUnitAttributeRepository.FirstOrDefault(m => m.OrganizationUnitId == ncEntity.OrganizationUnitId).Value;
if (ncEntity.DisplayName != entity.DisplayName || ncEntity.ExtensionData != entity.ExtensionData || entity.OrganisationalUnitXRef != systemOrganizationUnit)
{
ncEntity.DisplayName = entity.DisplayName;
ncEntity.ExtensionData = entity.ExtensionData;
totals.EntitiesUpdated++;
if (entity.OrganisationalUnitXRef != systemOrganizationUnit)
{
var newEntityOrganizationUnit = _organizationUnitAttributeRepository.FirstOrDefault(m => m.Value == entity.OrganisationalUnitXRef).OrganizationUnitId;
if (newEntityOrganizationUnit != 0)
{
ncEntity.OrganizationUnitId = newEntityOrganizationUnit;
}
else //TODO
{
//if home doesn't exist create it
//if location doesn't exist create it
//add the entity to the new location
//ncEntity.OrganizationUnitId = newEntityOrganizationUnit;
}
totals.EntitiesMoved++;
}
_ncEntityRepository.Update(ncEntity);
}
}
}
}
return totals;
}
Hi Aaron, Thanks for the reply. entity is returned from my data provider as a list and then and serialized as the following Dto:
public class DataProviderEntityDto : EntityDto<Guid>
{
public string DisplayName { get; set; }
public string ExtensionData { get; set; }
}
NcEntity is a domain entity defined as below:
[Table("NcEntity")]
public class NcEntity : FullAuditedEntity<Guid>, IMustHaveTenant, IMayHaveOrganizationUnit, IExtendableObject
{
public int TenantId { get; set; }
public long? OrganizationUnitId { get; set; }
[Required]
[StringLength(DbConsts.MaxNameLength)]
public virtual string DisplayName { get; set; }
public virtual string ExtensionData { get; set; }
}
My DataProviderAPIService reads the third party database and returns a list of entities. If either value (DisplayName or ExtensionData) in the entity is different to the relevant value in the NcEntity domain object the system updates NcEntity. NcEntity is a fully audited entity and I'm not sure why the history is not captured. I am not sure what you mean by >If ncEntity was untracked, then isModified = !(OriginalValue.Equals(CurrentValue)) would be false. . Thanks for your help so far.
@alper - brilliant, thanks a lot. Little snippets like this should really be placed in a blog - "How do I...". Your information was detailed and allowed me to do exactly what I wanted to do but I really do think it's time to implement a blog where all contributors can add articles on implementing little pieces of functionality like this.
@alper, that is just what I needed. I will go through the links and try implement. If there is a short and sweet solution I will post it here.