protected virtual object Build(Type type, string referenceName, object context, params object[] args)
{
if (type == null)
{
throw new ArgumentNullException(nameof(type));
}
EnsureInitialized();
var circularReference = BuildChain.FirstOrDefault(x => x.GetType() == type);
if (circularReference != null)
{
Log.CircularReferenceDetected(type);
return circularReference;
}
Func<Type, string, LinkedList<object>, object> generator = null;
Type generatorType = null;
var contextType = context?.GetType();
Type targetType = null;
// First check if there is a creation rule
var creationRule =
Configuration.CreationRules?.Where(x => x.IsMatch(contextType, referenceName))
.OrderByDescending(x => x.Priority)
.FirstOrDefault();
if (creationRule != null)
{
generator = creationRule.Create;
generatorType = creationRule.GetType();
// The creation rule is targeted against the type that owns the reference
targetType = contextType;
}
else
{
// Next check if this is a type supported by a value generator
var valueGenerator =
Configuration.ValueGenerators?.Where(x => x.IsSupported(type, referenceName, BuildChain))
.OrderByDescending(x => x.Priority)
.FirstOrDefault();
if (valueGenerator != null)
{
generator = valueGenerator.Generate;
generatorType = valueGenerator.GetType();
// The value generator is targeted against the type of the reference being generated for
targetType = type;
}
}
if (generator != null)
{
Log.CreatingValue(type, context);
try
{
return generator(targetType, referenceName, BuildChain);
}
catch (BuildException)
{
throw;
}
catch (Exception ex)
{
Log.BuildFailure(ex);
const string MessageFormat =
"Failed to create value for type {0} using value generator {1}, {2}: {3}{4}{4}At the time of the failure, the build log was:{4}{4}{5}";
var buildLog = Log.Output;
var message = string.Format(
CultureInfo.CurrentCulture,
MessageFormat,
type.FullName,
generatorType.FullName,
ex.GetType().Name,
ex.Message,
Environment.NewLine,
buildLog);
throw new BuildException(message, type, referenceName, context, buildLog, ex);
}
}
var typeCreator =
Configuration.TypeCreators?.Where(x => x.CanCreate(type, referenceName, BuildChain))
.OrderByDescending(x => x.Priority)
.FirstOrDefault();
if (typeCreator == null)
{
throw BuildFailureException(type, referenceName, context);
}
Log.CreatingType(type, context);
try
{
var instance = CreateAndPopulate(type, referenceName, BuildChain, args, typeCreator);
return instance;
}
catch (BuildException)
{
// Don't recapture build failures here
throw;
}
catch (Exception ex)
{
Log.BuildFailure(ex);
const string MessageFormat =
"Failed to create type {0} using type creator {1}, {2}: {3}{4}{4}At the time of the failure, the build log was:{4}{4}{5}";
var buildLog = Log.Output;
var message = string.Format(
CultureInfo.CurrentCulture,
MessageFormat,
type.FullName,
typeCreator.GetType().FullName,
ex.GetType().Name,
ex.Message,
Environment.NewLine,
buildLog);
throw new BuildException(message, type, referenceName, context, buildLog, ex);
}
finally
{
Log.CreatedType(type, context);
}
}