public void AddAnswer(String controlId, ControlCatalog catalog, IEnumerable<ControlRule> rules, params Object[] answers)
        {
            var date = DateTime.UtcNow;
            ApplyChange(new ControlAnswered
            {
                ControlId = controlId,
                Date = date,
                FormId = FormDefinitionId,
                Id = Guid.NewGuid(),
                ReportingEntityInstanceId = ReportingEntityId,
                Version = Version,
                Values = answers,
            });

            var msgs = ValidateAnswers(controlId, catalog, answers);
            foreach (var msg in msgs)
            {
                if (msg.IsValid)
                {
                    ApplyChange(new ValidationPassed
                    {
                        ControlId = controlId,
                        Date = date,
                        FormId = FormDefinitionId,
                        Id = Guid.NewGuid(),
                        ReportingEntityInstanceId = ReportingEntityId,
                        Message = msg.Message.Message,
                        Version = Version,
                        Validator = msg.Message.ObjectName,
                        Values = answers,
                    });
                }
                else
                {
                    ApplyChange(new ValidationFailed
                    {
                        ControlId = controlId,
                        Date = date,
                        FormId = FormDefinitionId,
                        Id = Guid.NewGuid(),
                        ReportingEntityInstanceId = ReportingEntityId,
                        Message = msg.Message.Message,
                        Version = Version,
                        Validator = msg.Message.ObjectName,
                        Values = answers,
                    });
                }
            }

            RunRules(controlId, catalog, rules, date, answers);

        }
        private static ControlCatalog SetUpFormControls(Guid formId)
        {
            var catalog = new ControlCatalog(Guid.NewGuid(), formId, new List<ControlField>
            {
                new ControlField("FirstName", new List<String>(), new List<IControlValidator>
                {
                    new RequiredValidator(),
                    new MaxLengthValidator(new Dictionary<String, Object> { {"maxLength", 10 } }),
                    new MinLengthValidator(new Dictionary<string, object> { { "minLength", 1 } }),
                }, "String"),
                new ControlField("LastName", new List<String>(), new List<IControlValidator>
                {
                    new RequiredValidator(),
                    new MaxLengthValidator(new Dictionary<String, Object> { {"maxLength", 40 } }),
                    new MinLengthValidator(new Dictionary<string, object> { { "minLength", 3 } }),
                }, "String"),
                new ControlField("Age", new List<String>(), new List<IControlValidator>
                {
                    new RequiredValidator(),
                    new NumericValidator()
                }, "Number"),
                new ControlField("Email", new List<String>(), new List<IControlValidator>
                {
                    new RegexValidator(new Dictionary<String, Object> { { "message", "BOO!" }, { "pattern", @"^([\w-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$" } })
                }, "String"),
                new ControlField("FavoriteFood", new List<String>(), new List<IControlValidator>
                {
                    new MaxLengthValidator(new Dictionary<String, Object> { {"maxLength", 40 } }),
                    new MinLengthValidator(new Dictionary<string, object> { { "minLength", 2 } }),
                }, "String"),
                new ControlField("FirstPart", new List<String>(), new List<IControlValidator>
                {
                    new NumericValidator(),
                }, "Number"),
                new ControlField("SecondPart", new List<String>(), new List<IControlValidator>
                {
                    new NumericValidator(),
                }, "Number"),
            });

            return catalog;
        }
        private static void PerfLoadNewRei(Guid formId, String rei, Int32 snapInterval, ControlCatalog catalog, IEnumerable<ControlRule> rules)
        {
            var newInstance = new ReportingEntityInstance(formId, rei);
            var fields = new String[]{ "FirstName", "LastName", "Age", "Email", "FavoriteFood" };

            Stopwatch sw = new Stopwatch();

            Console.WriteLine($"Starting the creation of {rei}");
            sw.Start();
            for (int i = 0; i < 10000; i++)
            {
                var field = fields[i % 5];
                newInstance.AddAnswer(field, catalog, rules, "answer" + i.ToString());

                EventStore.PersistEvents(_eventCollection, newInstance);

                if (i > 0 && i%snapInterval == 0)
                {
                    var snap = newInstance.TakeSnapshot();
                    EventStore.TakeSnapshot(_snapshotCollection, snap);
                }
            }
            sw.Stop();
            Console.WriteLine($"Took {sw.ElapsedMilliseconds} ms to write 10000 answers with interval of {snapInterval}");

            sw.Reset();

            sw.Start();
            var fullInstance = new ReportingEntityInstance(formId, rei);
            EventStore.LoadDomain(_eventCollection, fullInstance, formId, rei);
            sw.Stop();
            Console.WriteLine($"Took {sw.ElapsedMilliseconds} ms to load domain by replaying events");
            sw.Reset();

            sw.Start();
            var snapInstance = new ReportingEntityInstance(formId, rei);
            snapInstance.LoadSnapshot(EventStore.GetSnapshot(_snapshotCollection, rei));
            EventStore.LoadDomainStartingAtVersion(_eventCollection, snapInstance, formId, rei, snapInstance.Version);
            sw.Stop();
            Console.WriteLine($"Took {sw.ElapsedMilliseconds} ms to load domain from snapshot");

            Console.WriteLine("Replay instance status: ");
            DisplayDomainStatus(fullInstance);

            Console.WriteLine("Snapshot instance status: ");
            DisplayDomainStatus(snapInstance);
        }
 private static ReportingEntityInstance LoadDomainUpToVersion(Guid formId, String rei, Int64 maxVersion, ControlCatalog catalog)
 {
     var instance = new ReportingEntityInstance(formId, rei);
     EventStore.LoadDomainUpToVersion(_eventCollection, instance, formId, rei, maxVersion);
     return instance;
 }
        private void RunRules(String controlId, ControlCatalog catalog, IEnumerable<ControlRule> rules, DateTime now, Object[] answers)
        {
            var results = new List<RuleEvaluated>();
            var validRules = rules.Where(r => r.ControlId == controlId);
            var parameters = ControlAnswers.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.Values.First());
            foreach (var rule in validRules)
            {
                var calc = new NCalc.Expression(rule.Rule.If);
                
                foreach (var answer in answers)
                {
                    calc.Parameters.Clear();
                    calc.Parameters = parameters;
                    calc.Parameters[controlId] = answer;

                    try
                    {
                        var result = (Boolean)calc.Evaluate();
                        String message = null;
                        if (result)
                        {
                            switch (rule.Rule.Then.GetType().Name)
                            {
                                case "NoActionOutcome":
                                    message = rule.Rule.Then.Message;
                                    break;
                                case "FailedValidationOutcome":
                                    var outcome = (FailedValidationOutcome)rule.Rule.Then;

                                    message = $"{outcome.ValidatorName} failed for {outcome.ControlId} with message {outcome.Message}";
                                    break;
                                default:
                                    message = rule.Rule.Then.Message;
                                    break;
                            }
                        }
                        else
                        {
                            switch (rule.Rule.Else.GetType().Name)
                            {
                                case "NoActionOutcome":
                                    message = rule.Rule.Else.Message;
                                    break;
                                case "FailedValidationOutcome":
                                    var outcome = (FailedValidationOutcome)rule.Rule.Else;

                                    message = $"{outcome.ValidatorName} failed for {outcome.ControlId} with message {outcome.Message}";
                                    break;
                                default:
                                    message = rule.Rule.Else.Message;
                                    break;
                            }
                        }

                        ApplyChange(new RuleEvaluated
                        {
                            ControlId = controlId,
                            Date = now,
                            FormId = rule.FormId,
                            Id = Guid.NewGuid(),
                            ReportingEntityInstanceId = ReportingEntityId,
                            Result = message,
                            RuleName = rule.Rule.Name,
                            Values = answers,
                            Version = Version
                        });
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine($"Error in expression: {e.Message}");
                    }
                }
            }
        }
        private IEnumerable<ValidationMessage> ValidateAnswers(String controlId, ControlCatalog catalog, Object[] answers)
        {
            var msgs = new List<ValidationMessage>();
            var control = catalog.ControlFields.FirstOrDefault(c => c.Name.Equals(controlId, StringComparison.OrdinalIgnoreCase));
            if (control == null)
            {
                //not found
                return msgs;
            }
            foreach (var val in control.ControlValidations)
            {
                foreach (var answer in answers)
                {
                    var msg = val.Validate(answer);
                    msg.Message.ObjectName = val.GetType().Name;
                    msgs.Add(msg);
                }
            }

            return msgs;
        }