var contact = Contact.Create();
(Sorry for the formatting below... still trying to work out the prettifier tool)
However, when you are using the DefaultModelBinder in ASP.NET MVC to populate your models this can create an issue for you. Take this totally average example:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Index([Bind(Exclude="Id")] Contact contact)
{
if (!ModelState.IsValid)
return View();
contact.Save();
return RedirectToAction("Thanks");
}
The problem exposed here is that the DefaultModelBinder which runs in the background to populate (and validate) the incoming contact will cause an exception. It requires a public, parameter-less constructor to work.
The most flexible way to get around this (without breaking our business rule of not exposing constructors) is to make your own model binder that understands how to deal with these factory methods.
Your new class should inherit from DefaultModelBinder and the method you need to override is CreateModel. Here is an example of what I did:
public class XLDataAnnotationsModelBinder : DefaultModelBinder
{
/// <summary>
/// We override this because most of our models do not have publicly accessible constructors, which the the
/// default model binder requires. Here we check to see if there is a public, static Create method available
/// and, if so, use that to create the blank model.
/// </summary>
protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, System.Type modelType)
{
Type typeToCreate = modelType;
// most XL models have a static, parameterless Create() method we should use ...
var createMethod = GetParameterlessCreate(typeToCreate);
if(createMethod != null)
{
return createMethod.Invoke(null, null);
}
// we can understand some collection interfaces, e.g. IList<>, IDictionary<,>
if (modelType.IsGenericType) {
Type genericTypeDefinition = modelType.GetGenericTypeDefinition();
if (genericTypeDefinition == typeof(IDictionary<,>)) {
typeToCreate = typeof(Dictionary<,>).MakeGenericType(modelType.GetGenericArguments());
}
else if (genericTypeDefinition == typeof(IEnumerable<>) || genericTypeDefinition == typeof(ICollection<>) || genericTypeDefinition == typeof(IList<>)) {
typeToCreate = typeof(List<>).MakeGenericType(modelType.GetGenericArguments());
}
}
// fallback to the type's default constructor
return Activator.CreateInstance(typeToCreate);
}
private MethodInfo GetParameterlessCreate(Type typeToCreate)
{
var methodInfos = typeToCreate.GetMethods(BindingFlags.Static | BindingFlags.Public);
foreach (var methodInfo in methodInfos)
{
if (methodInfo.IsPublic && methodInfo.IsStatic && methodInfo.Name.Equals("Create") && methodInfo.GetParameters().Length == 0)
return methodInfo;
}
return null;
}