diff --git a/src/Microsoft.AspNetCore.Blazor/Components/ComponentFactory.cs b/src/Microsoft.AspNetCore.Blazor/Components/ComponentFactory.cs index 76be3fdadd..305bb5393b 100644 --- a/src/Microsoft.AspNetCore.Blazor/Components/ComponentFactory.cs +++ b/src/Microsoft.AspNetCore.Blazor/Components/ComponentFactory.cs @@ -52,16 +52,25 @@ namespace Microsoft.AspNetCore.Blazor.Components // Do all the reflection up front var injectableProperties = type.GetTypeInfo() .GetProperties(_injectablePropertyBindingFlags) - .Where(p => p.GetCustomAttribute() != null) - .Where(p => p.SetMethod != null); + .Where(p => p.GetCustomAttribute() != null); var injectables = injectableProperties.Select(property => - ( - propertyName: property.Name, - propertyType: property.PropertyType, - setter: (IPropertySetter)Activator.CreateInstance( - typeof(PropertySetter<,>).MakeGenericType(type, property.PropertyType), - property.SetMethod) - )).ToArray(); + { + if (property.SetMethod == null) + { + throw new InvalidOperationException($"Cannot provide a value for property " + + $"'{property.Name}' on type '{type.FullName}' because the property " + + $"has no setter."); + } + + return + ( + propertyName: property.Name, + propertyType: property.PropertyType, + setter: (IPropertySetter)Activator.CreateInstance( + typeof(PropertySetter<,>).MakeGenericType(type, property.PropertyType), + property.SetMethod) + ); + }).ToArray(); // Return an action whose closure can write all the injected properties // without any further reflection calls (just typecasts) @@ -72,7 +81,7 @@ namespace Microsoft.AspNetCore.Blazor.Components var serviceInstance = _serviceProvider.GetService(injectable.propertyType); if (serviceInstance == null) { - throw new InvalidOperationException($"Cannot provide value for property " + + throw new InvalidOperationException($"Cannot provide a value for property " + $"'{injectable.propertyName}' on type '{type.FullName}'. There is no " + $"registered service of type '{injectable.propertyType}'."); } diff --git a/test/Microsoft.AspNetCore.Blazor.Test/DependencyInjectionTest.cs b/test/Microsoft.AspNetCore.Blazor.Test/DependencyInjectionTest.cs index 957a3b37f0..f4d790a430 100644 --- a/test/Microsoft.AspNetCore.Blazor.Test/DependencyInjectionTest.cs +++ b/test/Microsoft.AspNetCore.Blazor.Test/DependencyInjectionTest.cs @@ -42,13 +42,16 @@ namespace Microsoft.AspNetCore.Blazor.Test } [Fact] - public void IgnoresGetOnlyProperties() + public void ThrowsForInjectablePropertiesWithoutSetter() { - // Arrange/Act - var component = InstantiateComponent(); + var ex = Assert.Throws(() => + { + InstantiateComponent(); + }); - // Assert - Assert.Null(component.MyService); + Assert.Equal($"Cannot provide a value for property '{nameof(HasInjectableProperty.MyService)}' " + + $"on type '{typeof(HasGetOnlyPropertyWithInject).FullName}' because the property " + + $"has no setter.", ex.Message); } [Fact] @@ -59,7 +62,7 @@ namespace Microsoft.AspNetCore.Blazor.Test InstantiateComponent(); }); - Assert.Equal($"Cannot provide value for property '{nameof(HasInjectableProperty.MyService)}' " + + Assert.Equal($"Cannot provide a value for property '{nameof(HasInjectableProperty.MyService)}' " + $"on type '{typeof(HasInjectableProperty).FullName}'. There is no registered service " + $"of type '{typeof(IMyService).FullName}'.", ex.Message); } @@ -130,7 +133,7 @@ namespace Microsoft.AspNetCore.Blazor.Test public static IMyService StaticPropertyWithoutInject { get; set; } } - class HasGetOnlyProperty : TestComponent + class HasGetOnlyPropertyWithInject : TestComponent { [Inject] public IMyService MyService { get; } }