GetPropertyValueを要求されたときに、そのプロパティがモデルアセンブリのオブジェクトであるかどうかを調べ、IRepository <>が自分のNInject IKernelに登録されているカスタムモデルバインダーです。 NinjectからIRepositoryを取得できる場合、IRepositoryを使用してそれを使用して外部キーオブジェクトを取得します。
public class ForeignKeyModelBinder : System.Web.Mvc.DefaultModelBinder
{
private IKernel serviceLocator;
public ForeignKeyModelBinder(IKernel serviceLocator)
{
Check.Require(serviceLocator, "IKernel is required");
this.serviceLocator = serviceLocator;
}
/// <summary>
/// if the property type being asked for has a IRepository registered in the service locator,
/// use that to retrieve the instance. if not, use the default behavior.
/// </summary>
protected override object GetPropertyValue(ControllerContext controllerContext, ModelBindingContext bindingContext,
PropertyDescriptor propertyDescriptor, IModelBinder propertyBinder)
{
var submittedValue = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
if (submittedValue == null)
{
string fullPropertyKey = CreateSubPropertyName(bindingContext.ModelName, "Id");
submittedValue = bindingContext.ValueProvider.GetValue(fullPropertyKey);
}
if (submittedValue != null)
{
var value = TryGetFromRepository(submittedValue.AttemptedValue, propertyDescriptor.PropertyType);
if (value != null)
return value;
}
return base.GetPropertyValue(controllerContext, bindingContext, propertyDescriptor, propertyBinder);
}
protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
{
string fullPropertyKey = CreateSubPropertyName(bindingContext.ModelName, "Id");
var submittedValue = bindingContext.ValueProvider.GetValue(fullPropertyKey);
if (submittedValue != null)
{
var value = TryGetFromRepository(submittedValue.AttemptedValue, modelType);
if (value != null)
return value;
}
return base.CreateModel(controllerContext, bindingContext, modelType);
}
private object TryGetFromRepository(string key, Type propertyType)
{
if (CheckRepository(propertyType) && !string.IsNullOrEmpty(key))
{
Type genericRepositoryType = typeof(IRepository<>);
Type specificRepositoryType = genericRepositoryType.MakeGenericType(propertyType);
var repository = serviceLocator.TryGet(specificRepositoryType);
int id = 0;
#if DEBUG
Check.Require(repository, "{0} is not available for use in binding".FormatWith(specificRepositoryType.FullName));
#endif
if (repository != null && Int32.TryParse(key, out id))
{
return repository.InvokeMethod("GetById", id);
}
}
return null;
}
/// <summary>
/// perform simple check to see if we should even bother looking for a repository
/// </summary>
private bool CheckRepository(Type propertyType)
{
return propertyType.HasInterface<IModelObject>();
}
}
DI容器と自分のリポジトリタイプをNinjectに置き換えることはできます。
非常に参考になる例!しかし、IModelBinderProvider'インターフェースを使用してバインダーの内部をチェックするのではなく、モデルのバインダーをこのモデルバインダーの対象としています。 Brad Wilsonはこれについて[http://bradwilson.typepad.com/blog/2010/10/service-location-pt9-model-binders.html]について書きました。 –
はい、それは素晴らしいでしょう。私はまだMVC3に更新していない。 –