public async Task InvokeStringMultipleResolvedProperties()
        {
            Attr2 attr = new Attr2("{p2}", "constant")
            {
                ResolvedProp1 = "{p1}"
            };

            var cloner = new AttributeCloner <Attr2>(attr, GetBindingContract("p1", "p2"));

            Attr2 attrResolved = cloner.ResolveFromBindings(new Dictionary <string, object> {
                { "p1", "v1" }, { "p2", "v2" }
            });

            Assert.Equal("v1", attrResolved.ResolvedProp1);
            Assert.Equal("v2", attrResolved.ResolvedProp2);
            Assert.Equal(attr.ConstantProp, attrResolved.ConstantProp);

            var invokeString = cloner.GetInvokeString(attrResolved);
            var attr2        = await cloner.ResolveFromInvokeString(invokeString);

            Assert.Equal(attrResolved.ResolvedProp1, attr2.ResolvedProp1);
            Assert.Equal(attrResolved.ResolvedProp2, attr2.ResolvedProp2);
            Assert.Equal(attrResolved.ConstantProp, attr2.ConstantProp);
        }
        public void Validation_Direct_Early_Fail(string value, bool shouldSucceed)
        {
            ValidationOnlyAttribute attr = new ValidationOnlyAttribute {
                Value = value
            };

            Dictionary <string, object> values = new Dictionary <string, object>()
            {
                { "x", "ignored" }, // ignored since not autoresolve
            };
            var ctx = GetCtx(values);

            try
            {
                var cloner = new AttributeCloner <ValidationOnlyAttribute>(attr, GetBindingContract("name"));

                if (shouldSucceed)
                {
                    // Success
                    var attrResolved = cloner.ResolveFromBindings(values);

                    // no autoresolve/appsetting, so the final value should be the same as the input value.
                    Assert.Equal(value, attrResolved.Value);

                    return;
                }
                Assert.False(true, "Validation should have failed");
            }
            catch (InvalidOperationException e)
            {
                Assert.False(shouldSucceed);

                // Non-appsetting, so include the value in the message
                Assert.True(e.Message.Contains(value));
            }
        }
        public async Task InvokeStringMultipleResolvedProperties()
        {
            Attr2 attr = new Attr2("{p2}", "constant") { ResolvedProp1 = "{p1}" };

            var cloner = new AttributeCloner<Attr2>(attr, GetBindingContract("p1", "p2"));

            Attr2 attrResolved = cloner.ResolveFromBindings(new Dictionary<string, object> {
                { "p1", "v1" }, { "p2", "v2" }});

            Assert.Equal("v1", attrResolved.ResolvedProp1);
            Assert.Equal("v2", attrResolved.ResolvedProp2);
            Assert.Equal(attr.ConstantProp, attrResolved.ConstantProp);

            var invokeString = cloner.GetInvokeString(attrResolved);
            var attr2 = await cloner.ResolveFromInvokeStringAsync(invokeString);

            Assert.Equal(attrResolved.ResolvedProp1, attr2.ResolvedProp1);
            Assert.Equal(attrResolved.ResolvedProp2, attr2.ResolvedProp2);
            Assert.Equal(attrResolved.ConstantProp, attr2.ConstantProp);
        }