예제 #1
0
        public async Task ExampleUsage2()
        {
            var t = Log.MethodEntered("DataStoreExample2.ExampleUsage2");

            // Add a thunk middleware to allow dispatching async actions:
            var thunkMiddleware = Middlewares.NewThunkMiddleware <MyAppState1>();

            // aDD A logging middleware to log all dispatched actions:
            var loggingMiddleware = Middlewares.NewLoggingMiddleware <MyAppState1>();

            // Add a recorder middleware to enable hot reload by replaying previously recorded actions:
            var recorder      = new ReplayRecorder <MyAppState1>();
            var recMiddleware = recorder.CreateMiddleware();

            var undoable = new UndoRedoReducer <MyAppState1>();
            // To allow undo redo on the full store wrap the main reducer with the undo reducer:
            var undoReducer = undoable.Wrap(MyReducers1.ReduceMyAppState1);

            var data  = new MyAppState1(null, null, 0); // the initial immutable state
            var store = new DataStore <MyAppState1>(undoReducer, data, loggingMiddleware, recMiddleware, thunkMiddleware);

            store.storeName = "Store 1";

            TestNormalDispatchOfActions(store);

            TestUndoAndRedo(store);

            await TestAsyncActions(store);

            await TestReplayRecorder(recorder, store);

            TestSkippableUndoActions(store);

            Log.MethodDone(t);
        }
예제 #2
0
 public void TestMakeDebugCopyOfAction()
 {
     {
         var    actionToClone         = new TestAction1("abc");
         bool   copyOfActionSupported = false;
         object actionBeforeDispatch  = null;
         Middlewares.MakeDebugCopyOfAction(actionToClone, ref copyOfActionSupported, ref actionBeforeDispatch);
         Assert.False(copyOfActionSupported);
     }
     {
         var    actionToClone         = new TestAction2("abc");
         bool   copyOfActionSupported = false;
         object actionBeforeDispatch  = null;
         Middlewares.MakeDebugCopyOfAction(actionToClone, ref copyOfActionSupported, ref actionBeforeDispatch);
         Assert.True(copyOfActionSupported);
     }
     {
         var objectToClone = new TestAction3()
         {
             SomeDir = EnvironmentV2.instance.GetNewInMemorySystem()
         };
         bool   copyOfActionSupported = false;
         object actionBeforeDispatch  = null;
         Middlewares.MakeDebugCopyOfAction(objectToClone, ref copyOfActionSupported, ref actionBeforeDispatch);
         Assert.False(copyOfActionSupported);
     }
 }
예제 #3
0
        public async Task <ISaga> Handle(ExecuteStepCommand command)
        {
            ISaga       saga       = command.Saga;
            ISagaStep   step       = command.SagaStep;
            ISagaAction sagaAction = command.SagaAction;
            ISagaModel  model      = command.Model;

            StepData stepData = GetOrCreateStepData(saga, step, model);

            MiddlewaresChain middlewaresChain = Middlewares.BuildFullChain(
                serviceProvider,
                SaveSaga, ExecuteStep);

            Exception executionError = null;

            try
            {
                await Middlewares.ExecuteChain(
                    middlewaresChain,
                    saga, step, stepData);

                stepData.
                SetSucceeded(saga.ExecutionState, dateTimeProvider);
            }
            catch (SagaStopException)
            {
                throw;
                return(null);
            }
            catch (Exception ex)
            {
                logger.
                LogError(ex, $"Saga: {saga.Data.ID}; Executing {(step.Async ? "async " : "")}step: {step.StepName}");

                executionError = ex;

                stepData.
                SetFailed(saga.ExecutionState, dateTimeProvider, executionError.ToSagaStepException());
            }
            finally
            {
                middlewaresChain.
                Clean();

                stepData.
                SetEnded(saga.ExecutionState, dateTimeProvider);
            }

            string nextStepName = CalculateNextStepName(
                saga, step, sagaAction, stepData, executionError);

            SaveNextStep(saga, stepData, nextStepName);

            CheckIfSagaIsDeleted(saga);

            await sagaPersistance.Set(saga);

            return(saga);
        }
예제 #4
0
        void setupImmutableDatastore()
        {
            Log.MethodEntered();
            var log   = Middlewares.NewMutationBroadcasterMiddleware <MyDataModel>();
            var store = new DataStore <MyDataModel>(MainReducer, new MyDataModel(new MyDataModel.SubSection1(string1: "", bool1: false)), log);

            IoC.inject.SetSingleton <IDataStore <MyDataModel> >(store);
        }
예제 #5
0
        private static DataStore <MyModel> NewDataStore()
        {
            MyModel model = new MyModel(null, ImmutableList <MyCircle> .Empty);
            Middleware <MyModel>      exampleMiddleware = Middlewares.NewLoggingMiddleware <MyModel>();
            UndoRedoReducer <MyModel> undoLogic         = new UndoRedoReducer <MyModel>();

            return(new DataStore <MyModel>(undoLogic.Wrap(MyReducer), model, exampleMiddleware));
        }
예제 #6
0
        public static async Task ShowIn(ViewStack viewStack)
        {
            MyModel model = new MyModel(null, ImmutableList <MyUser> .Empty);
            Middleware <MyModel>      exampleMiddleware = Middlewares.NewLoggingMiddleware <MyModel>();
            UndoRedoReducer <MyModel> undoLogic         = new UndoRedoReducer <MyModel>();
            DataStore <MyModel>       store             = new DataStore <MyModel>(undoLogic.Wrap(MyReducer), model, exampleMiddleware);

            MyPresenter presenter = new MyPresenter();

            presenter.targetView = viewStack.ShowView("7GUIs_Task5_CRUD");
            await presenter.LoadModelIntoView(store);
        }
        public Middleware <TContext> Build()
        {
            foreach (var hook in buildHooks)
            {
                hook();
            }

            // Assemble the middlewares sequentially
            Func <TContext, Task> seed = (ctx) => Task.CompletedTask;
            var reversedMiddlewares    = Middlewares.Reverse();
            var handler = reversedMiddlewares.Aggregate(seed, (x, y) => (TContext ctx) => y(ctx, x));

            Middleware <TContext> middleware = (ctx) => handler(ctx);

            return(middleware);
        }
예제 #8
0
        public override IEnumerator RunTest()
        {
            // Create an immutable datastore that will contain the data model in this example:
            var log = Middlewares.NewLoggingMiddleware <MyDataModel3>();
            IDataStore <MyDataModel3> store = new DataStore <MyDataModel3>(MainReducer, new MyDataModel3(), log);

            IoC.inject.SetSingleton(store);

            // Create a presenter that connectes the model with the view (the Unity UI):
            var currentUserPresenter = new MyUserUi3();

            // Set the target view by loading it from a prefab and setting the root GO:
            currentUserPresenter.targetView = ViewStackHelper.MainViewStack().ShowView("MyUserUi1");
            // Connect the model changes with the presenter:
            currentUserPresenter.ListenToStoreUpdates(store, state => state.currentUser);

            // Dispatch a first setUser action to update the UI:
            store.Dispatch(new ActionSetNewUser()
            {
                newUser = new MyUser3("Carl", 99)
            });
            // Delay needed since the UI update simulates a delay too:
            yield return(new WaitForSeconds(0.5f));

            // Check that the UI was automatically updated:
            AssertV2.AreEqual("Carl", currentUserPresenter.NameUi().text);
            AssertV2.AreEqual("99", currentUserPresenter.AgeUi().text);

            // Simulate that the user changed the model via the UI:
            store.Dispatch(new ActionUpdateUser()
            {
                target    = store.GetState().currentUser,
                newValues = new MyUser3("Paul", 0)
            });
            // Delay needed since the UI update simulates a delay too:
            yield return(new WaitForSeconds(2f));

            // Check that the UI was automatically updated:
            AssertV2.AreEqual("Paul", currentUserPresenter.NameUi().text);
            AssertV2.AreEqual("0", currentUserPresenter.AgeUi().text);
        }
예제 #9
0
        private async Task TestReplayRecorderOnNewStore(ReplayRecorder <MyAppState1> recorder, MyAppState1 finalStateOfFirstStore)
        {
            var t = Log.MethodEntered("TestReplayRecorderOnNewStore");

            // Connect the recorder to the new store:
            var recMiddleware = recorder.CreateMiddleware();
            var undoable      = new UndoRedoReducer <MyAppState1>();
            var logging       = Middlewares.NewLoggingMiddleware <MyAppState1>();

            var data2  = new MyAppState1(null, null, 0);
            var store2 = new DataStore <MyAppState1>(undoable.Wrap(MyReducers1.ReduceMyAppState1), data2, logging, recMiddleware);

            store2.storeName = "Store 2";

            // Replaying the recorder will now fill the second store with the same actions:
            await recorder.ReplayStore();

            AssertEqualJson(finalStateOfFirstStore, store2.GetState());

            Log.MethodDone(t);
        }
예제 #10
0
        public static async Task ShowIn(ViewStack viewStack)
        {
            // Call model unit tests manually before the UI is shown:
            CellsModelTests.TestFromAndToRowName();
            CellsModelTests.TestDataStoreTransitiveChanges();

            CellsModel model = new CellsModel(ImmutableDictionary <CellPos, Cell> .Empty);
            Middleware <CellsModel>      logging   = Middlewares.NewLoggingMiddleware <CellsModel>();
            UndoRedoReducer <CellsModel> undoLogic = new UndoRedoReducer <CellsModel>();
            DataStore <CellsModel>       store     = new DataStore <CellsModel>(undoLogic.Wrap(CellsReducers.MainReducer), model, logging);

            MyPresenter presenter = new MyPresenter();

            presenter.targetView = viewStack.ShowView("7GUIs_Task7_Cells");
            await presenter.LoadModelIntoView(store);

            await TaskV2.Delay(2000);

            Toast.Show("Now simulating some table model changes..");
            // Simulate changes in the model to check if the UI updates correctly:
            CellsModelTests.SimulateSomeChangesInModel(store);
        }
 public void Use(Func <TContext, Func <TContext, Task>, Task> middleware) => Middlewares.Add(middleware);
예제 #12
0
        public async Task ExampleUsage1()
        {
            var t = Log.MethodEntered("DataStoreExample3.ExampleUsage1");

            // Add a thunk middleware to allow dispatching async actions:
            var thunkMiddleware = Middlewares.NewThunkMiddleware <MyAppState1>();

            // aDD A logging middleware to log all dispatched actions:
            var loggingMiddleware = Middlewares.NewLoggingMiddleware <MyAppState1>();

            var serverOutboxHandler = new ServerOutboxHandler <MyAppState1>();
            // To allow undo redo on the full store wrap the main reducer with the undo reducer:
            var outboxReducer = serverOutboxHandler.Wrap(MyReducers1.ReduceMyAppState1);
            var initialState  = new MyAppState1(); // the initial immutable state
            var store         = new DataStore <MyAppState1>(outboxReducer, initialState, loggingMiddleware, thunkMiddleware);

            IoC.inject.SetSingleton(store);
            store.storeName = "Store 3";

            { // Do a login which is an async server action that cant be cached optimistically and wont work offline:
                Func <Task> asyncLoginTask = async() => {
                    await TaskV2.Delay(100);

                    store.Dispatch(new ActionUserLoggedIn()
                    {
                        newLoggedInUser = new MyUser1("*****@*****.**")
                    });
                };
                await(store.Dispatch(asyncLoginTask) as Task);
            }
            { // Change the email a first time:
                var a = new ActionOnUser.ChangeEmail()
                {
                    targetEmail = "*****@*****.**", newEmail = "*****@*****.**"
                };
                store.Dispatch(a);
                Assert.Equal(a, store.GetState().serverOutbox.serverActions.First());
                Assert.False(store.GetState().user.emailConfirmed);
            }
            { // Change the email a second time:
                var a = new ActionOnUser.ChangeEmail()
                {
                    targetEmail = "*****@*****.**", newEmail = "*****@*****.**"
                };
                store.Dispatch(a);
                Assert.Equal(a, store.GetState().serverOutbox.serverActions.Last());
            }

            Assert.Equal(2, store.GetState().serverOutbox.serverActions.Count);
            await store.SyncWithServer(store.GetState().serverOutbox.serverActions.First());

            Assert.Single(store.GetState().serverOutbox.serverActions);
            await store.SyncWithServer(store.GetState().serverOutbox.serverActions.First());

            Assert.Empty(store.GetState().serverOutbox.serverActions);
            Assert.True(store.GetState().user.emailConfirmed);

            { // Simulate a server task that has a timeout:
                var a = new ActionOnUser.ChangeEmail()
                {
                    targetEmail = "*****@*****.**", newEmail = "*****@*****.**", simulateOneTimeout = true
                };
                store.Dispatch(a);
                Assert.Single(store.GetState().serverOutbox.serverActions);
                Assert.False(store.GetState().user.emailConfirmed);
                await store.SyncWithServer(a);

                Assert.Empty(store.GetState().serverOutbox.serverActions);
                Assert.Equal(2, a.sentToServerCounter);
                Assert.True(store.GetState().user.emailConfirmed);
            }
            { // Simulate the server rejecting an email change:
                var a = new ActionOnUser.ChangeEmail()
                {
                    targetEmail = "*****@*****.**", newEmail = "*****@*****.**", simulateError = true
                };
                store.Dispatch(a);
                await store.SyncWithServer(a);

                Assert.Empty(store.GetState().serverOutbox.serverActions);
                Assert.Equal("*****@*****.**", store.GetState().user.email);
                Assert.True(store.GetState().user.emailConfirmed);
            }
            { // Test persisting and restoring the full store and continue with the pending server requests:
                store.Dispatch(new ActionOnUser.ChangeEmail()
                {
                    targetEmail = "*****@*****.**", newEmail = "*****@*****.**"
                });
                store.Dispatch(new ActionOnUser.ChangeEmail()
                {
                    targetEmail = "*****@*****.**", newEmail = "*****@*****.**"
                });
                Assert.Equal(2, store.GetState().serverOutbox.serverActions.Count);
                Assert.False(store.GetState().user.emailConfirmed);
                Assert.Equal("*****@*****.**", store.GetState().user.email);

                // Simulate persisiting the store to disk and back into memory:
                string persistedStateJson = TypedJsonHelper.NewTypedJsonWriter().Write(store.GetState());

                store.Destroy(); // Destroy the old store before loading the state again into an new store
                var data2  = TypedJsonHelper.NewTypedJsonReader().Read <MyAppState1>(persistedStateJson);
                var store2 = new DataStore <MyAppState1>(outboxReducer, data2, loggingMiddleware, thunkMiddleware);
                IoC.inject.SetSingleton(store2, overrideExisting: true);
                store2.storeName = "Store 3 (2)";

                Assert.Equal(2, store2.GetState().serverOutbox.serverActions.Count);
                Assert.False(store2.GetState().user.emailConfirmed);
                Assert.Equal("*****@*****.**", store2.GetState().user.email);

                // Sync the pending server tasks one after another:
                foreach (var serverAction in store2.GetState().serverOutbox.serverActions)
                {
                    await store2.SyncWithServer(serverAction);
                }
                Assert.True(store2.GetState().user.emailConfirmed);
                Assert.NotNull(store2.GetState().serverOutbox);
                Assert.Empty(store2.GetState().serverOutbox.serverActions);
            }
        }
예제 #13
0
        public static void TestDataStoreTransitiveChanges()
        {
            CellsModel model = new CellsModel(ImmutableDictionary <CellPos, Cell> .Empty);
            var        store = new DataStore <CellsModel>(CellsReducers.MainReducer, model, Middlewares.NewLoggingMiddleware <CellsModel>());

            store.Dispatch(new MyActions.SetCell("A", 1, "1 + 1"));
            var  cells = store.SelectElement(s => s.cells);
            Cell a1    = cells()[new CellPos("A", 1)];

            Assert.Equal(2, a1.value);

            // B1 will depend on A1:
            store.Dispatch(new MyActions.SetCell("B", 1, "3 * A1"));
            Cell b1 = cells()[new CellPos("B", 1)];

            Assert.Equal(6, b1.value);

            // C1 will depend on A1 and B1:
            store.Dispatch(new MyActions.SetCell("C", 1, "B1 + 3 - A1"));
            Cell c1 = cells()[new CellPos("C", 1)];

            Assert.Equal(7, c1.value);

            // D1 will depend on C1 (so transitivly also on A1 and B1):
            store.Dispatch(new MyActions.SetCell("D", 1, "2 * C1"));
            Cell d1 = cells()[new CellPos("D", 1)];

            Assert.Equal(14, d1.value);

            // Now changing A1 must have affects to B1, C1, D1 as well:
            store.Dispatch(new MyActions.SetCell("A", 1, "4 + 1"));
            c1 = cells()[new CellPos("C", 1)];
            Assert.Equal(13, c1.value);
            d1 = cells()[new CellPos("D", 1)];
            Assert.Equal(26, d1.value);

            // Select cell C1:
            store.Dispatch(new MyActions.SelectCell(c1.pos));
            Assert.True(cells()[new CellPos("C", 1)].isSelected);
            // Select cell D1:
            store.Dispatch(new MyActions.SelectCell(d1.pos));
            Assert.True(cells()[new CellPos("D", 1)].isSelected);
            Assert.False(cells()[new CellPos("C", 1)].isSelected);

            store.Dispatch(new MyActions.SetCell("A", 3, "1"));
            store.Dispatch(new MyActions.SetCell("A", 2, "A3 + 1"));
            Assert.Throws <MyActions.SetCell.SelfRefException>(() => {
                store.Dispatch(new MyActions.SetCell("A", 3, "A2 + 1"));
            });
        }
예제 #14
0
 public SmartSqlBuilder AddMiddleware(IMiddleware middleware)
 {
     Middlewares.Add(middleware);
     return(this);
 }
예제 #15
0
 public EletraMiddlewareService(IMapper mapper, IOptionsMonitor <Middlewares> middlewaresAccessor)
 {
     _mapper      = mapper;
     _middlewares = middlewaresAccessor.CurrentValue;
     Manufacturer = "Eletra";
 }
예제 #16
0
 /// <summary>
 /// 获取指定类型的全部中间件
 /// </summary>
 /// <typeparam name="T"></typeparam>
 /// <returns></returns>
 public IEnumerable <T> GetMiddlewares <T>()
 {
     return(Middlewares.Where(m => m is T).Cast <T>());
 }
예제 #17
0
 public GrpcServerBuilder AddMiddleware <T>() where T : GrpcMiddlewareBase
 {
     Middlewares.Add(typeof(T));
     return(this);
 }
 public IApplicationBuilder Use(Func <RequestDelegate, RequestDelegate> middleware)
 {
     Middlewares.Add(middleware);
     return(this);
 }
예제 #19
0
파일: Store.cs 프로젝트: Crocus7724/ReNet
        private IAction InvokeDispathAction(IAction action)
        {
            action = Middlewares.Invoke(action);

            return(InnerDispath(action));
        }
예제 #20
0
        public static async Task TestLoggingOverhead()
        {
            StopwatchV2 t1, t2;

            {
                t1 = Log.MethodEntered("SimulateManyChangesInModel without logging");
                CellsModel model = new CellsModel(ImmutableDictionary <CellPos, Cell> .Empty);
                var        store = new DataStore <CellsModel>(CellsReducers.MainReducer, model);
                await SimulateManyChangesInModel(store);

                Log.MethodDone(t1);
            }
            {
                t2 = Log.MethodEntered("SimulateManyChangesInModel without logging");
                CellsModel model = new CellsModel(ImmutableDictionary <CellPos, Cell> .Empty);
                var        store = new DataStore <CellsModel>(CellsReducers.MainReducer, model, Middlewares.NewLoggingMiddleware <CellsModel>());
                await SimulateManyChangesInModel(store);

                Log.MethodDone(t2);
            }
            // Logging makes mutating the model at least double as slow:
            Assert.True(t1.ElapsedMilliseconds * 2 < t2.ElapsedMilliseconds, $"t1={t1}, t2={t2}");
        }
예제 #21
0
 public WebSocketService(IServiceProvider serviceProvider, IOptions <ServerOptions> serverOptions, IChannelCreatorFactory channelCreatorFactory)
     : base(serviceProvider, serverOptions, channelCreatorFactory)
 {
     _sessionContainerMiddleware = Middlewares.FirstOrDefault(m => m is IAsyncSessionContainer || m is ISessionContainer);
 }
예제 #22
0
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseSwagger();
            app.UseSwaggerUI(c =>
            {
                var swaggerJsonBasePath = string.IsNullOrWhiteSpace(c.RoutePrefix) ? "." : "..";
                c.SwaggerEndpoint($"{swaggerJsonBasePath}/swagger/v1.0/swagger.json", Configuration.GetValue <string>("SystemInfo:Name"));
            });

            var basePath = Path.Combine(OptionsClient.GetData(Configuration.GetValue <string>("FileStore:BasePath")));

            if (!Directory.Exists(basePath))
            {
                Directory.CreateDirectory(basePath);
            }

            app.UseStaticFiles(new StaticFileOptions()
            {
                FileProvider     = new PhysicalFileProvider(basePath),
                HttpsCompression = HttpsCompressionMode.Compress
            });

            app.UseCors(builder => builder
                        .AllowAnyOrigin()
                        .AllowAnyMethod()
                        .AllowAnyHeader());

            app.UseResponseCompression();

            var headersOptions = new ForwardedHeadersOptions
            {
                ForwardedHeaders      = ForwardedHeaders.XForwardedFor,
                RequireHeaderSymmetry = false,
                ForwardLimit          = null
            };

            headersOptions.KnownNetworks.Clear();
            headersOptions.KnownProxies.Clear();
            app.UseForwardedHeaders(headersOptions);

            app.UseMetricsAllMiddleware();

            app.Use(async(context, next) =>
            {
                await next.Invoke();
                Middlewares.AutoDiscoverRoutes(context);
            });

            app.UseRouting();

            app.UseAuthentication();
            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapHealthChecks("/health");
                endpoints.MapControllers();
            });
        }
예제 #23
0
 public override PipelineResult <ClientActionContext> Build()
 {
     return(new ClientPipelineResult(BuildActionDelegate(), Middlewares.ToList()));
 }
예제 #24
0
        public void ExampleUsage1()
        {
            var t = Log.MethodEntered("DataStoreExample3.ExampleUsage1");

            // A middleware that will allow to use mutable data in the data store:
            var mutableMiddleware = Middlewares.NewMutableDataSupport <MyAppState1>();

            // Add A logging middleware to log all dispatched actions:
            var loggingMiddleware = Middlewares.NewLoggingMiddleware <MyAppState1>();

            MyUser1 user = new MyUser1()
            {
                name = "Carl"
            };
            var model = new MyAppState1()
            {
                user = user
            };

            var store = new DataStore <MyAppState1>(MyReducers1.ReduceMyAppState1, model, loggingMiddleware, mutableMiddleware);

            IoC.inject.SetSingleton(store);
            store.storeName = "Store 4";

            // Setup 3 listeners that react when the name of the user or his contacts change:
            var userChangedCounter         = 0;
            var userNameChangedCounter     = 0;
            var contact1NameChangedCounter = 0;
            var contact2NameChangedCounter = 0;

            store.AddStateChangeListener(s => s.user, (MyUser1 theChangedUser) => {
                userChangedCounter++;
            });
            store.AddStateChangeListener(s => s.user?.name, (string theChangedName) => {
                userNameChangedCounter++;
            });
            store.AddStateChangeListener(s => s.user?.contacts?.FirstOrDefault()?.contactData.name, (_) => {
                contact1NameChangedCounter++;
            });
            store.AddStateChangeListener(s => s.user?.contacts?.Skip(1).FirstOrDefault()?.contactData.name, (_) => {
                contact2NameChangedCounter++;
            });

            var contact1 = new MyUser1()
            {
                name = "Tom"
            };
            { // Add a first contact to the user:
                Assert.Null(user.contacts);
                store.Dispatch(new ActionAddContact()
                {
                    targetUserId = user.id,
                    newContact   = new MyContact1()
                    {
                        contactData = contact1
                    }
                });
                Assert.Same(contact1, user.contacts.First().contactData);
                Assert.True(user.WasModifiedInLastDispatch());

                // Now that there is a contact 1 the listener was triggered:
                Assert.Equal(1, userChangedCounter);
                Assert.Equal(0, userNameChangedCounter);
                Assert.Equal(1, contact1NameChangedCounter);
                Assert.Equal(0, contact2NameChangedCounter);
            }

            var contact2 = new MyUser1()
            {
                name = "Bill"
            };
            { // Add a second contact to the user which should not affect contact 1:
                store.Dispatch(new ActionAddContact()
                {
                    targetUserId = user.id,
                    newContact   = new MyContact1()
                    {
                        contactData = contact2
                    }
                });
                Assert.Same(contact2, user.contacts.Last().contactData);
                Assert.True(user.WasModifiedInLastDispatch());
                Assert.False(contact1.WasModifiedInLastDispatch());

                Assert.Equal(2, userChangedCounter);
                Assert.Equal(0, userNameChangedCounter);
                Assert.Equal(1, contact1NameChangedCounter);
                Assert.Equal(1, contact2NameChangedCounter);
            }
            { // Change the name of contact 1 which should not affect contact 2:
                var newName1 = "Toooom";
                store.Dispatch(new ActionChangeUserName()
                {
                    targetUserId = contact1.id, newName = newName1
                });
                Assert.True(user.WasModifiedInLastDispatch());
                Assert.True(contact1.WasModifiedInLastDispatch());
                Assert.False(contact2.WasModifiedInLastDispatch());

                Assert.Equal(3, userChangedCounter);
                Assert.Equal(0, userNameChangedCounter);
                Assert.Equal(2, contact1NameChangedCounter);
                Assert.Equal(1, contact2NameChangedCounter);
            }
            { // Change the name of the user which should not affect the 2 contacts:
                var newName = "Caaaarl";
                Assert.NotEqual(newName, user.name);
                Assert.Equal(user.name, store.GetState().user.name);
                var tBeforeDispatch = user.LastMutation;
                store.Dispatch(new ActionChangeUserName()
                {
                    targetUserId = user.id, newName = newName
                });
                Assert.Equal(newName, store.GetState().user.name);
                Assert.Equal(newName, user.name);
                Assert.Same(model, store.GetState());

                Assert.NotEqual(tBeforeDispatch, user.LastMutation);
                Assert.True(user.WasModifiedInLastDispatch());
                Assert.False(contact1.WasModifiedInLastDispatch());
                Assert.False(contact2.WasModifiedInLastDispatch());

                Assert.Equal(4, userChangedCounter);
                Assert.Equal(1, userNameChangedCounter);
                Assert.Equal(2, contact1NameChangedCounter);
                Assert.Equal(1, contact2NameChangedCounter);
            }
            { // Marking an object mutated while not dispatching will throw an exception:
                Assert.Throws <InvalidOperationException>(() => {
                    user.name = "Cooorl";
                    user.MarkMutated();
                });
                Assert.Equal(4, userChangedCounter); // Count should not have changed
                Assert.Equal(1, userNameChangedCounter);
            }
        }
예제 #25
0
        public async Task RunTestTask()
        {
            MyUser1 initialState = null; // Initially no user is logged in
            var     store        = new DataStore <MyUser1>(MyReducers1.ReduceUser, initialState, Middlewares.NewLoggingMiddleware <MyUser1>());

            var links = gameObject.GetLinkMap();

            var loginButton           = links.Get <Button>("LoginBtn");
            var loginButtonWasClicked = loginButton.SetOnClickAction(async delegate {
                loginButton.gameObject.Destroy();
                await TaskV2.Run(async() => {
                    await TaskV2.Delay(1000);
                    store.Dispatch(new ActionLoginUser()
                    {
                        newLoggedInUser = new MyUser1("Karl")
                    });
                }).LogOnError();
                Assert.IsNotNull(store.GetState());
            });

            // Register a listener that is attached to the UI button to demonstrate that its no longer triggered once the button is destroyed:
            var userNameChangedCounter = 0;
            var subStateListener       = store.NewSubStateListenerForUnity(loginButton, user => user);

            subStateListener.AddStateChangeListener(x => x?.name, newName => {
                userNameChangedCounter++;
                Toast.Show("User name changed to " + newName);
                Assert.IsFalse(loginButton.IsDestroyed());
            }, triggerInstantToInit: false);

            var userInfoText1 = links.Get <InputField>("UserNameInput1");

            ConnectInputFieldUiToModel(userInfoText1, store);
            var userInfoText2 = links.Get <InputField>("UserNameInput2");

            ConnectInputFieldUiToModel(userInfoText2, store);

            var oldCounterValue = userNameChangedCounter;

            SimulateButtonClickOn("LoginBtn");
            await loginButtonWasClicked;

            Assert.IsTrue(loginButton.IsDestroyed());
            // Since the button was destroyed, the counter should not change anymore:
            Assert.AreEqual(oldCounterValue, userNameChangedCounter);

            Toast.Show("Changing user name from background thread...");
            await Task.Delay(2000);

            // When NewSubStateListener instead of NewSubStateListenerForUnity is used, the
            // event will arrive on the thread where it was dispatched:
            var wasCalledOnMainThread = true;

            store.NewSubStateListener(user => user).AddStateChangeListener(x => x.name, newName => {
                wasCalledOnMainThread = MainThread.isMainThread;
            }, triggerInstantToInit: false);

            await TaskV2.Run(async() => { store.Dispatch(new ChangeName()
                {
                    newName = "Caaarl"
                }); });

            Assert.AreEqual("Caaarl", store.GetState().name);
            Assert.IsFalse(wasCalledOnMainThread);
        }