这是我的看法 - 这是一个自定义模型绑定器,当要求 GetPropertyValue 时,它会查看该属性是否是我的模型程序集中的一个对象,并且在我的 NInject IKernel 中注册了一个 IRepository<>。如果它可以从 Ninject 获得 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>();
}
}
您显然可以用 Ninject 代替您的 DI 容器和您自己的存储库类型。