public void CreateBindings(IBindingCollection collection)
        {
            var targetObjectsToId = new Dictionary <int, object>();

            var sourceCompileTimeTypes =
                _types.GetTypesWithAttributes(typeof(SourceAttribute), typeof(CompileTimeTypeAttribute));

            var sourceRunTimeTypes =
                _types.GetTypesWithAttributes(typeof(SourceAttribute), typeof(RunTimeTypeAttribute));

            foreach (var sourceCompileTimeType in sourceCompileTimeTypes)
            {
                foreach (var sourceRunTimeType in sourceRunTimeTypes.Where(type => sourceCompileTimeType.IsAssignableFrom(type)))
                {
                    var objects      = new List <object>();
                    var sourceObject = SandboxHelpers.CreateInstance(sourceRunTimeType);
                    objects.Add(sourceObject);

                    var targetObjects = ConfigureTargets(sourceCompileTimeType, sourceObject, targetObjectsToId);
                    objects.AddRange(targetObjects);

                    var bindingSymbols = GetBindingSymbols(sourceCompileTimeType);
                    var bindings       = new List <IBinding <Attribute> >();

                    foreach (var bindingSymbol in bindingSymbols)
                    {
                        bindings.AddRange(CreateBindings(sourceCompileTimeType, sourceObject, bindingSymbol));
                    }

                    if (bindings.Count > 0)
                    {
                        var bindingInitializer = (MethodInfo)sourceCompileTimeType.GetMemberWithAttributeInType <BindingInitializerAttribute>();
                        var bindingSet         = new BindingSet(bindings, bindingInitializer, sourceObject);

                        collection.AddBindingSet(bindingSet, objects);
                    }
                }
            }
        }
        private IReadOnlyCollection <object> ConfigureTargets(Type sourceType, object sourceObject, Dictionary <int, object> targetObjectsToId)
        {
            void ThrowIfNoMatches <T>(IReadOnlyCollection <T> collection,
                                      BindingTargetAttribute bindingTargetAttribute, IdAttribute mappingId)
            {
                if (collection.Count == 0)
                {
                    throw new Exception(
                              $"Cannot find set of {nameof(TargetAttribute)} (ID = {bindingTargetAttribute.Id}) " +
                              $"and {nameof(IdAttribute)} (ID = {mappingId.Id}) on {sourceType.FullName}.");
                }
            }

            void ThrowIfMultipleMatches <T>(IReadOnlyCollection <T> collection,
                                            BindingTargetAttribute bindingTargetAttribute, IdAttribute mappingId)
            {
                if (collection.Count > 1)
                {
                    throw new AmbiguousMatchException(
                              $"Found multiple types with the same set of {nameof(TargetAttribute)} (ID = {bindingTargetAttribute.Id}) " +
                              $"and {nameof(IdAttribute)} (ID = {mappingId.Id}) on {sourceType.FullName}.");
                }
            }

            var targetObjects = new List <object>();

            var bindingTargets = sourceType.GetMembersWithAttributeInType <BindingTargetAttribute>();

            foreach (var bindingTarget in bindingTargets)
            {
                var bindingTargetAttribute = bindingTarget.GetCustomAttribute <BindingTargetAttribute>();
                var mappingId = bindingTarget.GetCustomAttribute <IdAttribute>();

                var runtimeTypes = _types.GetTypesWithAttributes(
                    new[] { typeof(TargetAttribute), typeof(IdAttribute) },
                    attributes => ContainsMatchingTargetAndIdPair(attributes, bindingTargetAttribute, mappingId))
                                   .ToList();

                ThrowIfMultipleMatches(runtimeTypes, bindingTargetAttribute, mappingId);

                object targetObject = null;
                if (runtimeTypes.Count > 0)
                {
                    // reuse target instance if Id matches
                    var runtimeType = runtimeTypes[0];
                    var id          = mappingId.Id;

                    if (targetObjectsToId.ContainsKey(id))
                    {
                        targetObject = targetObjectsToId[id];
                    }
                    else
                    {
                        targetObject = SandboxHelpers.CreateInstance(runtimeType);
                        targetObjects.Add(targetObject);
                        targetObjectsToId[id] = targetObject;
                    }
                }

                var targetBindingSymbols = sourceType.GetMembersWithAttributesInType(
                    new[] { typeof(BindingTargetAttribute), typeof(IdAttribute) },
                    attributes => ContainsMatchingTargetAndIdPair(attributes, bindingTargetAttribute, mappingId))
                                           .ToArray();

                ThrowIfMultipleMatches(targetBindingSymbols, bindingTargetAttribute, mappingId);
                ThrowIfNoMatches(targetBindingSymbols, bindingTargetAttribute, mappingId);

                var targetBindingSymbol = targetBindingSymbols[0];
                if (targetBindingSymbol is PropertyInfo propertyInfo && propertyInfo.CanWrite == false)
                {
                    throw new Exception(
                              $"TestFramework requires BindingTargets to have a setter {propertyInfo.DeclaringType.FullName}::{propertyInfo.Name} is missing this setter.");
                }

                targetBindingSymbol.SetFieldOrPropertyValue(sourceObject, targetObject);
            }

            return(targetObjects);
        }