尽管通过构造函数传递依赖关系的建议我发现,拥有无参数构造函数然后自动装配所有属性的开发成本显着降低,并使应用程序更容易开发和维护。但有时(例如在视图模型上)我有一个在容器中注册的属性,但我不想在构造中填充(例如绑定到容器的选定项)。
有没有办法告诉容器在自动装配其余属性时忽略某些属性?
目前我只是重置了在激活的事件中标有属性的属性a la:
public static IRegistrationBuilder<TLimit, ScanningActivatorData, TRegistrationStyle>
PropertiesAutowiredExtended<TLimit, TRegistrationStyle>(
this IRegistrationBuilder<TLimit, ScanningActivatorData, TRegistrationStyle> builder)
{
builder.ActivatorData.ConfigurationActions.Add(
(type, innerBuilder) =>
{
var parameter = Expression.Parameter(typeof(object));
var cast = Expression.Convert(parameter, type);
var assignments = type.GetProperties()
.Where(candidate => candidate.HasAttribute<NotAutowiredAttribute>())
.Select(property => new { Property = property, Expression = Expression.Property(cast, property) })
.Select(data => Expression.Assign(data.Expression, Expression.Default(data.Property.PropertyType)))
.Cast<Expression>()
.ToArray();
if (assignments.Any())
{
var @action = Expression
.Lambda<Action<object>>(Expression.Block(assignments), parameter)
.Compile();
innerBuilder.OnActivated(e =>
{
e.Context.InjectUnsetProperties(e.Instance);
@action(e.Instance);
});
}
else
{
innerBuilder.OnActivated(e => e.Context.InjectUnsetProperties(e.Instance));
}
});
return builder;
}
有更好的方法吗?
答案 0 :(得分:2)
不确定这是一个更好的,但你可以从另一边,通过WithProperty语法只注册所需的属性。优点是Autofac无法解决不必要的服务。这是一个有效的例子:
public class MyClass
{
public MyDependency MyDependency { get; set; }
public MyDependency MyExcludeDependency { get; set; }
}
public class MyDependency {}
public class Program
{
public static void Main(string[] args)
{
var builder = new ContainerBuilder();
builder.RegisterType<MyDependency>();
builder.RegisterType<MyClass>().WithPropertiesAutowiredExcept("MyExcludeDependency");
using (var container = builder.Build())
{
var myClass = container.Resolve<MyClass>();
Console.WriteLine(myClass.MyDependency == null);
Console.WriteLine(myClass.MyExcludeDependency == null);
}
}
}
public static class PropertiesAutowiredExtensions
{
// Extension that registers only needed properties
// Filters by property name for simplicity
public static IRegistrationBuilder<TLimit, TReflectionActivatorData, TRegistrationStyle>
WithPropertiesAutowiredExcept<TLimit, TReflectionActivatorData, TRegistrationStyle>(
this IRegistrationBuilder<TLimit, TReflectionActivatorData, TRegistrationStyle> registrationBuilder,
params string[] propertiesNames)
where TReflectionActivatorData : ReflectionActivatorData
{
var type = ((IServiceWithType)registrationBuilder.RegistrationData.Services.Single()).ServiceType;
foreach (var property in type
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(pi => pi.CanWrite && !propertiesNames.Contains(pi.Name)))
{
// There's no additional checks like in PropertiesAutowired for simplicity
// You can add them from Autofac.Core.Activators.Reflection.AutowiringPropertyInjector.InjectProperties
var localProperty = property;
registrationBuilder.WithProperty(
new ResolvedParameter(
(pi, c) =>
{
PropertyInfo prop;
return pi.TryGetDeclaringProperty(out prop) &&
prop.Name == localProperty.Name;
},
(pi, c) => c.Resolve(localProperty.PropertyType)));
}
return registrationBuilder;
}
// From Autofac.Util.ReflectionExtensions
public static bool TryGetDeclaringProperty(this ParameterInfo pi, out PropertyInfo prop)
{
var mi = pi.Member as MethodInfo;
if (mi != null && mi.IsSpecialName && mi.Name.StartsWith("set_", StringComparison.Ordinal)
&& mi.DeclaringType != null)
{
prop = mi.DeclaringType.GetProperty(mi.Name.Substring(4));
return true;
}
prop = null;
return false;
}
}