Base solution for your next web application
Open Closed

Entity History design #5364


User avatar
0
BobIngham created

One for Aaron, I think. First, when an entity has a guid for primary key why do we place quotation marks around the string which is entered into the AbpEntityChanges.EntityId field? This causes all kinds of problems when trying to join data. Is it deliberate by design and if so, why? More importantly, if I have a parent-child table relationship and the child is fully audited and subject to entity history how do I retrieve all entity changes for children of the parent? I am trying to return entity changes for a given entity (say, Customer) and for all entities related as children to the same entity (say, CustomerOrder etc). Given the current design of the system I am not sure if that is possible. I have noticed that when a child table is updated the first entry in the AbpEntityChanges for the entity change set contains a reference to the parent table with no property change collection. After this entry we have the actual property changes to the child table. Is there any way this design can be used to return a entity history for a given entity including all child tables?

BTW - I am really enjoying working with the entity history system, thanks for all your efforts.


12 Answer(s)
  • User Avatar
    0
    aaron created
    Support Team

    when an entity has a guid for primary key why do we place quotation marks around the string which is entered into the AbpEntityChanges.EntityId field? Is it deliberate by design and if so, why?

    This is due to the use of ToJsonString() to standardize conversion. As .NET represents Guid with dashes (it's also the default ToString() value), its JSON-compliant value has quote marks.

    if I have a parent-child table relationship and the child is fully audited and subject to entity history how do I retrieve all entity changes for children of the parent?

    1. Get child IDs normally.
    2. Query EntityChanges by EntityId column.

    I have noticed that when a child table is updated the first entry in the AbpEntityChanges for the entity change set contains a reference to the parent table with no property change collection. After this entry we have the actual property changes to the child table. Is there any way this design can be used to return a entity history for a given entity including all child tables?

    That's if you update the children via the parent. Entity History only stores enough to identify an entity and its changes, so it's not designed for that.

  • User Avatar
    0
    BobIngham created

    Hi Aaron, Thanks for getting back. Ok, I'm good with all this it's a fine piece of work and a great addition to functionality. Just to be clear, the only way to get a set of entity history changes for child tables I need to get the child table Id's from the database and then, for each Id found, search the AbpEntityChanges table using the EntityTypeFullName and the EntityId. Is this how you envisage it working?

    That's if you update the children via the parent.

    What does this mean?

  • User Avatar
    0
    aaron created
    Support Team

    Is this how you envisage it working?

    Yes.

    <cite>aaron: </cite> That's if you update the children via the parent. What does this mean?

    Show your method.

  • User Avatar
    0
    BobIngham created

    Not sure what good this will do....

    private async Task<DataProviderUpdateTotals> CreateCarePlan(List<CarePlanDto> carePlans, int tenantId, Guid entityId, DataProviderUpdateTotals totals)
    {
        foreach (var carePlan in carePlans)
        {
            carePlan.TenantId = tenantId;
            carePlan.NcEntityId = entityId;
    
            //if exists update...
            var currentCarePlan = await _ncCarePlanRepository.FirstOrDefaultAsync(m => m.NcEntityId == entityId && m.Subject == carePlan.Subject);
            if (currentCarePlan != null)
            {
                ObjectMapper.Map(carePlan, currentCarePlan);
                //await _ncCarePlanRepository.UpdateAsync(AutoMapper.Mapper.Map<NcCarePlan>(carePlan));
            }
            else //insert new
            {
                await _ncCarePlanRepository.InsertAsync(AutoMapper.Mapper.Map<NcCarePlan>(carePlan));
            }
            if (totals != null)
            {
                totals.CarePlanInsertions++;
            }
        }
        return totals;
    }
    
  • User Avatar
    0
    aaron created
    Support Team

    Where's the parent that is referenced in the EntityChange with no PropertyChange collection?

  • User Avatar
    0
    BobIngham created

    Hi Aaron, I'm not sure where you're going with this but the following entry is a typical example, represented as Json (the net effect is the same for SQL Server with the exception of the id and foreign key columns) from Mongodb. The change set contains changes to two care plan objects but there are four records in (the equivalent of) AbpEntityChanges. The first entry is for the entity to which the first set of care plan changes belong, there are no property changes. The second entry contains the changes to the care plan for the first entity. The third entry is for the entity to which the second set of care plan changes belong, there are no property changes. The fourth entry contains the changes to the care plan for the second entity. I hope this makes sense, I have no problems with the system as designed but I have work to do to iterate through the child tables of an entity table to find entity history data.

    {
      "_id": "5b45e34c5062b703444d63f5",
      "BrowserInfo": null,
      "ClientIpAddress": null,
      "ClientName": null,
      "CreationTime": "2018-07-11T11:00:28.309Z",
      "ExtensionData": null,
      "ImpersonatorTenantId": null,
      "ImpersonatorUserId": null,
      "Reason": null,
      "TenantId": 3,
      "UserId": "4",
      "EntityChanges": [
        {
          "ChangeTime": "2018-07-11T11:00:28.103Z",
          "ChangeType": 1,
          "EntityId": "\"5f12294d-b84a-4eb0-12fc-08d5d5fc588e\"",
          "EntityTypeFullName": "Nuagecare.NcEntity.NcEntity",
          "TenantId": 3,
          "PropertyChanges": []
        },
        {
          "ChangeTime": "2018-07-11T11:00:28.103Z",
          "ChangeType": 1,
          "EntityId": "18137",
          "EntityTypeFullName": "Nuagecare.NcEntity.NcCarePlan",
          "TenantId": 3,
          "PropertyChanges": [
            {
              "NewValue": "\"2018-07-11T00:00:00\"",
              "OriginalValue": "\"2018-06-12T00:00:00Z\"",
              "PropertyName": "CreatedDatetime",
              "PropertyTypeFullName": "System.DateTime",
              "TenantId": 3
            },
            {
              "NewValue": "\"2018-07-11T11:00:24.1526926Z\"",
              "OriginalValue": "\"2018-06-19T15:50:13.8613051Z\"",
              "PropertyName": "CreationTime",
              "PropertyTypeFullName": "System.DateTime",
              "TenantId": 3
            },
            {
              "NewValue": "\"Lindsey Smith\"",
              "OriginalValue": "\"Helen Joslin\"",
              "PropertyName": "Editor",
              "PropertyTypeFullName": "System.String",
              "TenantId": 3
            },
            {
              "NewValue": "\"Implemented by Lindsey Smith 11.07.18\"",
              "OriginalValue": "null",
              "PropertyName": "Evaluation",
              "PropertyTypeFullName": "System.String",
              "TenantId": 3
            },
            {
              "NewValue": "\"> My last Eye test was March 2018\\r\\n\\r\\n> I have cataracts and will need staff to contact the Gp if this becomes a problem.\\r\\n\\r\\n> I am not registered as blind\\r\\n\\r\\n> I wear glasses to help with my eyesight occasionally when reading and need staff to encourage me to wear these when I am reading.\\r\\n\\r\\n> I am not completely dependent on my glasses with every day activities.\\r\\n\\r\\n> I am able to clean and maintain my own glasses.\\r\\n\\r\\n> I have never had my hearing tested, staff to organise this f...",
              "OriginalValue": "\"Please refer to my A4 information sheet in my room and my pre-assessment document until my individual care plan is completed..\"",
              "PropertyName": "Prescription",
              "PropertyTypeFullName": "System.String",
              "TenantId": 3
            },
            {
              "NewValue": "\"2018-08-10T00:00:00\"",
              "OriginalValue": "\"2018-07-12T00:00:00Z\"",
              "PropertyName": "ReviewDatetime",
              "PropertyTypeFullName": "System.Nullable`1[[System.DateTime, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]",
              "TenantId": 3
            },
            {
              "NewValue": "\"> My last Eye test was March 2018\\r\\n\\r\\n> I have cataracts\\r\\n\\r\\n> I am not registered as blind\\r\\n\\r\\n> I wear glasses to help with my eyesight occasionally when reading.\\r\\n\\r\\n> I am not completely dependent on my glasses with every day activities.\\r\\n\\r\\n> I am able to clean and maintain my own glasses.\\r\\n\\r\\n> I have never had my hearing tested.\\r\\n\\r\\n> I have no known hearing impairments and I do not use any hearing aids.\"",
              "OriginalValue": "\"Please make sure you are aware of my sight and hearing needs by reading my pre-assessment document whilst I am awaiting my individual person-centered care plan which should be completed by day 5 of my admission\"",
              "PropertyName": "Situation",
              "PropertyTypeFullName": "System.String",
              "TenantId": 3
            },
            {
              "NewValue": "\"SIGHT AND HEARING                                                                         \"",
              "OriginalValue": "\"SIGHT AND HEARING                                                                                   \"",
              "PropertyName": "Subject",
              "PropertyTypeFullName": "System.String",
              "TenantId": 3
            }
          ]
        },
        {
          "ChangeTime": "2018-07-11T11:00:28.103Z",
          "ChangeType": 1,
          "EntityId": "\"7233cd97-7444-41be-1300-08d5d5fc588e\"",
          "EntityTypeFullName": "Nuagecare.NcEntity.NcEntity",
          "TenantId": 3,
          "PropertyChanges": []
        },
        {
          "ChangeTime": "2018-07-11T11:00:28.103Z",
          "ChangeType": 1,
          "EntityId": "18184",
          "EntityTypeFullName": "Nuagecare.NcEntity.NcCarePlan",
          "TenantId": 3,
          "PropertyChanges": [
            {
              "NewValue": "\"2018-07-11T00:00:00\"",
              "OriginalValue": "\"2018-07-10T00:00:00Z\"",
              "PropertyName": "CreatedDatetime",
              "PropertyTypeFullName": "System.DateTime",
              "TenantId": 3
            },
            {
              "NewValue": "\"2018-07-11T11:00:27.624242Z\"",
              "OriginalValue": "\"2018-07-10T20:31:18.5695605Z\"",
              "PropertyName": "CreationTime",
              "PropertyTypeFullName": "System.DateTime",
              "TenantId": 3
            },
            {
              "NewValue": "\"Ellen Smith\"",
              "OriginalValue": "\"Emma Howlett\"",
              "PropertyName": "Editor",
              "PropertyTypeFullName": "System.String",
              "TenantId": 3
            },
            {
              "NewValue": "\"2018-08-10T00:00:00\"",
              "OriginalValue": "\"2018-08-09T00:00:00Z\"",
              "PropertyName": "ReviewDatetime",
              "PropertyTypeFullName": "System.Nullable`1[[System.DateTime, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]",
              "TenantId": 3
            },
            {
              "NewValue": "\"Mobility\"",
              "OriginalValue": "\"Mobility                                                                                  \"",
              "PropertyName": "Subject",
              "PropertyTypeFullName": "System.String",
              "TenantId": 3
            }
          ]
        }
      ]
    }
    
  • User Avatar
    0
    aaron created
    Support Team

    That's odd. By "belong" do you mean just foreign key reference, or owned entities? Is the parent entity linked by a navigation property (and loaded) in the child entity?

  • User Avatar
    0
    BobIngham created

    Ah, now I see where you're going. You are correct in that the child entity has a navigation property to the parent which is by design to facilitate searches in Mongodb:

    namespace Nuagecare.NcEntity
    {
    	[Table("NcCarePlan")]
        public class NcCarePlan : FullAuditedEntity<long>, IMustHaveTenant
        {
    		public int TenantId { get; set; }
    
    		[Required]
    		public virtual string Subject { get; set; }
    		
    		[Required]
    		public virtual string Situation { get; set; }
    		
    		public virtual string Objective { get; set; }
    		
    		public virtual string Prescription { get; set; }
    
            public virtual string Evaluation { get; set; }
    
            public virtual DateTime CreatedDatetime { get; set; }
    		
    		public virtual DateTime? ReviewDatetime { get; set; }
    		
    		[Required]
    		public virtual string Editor { get; set; }
    
            public virtual Guid? NcEntityId { get; set; }
            public NcEntity NcEntity { get; set; }
    
        }
    }
    

    Is it the navigation property that is throwing the rogue entry for the property?

  • User Avatar
    0
    BobIngham created

    If that navigation property could be persisted somehow in the entity change set it would be great. In my own implementation I could cut into EntityHistoryStore.SaveAsync() and add parent properties to my Bson document. That would allow me to simplify the search for entity histories by entity including child entities.

  • User Avatar
    0
    aaron created
    Support Team

    Is it the navigation property that is throwing the rogue entry for the property?

    Yes, indirectly.

    If that navigation property could be persisted somehow in the entity change set it would be great. In my own implementation I could cut into EntityHistoryStore.SaveAsync() and add parent properties to my Bson document.

    Yes, you could do that. EntityChange has a reference to EF's EntityEntry. You can persist the navigation property as EntityPropertyChanges.

  • User Avatar
    0
    BobIngham created

    Yes, you could do that. EntityChange has a reference to EF's EntityEntry. You can persist the navigation property as EntityPropertyChanges.

    When I put a watch on EntityChangeSet inside EntityHistoryStore.SaveAsync(EntityChangeSet changeSet) I can see EntityEntry: (e.g. changeSet.EntityChanges[1].EntityEntry but I can not seem to read this data so I add an parentId element to the Bson document (see below).

    How do I get at the data in EF's EntityEntry?

    [attachment=0:293rncof]EntityChangeSet.PNG[/attachment:293rncof]

  • User Avatar
    0
    aaron created
    Support Team

    so I add an parentId element to the Bson document (see below).

    I didn't get that.

    How do I get at the data in EF's EntityEntry?

    var plan = changeSet.EntityChanges[1].EntityEntry.As<EntityEntry>().Entity as NcCarePlan;
    if (plan != null)
    {
        // ...
    }