static void Main(string[] args) { MyUser original = new MyUser(1, "Joe", "Bloggs", "*****@*****.**"); MyUser changed = new MyUser(1, "John", "Bloggs", "*****@*****.**", "Line 1", "Line 2", "City", "ZipCode"); ChangeLogger logger = new ChangeLogger(original.Id, original, changed); if (!logger.Success) { Console.Write(logger.Exception.ToString()); return; } // You can also change the value of the changed class before calling Audit changed.LastName = "Blogs"; logger.Audit(); if (!logger.Success) { Console.Write(logger.Exception.ToString()); return; } foreach (ChangeLog change in logger.Changes) { Console.Write(string.Format("{2} property changed from {3} to {4} for class {1} with id {0}\r\n", change.ObjectId, change.ObjectType, change.Property, change.ValueOld, change.ValueNew)); } Console.ReadKey(); }
public static IChangeLogger CreateChangeLogger(ConnectionFactory.ConnectionMethod connectionMethod, string connectionstring = null, DbConnection conn = null, bool?killConnectionOnDispose = null) { IChangeLogger c = new ChangeLogger(); switch (connectionMethod) { case ConnectionFactory.ConnectionMethod.AppSettingsConnectionString: { c.LoadContext(); break; } case ConnectionFactory.ConnectionMethod.ConnectionString: { c.LoadContext(connectionstring); break; } case ConnectionFactory.ConnectionMethod.CurrentContextConnection: { bool killconn = false; if (killConnectionOnDispose.HasValue) { killconn = killConnectionOnDispose.Value; } c.LoadContext(conn, killconn); break; } } return(c); }
public void TestSolverRuleOnce() { var game = (new GameFactoryForTesting()).GenerateGameOneSolution(); var changesOnce = new ChangeLogger(true); var changesUnlimited = new ChangeLogger(false); var fullGridRules = new List <ISolverRule>() { changesOnce, changesUnlimited }; var solver = new Solver(null, fullGridRules, new BruteForce(), new Backup <IBimaruGrid>()); solver.Solve(game); Assert.IsTrue(changesOnce.NumberOfSolverRuleCalls == 1); Assert.IsTrue(changesUnlimited.NumberOfSolverRuleCalls > 1); }
private void logChanges(TPrincipal principal) { var logger = new ChangeLogger <TChangeSet, TPrincipal>(context, factory, filter); // This returns the log objects, but they are not attached to the context // so the context change tracker hasn't noticed them var oven = logger.Log(context.ObjectStateManager); // So when we accept changes, we are only accepting the changes from the // original changes - the context hasn't yet detected the log changes context.AcceptAllChanges(); // This code then attaches the log objects to the context if (oven.HasChangeSet) { // First do any deferred log value calculations. // See PropertyChange.Bake for more information TChangeSet changeSet = oven.Bake(DateTime.Now, principal); context.AddChangeSet(changeSet); } }
public void TestInitialFieldChanges() { var game = (new GameFactory()).GenerateEmptyGame(2, 2); game.Grid[new GridPoint(0, 0)] = BimaruValue.WATER; game.Grid[new GridPoint(0, 1)] = BimaruValue.WATER; var changeLogger = new ChangeLogger(); var solver = new Solver(new List <IFieldValueChangedRule>() { changeLogger }, null, null, new Backup <IBimaruGrid>()); solver.Solve(game); AssertEqualChangedEventArgs( new List <FieldValueChangedEventArgs <BimaruValue> >() { new FieldValueChangedEventArgs <BimaruValue>(new GridPoint(0, 0), BimaruValue.UNDETERMINED), new FieldValueChangedEventArgs <BimaruValue>(new GridPoint(0, 1), BimaruValue.UNDETERMINED), }, changeLogger); }
public Task SaveAndLogAsync(AuditModel audit, Type auditMasterEntityType, string auditMasterEntityId) { return(ChangeLogger.SaveChangesAsync(_db, _auditDb, _db.SaveChangesWithValidationExplainedAsync, audit, auditMasterEntityType, auditMasterEntityId)); }
protected async Task <ISaveResult <TChangeSet> > saveChangesAsync(TPrincipal principal, ITransactionProvider transactionProvider, CancellationToken cancellationToken) { if (!Enabled) { return(new SaveResult <TChangeSet, TPrincipal>(await context.SaveAndAcceptChangesAsync(cancellationToken: cancellationToken))); } var result = new SaveResult <TChangeSet, TPrincipal>(); // We want to split saving and logging into two steps, so that when we // generate the log objects the database has already assigned IDs to new // objects. Then we can log about them meaningfully. So we wrap it in a // transaction so that even though there are two saves, the change is still // atomic. cancellationToken.ThrowIfCancellationRequested(); await transactionProvider.InTransactionAsync(async() => { var logger = new ChangeLogger <TChangeSet, TPrincipal>(context, factory, filter, serializer); var oven = (IOven <TChangeSet, TPrincipal>)null; // First we detect all the changes, but we do not save or accept the changes // (i.e. we keep our record of them). cancellationToken.ThrowIfCancellationRequested(); context.DetectChanges(); // Then we save and accept the changes, which invokes the standard EntityFramework // DbContext.SaveChanges(), including any custom user logic the end-user has defined. // Eventually, DbContext.InternalContext.ObjectContext.SaveChanges() will be invoked // and then the delegate below is called back to prepare the log objects/changes. cancellationToken.ThrowIfCancellationRequested(); result.AffectedObjectCount = await context.SaveAndAcceptChangesAsync(cancellationToken: cancellationToken, onSavingChanges: (sender, args) => { // This is invoked just moments before EntityFramework accepts the original changes. // Now is our best oppertunity to create the log objects, which will not yet be attached // to the context. They are unattached so that the context change tracker won't noticed // them when accepting the original changes. cancellationToken.ThrowIfCancellationRequested(); oven = logger.Log(context.ObjectStateManager); // NOTE: This is the last chance to cancel the save. After this, the original changes // will have been accepted and it will be too late to stop now (see comment below) cancellationToken.ThrowIfCancellationRequested(); } ); // NOTE: From this point in, we stop honoring the cancellation token. // Why? because if we did, you would end up persisted object changes without any associated logging. // In the interest of data integrity, we either persist the object changes + logging, or nothing at all. // If the oven is not set here, then DbContext.SaveChanges() did not call our delegate back // when accepting the original changes. Without the oven, we cannot bake the logged changes. if (oven == null) { throw new ChangesNotDetectedException(); } // Finally, we attach the previously prepared log objects to the context (and save/accept them) if (oven.HasChangeSet) { // First do any deferred log value calculations. // (see PropertyChange.Bake for more information) // Then detect all the log changes that were previously deferred result.ChangeSet = oven.Bake(DateTime.Now, principal); context.AddChangeSet(result.ChangeSet); context.DetectChanges(); // Then we save and accept the changes that result from creating the log objects // NOTE: We do not use SaveAndAcceptChanges() here because we are not interested in going // through DbContext.SaveChanges() and invoking end-users custom logic. await context.SaveChangesAsync(SaveOptions.AcceptAllChangesAfterSave); } }); return(result); }
/// <summary> /// Run in PreCommit phase to create Framelog module, IOven instance /// </summary> /// <param name="principal"></param> public void LogChanges(TPrincipal principal) { logger = new ChangeLogger <TChangeSet, TPrincipal>(contextInfo.ObjectContext, factory, filter); oven = logger.Log(contextInfo.ObjectContext.ObjectStateManager); }