public void CanSerializeCircularObjectGraphs() { var charlie = new EntityWithChildren() { Name = "Charlie" }; var bravo = new EntityWithChildren("Bravo", new EntityWithChildren[] { charlie }); var alpha = new EntityWithChildren("Alpha", new EntityWithChildren[] { bravo }); charlie.Children.Add(alpha); // Establish a cycle Alpha -> Bravo -> Charlie -> Alpha // Also form a cycle of direct "Sibling" references the other way round, i.e Charlie -> Bravo -> Alpha -> Charlie charlie.Sibling = bravo; bravo.Sibling = alpha; alpha.Sibling = charlie; var healthReporterMock = new Mock <IHealthReporter>(); var observer = new Mock <IObserver <EventData> >(); IDictionary <string, object> spiedPayload = null; observer.Setup(p => p.OnNext(It.IsAny <EventData>())) .Callback <EventData>((p) => { spiedPayload = p.Payload; }); SerilogInputConfiguration configuration = new SerilogInputConfiguration() { UseSerilogDepthLevel = true }; const int MaxDepth = 10; using (var serilogInput = new SerilogInput(configuration, healthReporterMock.Object)) using (serilogInput.Subscribe(observer.Object)) { var logger = new LoggerConfiguration().WriteTo.Sink(serilogInput).Destructure.ToMaximumDepth(MaxDepth).CreateLogger(); logger.Information("Here is an {@entity}", alpha); } // The fact that the loging of alpha succeeds at all (instead of going into an infinite loop) is already a good sign :-) IDictionary <string, object> entity = (IDictionary <string, object>)spiedPayload["entity"]; object[] children = null; int depth = 0; while (entity != null && entity.ContainsKey("Children")) { children = entity["Children"] as object[]; entity = null; if (children != null && children.Length > 0) { entity = children[0] as IDictionary <string, object>; } depth++; } // Every child increases depth by two because the Children property is and array. Assert.Equal(MaxDepth / 2, depth); }
public async void AuditChanges_InsertEntityWithChildrenAndAddChildren_AddsAuditLogWithExpectedForeignKeyJson() { using (DatabaseWrapper database = new DatabaseWrapper()) { //Arrange Guid userId = Guid.Parse("F37FEB8F-A364-47F6-86A5-02E191E8AF20"); SimpleEntity sA = new SimpleEntity("Simple A"); SimpleEntity sB = new SimpleEntity("Simple B"); database.TestContext.SimpleEntities.AddRange(new List <SimpleEntity> { sA, sB }); EntityWithChildren ewc = new EntityWithChildren(sA, sB); database.TestContext.ParentEntities.Add(ewc); database.TestContext.SetEntityId(); //Act database.TestContext.AuditChanges(userId); await database.TestContext.SaveChangesAsync(); //Assert //Audit Log stores correct json, with foreign keys Guid ewcId = database.TestContext.ParentEntities.Single().Id; Guid sBId = database.TestContext.SimpleEntities.Single(se => se.Data == "Simple B").Id; String expectedJson = "{\\\"Id\\\":\\\"" + ewcId + "\\\",\\\"SimpleEntityBId\\\":\\\"" + sBId + "\\\",\\\"RowVersion\\\":null}"; Assert.Matches(expectedJson, database.TestContext.AuditLogs.Single(al => al.RecordId == ewcId).NewValue); } }
public async void AuditChanges_InsertEntityWithChildrenAndUpdateChildren_AddsAuditLogWithExpectedOriginalForeignKeyJson() { using (DatabaseWrapper database = new DatabaseWrapper()) { //Arrange //Add initial entities Guid userId = Guid.Parse("F37FEB8F-A364-47F6-86A5-02E191E8AF20"); SimpleEntity sA = new SimpleEntity("Simple A"); SimpleEntity sB = new SimpleEntity("Simple B"); database.TestContext.SimpleEntities.AddRange(new List <SimpleEntity> { sA, sB }); EntityWithChildren ewc = new EntityWithChildren(sA, sB); database.TestContext.ParentEntities.Add(ewc); database.TestContext.SetEntityId(); database.TestContext.AuditChanges(userId); await database.TestContext.SaveChangesAsync(); // Update exisiting EntityWithChildren with new child entity SimpleEntity sBv2 = new SimpleEntity("Simple B Version 2"); database.TestContext.SimpleEntities.Add(sBv2); ewc.UpdateSimpleEntityB(sBv2); database.TestContext.SetEntityId(); //Act database.TestContext.AuditChanges(userId); await database.TestContext.SaveChangesAsync(); //Assert //Audit Log stores correct original json, with foreign keys Guid ewcId = database.TestContext.ParentEntities.Single().Id; Guid sBId = database.TestContext.SimpleEntities.Single(se => se.Data == "Simple B").Id; String expectedOriginalJson = "{\\\"Id\\\":\\\"" + ewcId + "\\\",\\\"SimpleEntityBId\\\":\\\"" + sBId + "\\\",\\\"RowVersion\\\":.*}"; List <AuditLog> orderedLogs = database.TestContext.AuditLogs.OrderBy(a => a.EventDate).ToList(); Assert.Matches(expectedOriginalJson, orderedLogs.Last(al => al.RecordId == ewcId).OriginalValue); } }
public void UsesSerilogMaxDestructuringDepth() { // Create object structure 5 levels deep var bravo = new EntityWithChildren() { Name = "Bravo" }; var alpha = new EntityWithChildren("Alpha", new EntityWithChildren[] { bravo }); var healthReporterMock = new Mock <IHealthReporter>(); var observer = new Mock <IObserver <EventData> >(); IDictionary <string, object> spiedPayload = null; observer.Setup(p => p.OnNext(It.IsAny <EventData>())) .Callback <EventData>((p) => { spiedPayload = p.Payload; }); SerilogInputConfiguration configuration = new SerilogInputConfiguration() { UseSerilogDepthLevel = true }; using (var serilogInput = new SerilogInput(configuration, healthReporterMock.Object)) using (serilogInput.Subscribe(observer.Object)) { var logger = new LoggerConfiguration().WriteTo.Sink(serilogInput).Destructure.ToMaximumDepth(2).CreateLogger(); logger.Information("Here is an {@entity}", alpha); } // At depth 2 there should be no children (only a null child placeholder) var e = (IDictionary <string, object>)spiedPayload["entity"]; Assert.Equal("Alpha", e["Name"]); Assert.Collection((object[])e["Children"], c => Assert.Null(c)); using (var serilogInput = new SerilogInput(configuration, healthReporterMock.Object)) using (serilogInput.Subscribe(observer.Object)) { var logger = new LoggerConfiguration().WriteTo.Sink(serilogInput).Destructure.ToMaximumDepth(3).CreateLogger(); logger.Information("Here is an {@entity}", alpha); } // At depth 3 there should be one child of Alpha, but all its properties should be blank Assert.Equal("Alpha", ((IDictionary <string, object>)spiedPayload["entity"])["Name"]); var childrenOfAlpha = ((IDictionary <string, object>)spiedPayload["entity"])["Children"] as object[]; Assert.Single(childrenOfAlpha); var b = childrenOfAlpha.First() as IDictionary <string, object>; Assert.True(b.ContainsKey("Name")); Assert.Null(b["Name"]); Assert.True(b.ContainsKey("Children")); Assert.Null(b["Children"]); using (var serilogInput = new SerilogInput(configuration, healthReporterMock.Object)) using (serilogInput.Subscribe(observer.Object)) { var logger = new LoggerConfiguration().WriteTo.Sink(serilogInput).Destructure.ToMaximumDepth(4).CreateLogger(); logger.Information("Here is an {@entity}", alpha); } // At depth 4 the child of Alpha (Bravo) should have its properties set, "Name" in particular. Assert.Equal("Alpha", ((IDictionary <string, object>)spiedPayload["entity"])["Name"]); childrenOfAlpha = ((IDictionary <string, object>)spiedPayload["entity"])["Children"] as object[]; Assert.Single(childrenOfAlpha); b = childrenOfAlpha.First() as IDictionary <string, object>; Assert.Equal("Bravo", b["Name"]); }