[MVC] Remove obsolete APIs

- Removed ViewsFeatureProvider
- Removed PageArgumentBinder and its internal implementation DefaultPageArgumentBinder.
- Removed corresponding test classes/methods for all the above.
- Reacted to class/member changes in dependencies.

#7326
This commit is contained in:
N. Taylor Mullen 2019-02-08 09:40:24 -08:00
parent 597c8434d5
commit dfddc4e8ff
10 changed files with 3 additions and 431 deletions

View File

@ -36,7 +36,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationParts
foreach (var item in provider.CompiledItems)
{
var descriptor = new CompiledViewDescriptor(item, attribute: null);
var descriptor = new CompiledViewDescriptor(item);
feature.ViewDescriptors.Add(descriptor);
}
}

View File

@ -1,111 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using Microsoft.AspNetCore.Mvc.ApplicationParts;
using Microsoft.Extensions.Primitives;
namespace Microsoft.AspNetCore.Mvc.Razor.Compilation
{
/// <summary>
/// An <see cref="IApplicationFeatureProvider{TFeature}"/> for <see cref="ViewsFeature"/>.
/// </summary>
[Obsolete("This type is obsolete and will be removed in a future version. See " + nameof(IRazorCompiledItemProvider) + " for alternatives.")]
public class ViewsFeatureProvider : IApplicationFeatureProvider<ViewsFeature>
{
public static readonly string PrecompiledViewsAssemblySuffix = ".PrecompiledViews";
/// <inheritdoc />
public void PopulateFeature(IEnumerable<ApplicationPart> parts, ViewsFeature feature)
{
foreach (var assemblyPart in parts.OfType<AssemblyPart>())
{
var viewAttributes = GetViewAttributes(assemblyPart)
.Select(attribute => (Attribute: attribute, RelativePath: ViewPath.NormalizePath(attribute.Path)));
var duplicates = viewAttributes.GroupBy(a => a.RelativePath, StringComparer.OrdinalIgnoreCase)
.FirstOrDefault(g => g.Count() > 1);
if (duplicates != null)
{
// Ensure parts do not specify views with differing cases. This is not supported
// at runtime and we should flag at as such for precompiled views.
var viewsDifferingInCase = string.Join(Environment.NewLine, duplicates.Select(d => d.RelativePath));
var message = string.Join(
Environment.NewLine,
Resources.RazorViewCompiler_ViewPathsDifferOnlyInCase,
viewsDifferingInCase);
throw new InvalidOperationException(message);
}
foreach (var (attribute, relativePath) in viewAttributes)
{
var viewDescriptor = new CompiledViewDescriptor
{
ExpirationTokens = Array.Empty<IChangeToken>(),
RelativePath = relativePath,
ViewAttribute = attribute,
};
feature.ViewDescriptors.Add(viewDescriptor);
}
}
}
/// <summary>
/// Gets the sequence of <see cref="RazorViewAttribute"/> instances associated with the specified <paramref name="assemblyPart"/>.
/// </summary>
/// <param name="assemblyPart">The <see cref="AssemblyPart"/>.</param>
/// <returns>The sequence of <see cref="RazorViewAttribute"/> instances.</returns>
protected virtual IEnumerable<RazorViewAttribute> GetViewAttributes(AssemblyPart assemblyPart)
{
if (assemblyPart == null)
{
throw new ArgumentNullException(nameof(assemblyPart));
}
var featureAssembly = GetFeatureAssembly(assemblyPart);
if (featureAssembly != null)
{
return featureAssembly.GetCustomAttributes<RazorViewAttribute>();
}
return Enumerable.Empty<RazorViewAttribute>();
}
private static Assembly GetFeatureAssembly(AssemblyPart assemblyPart)
{
if (assemblyPart.Assembly.IsDynamic || string.IsNullOrEmpty((string)assemblyPart.Assembly.Location))
{
return null;
}
var precompiledAssemblyFileName = assemblyPart.Assembly.GetName().Name
+ PrecompiledViewsAssemblySuffix
+ ".dll";
var precompiledAssemblyFilePath = Path.Combine(
Path.GetDirectoryName(assemblyPart.Assembly.Location),
precompiledAssemblyFileName);
if (File.Exists(precompiledAssemblyFilePath))
{
try
{
return Assembly.LoadFile(precompiledAssemblyFilePath);
}
catch (FileLoadException)
{
// Don't throw if assembly cannot be loaded. This can happen if the file is not a managed assembly.
}
}
return null;
}
}
}

View File

@ -72,13 +72,6 @@ namespace Microsoft.Extensions.DependencyInjection
{
builder.PartManager.FeatureProviders.Add(new RazorCompiledItemFeatureProvider());
}
#pragma warning disable CS0618 // Type or member is obsolete
if (!builder.PartManager.FeatureProviders.OfType<ViewsFeatureProvider>().Any())
{
builder.PartManager.FeatureProviders.Add(new ViewsFeatureProvider());
}
#pragma warning restore CS0618 // Type or member is obsolete
}
/// <summary>

View File

@ -114,11 +114,6 @@ namespace Microsoft.Extensions.DependencyInjection
services.TryAddSingleton<IPageLoader, DefaultPageLoader>();
services.TryAddSingleton<IPageHandlerMethodSelector, DefaultPageHandlerMethodSelector>();
// Page model binding
#pragma warning disable CS0618 // Type or member is obsolete
services.TryAddSingleton<PageArgumentBinder, DefaultPageArgumentBinder>();
#pragma warning restore CS0618 // Type or member is obsolete
// Action executors
services.TryAddSingleton<PageResultExecutor>();

View File

@ -1,50 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.ModelBinding;
namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
{
#pragma warning disable CS0618 // Type or member is obsolete
internal class DefaultPageArgumentBinder : PageArgumentBinder
#pragma warning restore CS0618 // Type or member is obsolete
{
private readonly ParameterBinder _parameterBinder;
public DefaultPageArgumentBinder(ParameterBinder binder)
{
_parameterBinder = binder;
}
protected override async Task<ModelBindingResult> BindAsync(PageContext pageContext, object value, string name, Type type)
{
var valueProvider = await GetCompositeValueProvider(pageContext);
var parameterDescriptor = new ParameterDescriptor
{
BindingInfo = null,
Name = name,
ParameterType = type,
};
#pragma warning disable CS0618 // Type or member is obsolete
return await _parameterBinder.BindModelAsync(pageContext, valueProvider, parameterDescriptor, value);
#pragma warning restore CS0618 // Type or member is obsolete
}
private static async Task<CompositeValueProvider> GetCompositeValueProvider(PageContext pageContext)
{
var factories = pageContext.ValueProviderFactories;
var valueProviderFactoryContext = new ValueProviderFactoryContext(pageContext);
for (var i = 0; i < factories.Count; i++)
{
var factory = factories[i];
await factory.CreateValueProviderAsync(valueProviderFactoryContext);
}
return new CompositeValueProvider(valueProviderFactoryContext.ValueProviders);
}
}
}

View File

@ -1,44 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.ModelBinding;
namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
{
[Obsolete("This type is obsolete and will be removed in a future version.")]
public abstract class PageArgumentBinder
{
public async Task<object> BindModelAsync(PageContext context, Type type, object @default, string name)
{
var result = await BindAsync(context, null, name, type);
return result.IsModelSet ? result.Model : @default;
}
public Task<TModel> BindModelAsync<TModel>(PageContext context, string name)
{
return BindModelAsync(context, default(TModel), name);
}
public async Task<TModel> BindModelAsync<TModel>(PageContext context, TModel @default, string name)
{
var result = await BindAsync(context, null, name, typeof(TModel));
return result.IsModelSet ? (TModel)result.Model : @default;
}
public async Task<bool> TryUpdateModelAsync<TModel>(PageContext context, TModel value)
{
var result = await BindAsync(context, value, string.Empty, typeof(TModel));
return result.IsModelSet && context.ModelState.IsValid;
}
public async Task<bool> TryUpdateModelAsync<TModel>(PageContext context, TModel value, string name)
{
var result = await BindAsync(context, value, name, typeof(TModel));
return result.IsModelSet && context.ModelState.IsValid;
}
protected abstract Task<ModelBindingResult> BindAsync(PageContext context, object value, string name, Type type);
}
}

View File

@ -19,17 +19,6 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
private readonly RazorPageBase _page;
private readonly Type _modelType;
[Obsolete("This constructor is obsolete and will be removed in a future version.")]
public RazorPageAdapter(RazorPageBase page)
{
if (page == null)
{
throw new ArgumentNullException(nameof(page));
}
_page = page;
}
public RazorPageAdapter(RazorPageBase page, Type modelType)
{
_page = page ?? throw new ArgumentNullException(nameof(page));

View File

@ -35,7 +35,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationParts
}
[Fact]
public void PopulateFeature_PopulatesRazorViewAttributeFromTypeAssembly()
public void PopulateFeature_PopulatesRazorCompiledItemsFromTypeAssembly()
{
// Arrange
var item1 = Mock.Of<RazorCompiledItem>(i => i.Identifier == "Item1" && i.Type == typeof(TestView));

View File

@ -1,197 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using Microsoft.AspNetCore.Mvc.ApplicationParts;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.Razor.Compilation
{
#pragma warning disable CS0618 // Type or member is obsolete
public class ViewsFeatureProviderTest
{
[Fact]
public void PopulateFeature_ReturnsEmptySequenceIfNoAssemblyPartHasViewAssembly()
{
// Arrange
var applicationPartManager = new ApplicationPartManager();
applicationPartManager.ApplicationParts.Add(
new AssemblyPart(typeof(ViewsFeatureProviderTest).GetTypeInfo().Assembly));
applicationPartManager.FeatureProviders.Add(new ViewsFeatureProvider());
var feature = new ViewsFeature();
// Act
applicationPartManager.PopulateFeature(feature);
// Assert
Assert.Empty(feature.ViewDescriptors);
}
[Fact]
public void PopulateFeature_ReturnsViewsFromAllAvailableApplicationParts()
{
// Arrange
var part1 = new AssemblyPart(typeof(object).GetTypeInfo().Assembly);
var part2 = new AssemblyPart(GetType().GetTypeInfo().Assembly);
var featureProvider = new TestableViewsFeatureProvider(new Dictionary<AssemblyPart, IEnumerable<RazorViewAttribute>>
{
{
part1,
new[]
{
new RazorViewAttribute("/Views/test/Index.cshtml", typeof(object)),
}
},
{
part2,
new[]
{
new RazorViewAttribute("/Areas/Admin/Views/Index.cshtml", typeof(string)),
new RazorViewAttribute("/Areas/Admin/Views/About.cshtml", typeof(int)),
}
},
});
var applicationPartManager = new ApplicationPartManager();
applicationPartManager.ApplicationParts.Add(part1);
applicationPartManager.ApplicationParts.Add(part2);
applicationPartManager.FeatureProviders.Add(featureProvider);
var feature = new ViewsFeature();
// Act
applicationPartManager.PopulateFeature(feature);
// Assert
Assert.Collection(feature.ViewDescriptors.OrderBy(f => f.RelativePath, StringComparer.Ordinal),
view =>
{
Assert.Equal("/Areas/Admin/Views/About.cshtml", view.RelativePath);
Assert.Equal(typeof(int), view.ViewAttribute.ViewType);
},
view =>
{
Assert.Equal("/Areas/Admin/Views/Index.cshtml", view.RelativePath);
Assert.Equal(typeof(string), view.ViewAttribute.ViewType);
},
view =>
{
Assert.Equal("/Views/test/Index.cshtml", view.RelativePath);
Assert.Equal(typeof(object), view.ViewAttribute.ViewType);
});
}
[Fact]
public void PopulateFeature_ThrowsIfSingleAssemblyContainsMultipleAttributesWithTheSamePath()
{
// Arrange
var path1 = "/Views/test/Index.cshtml";
var path2 = "/views/test/index.cshtml";
var expected = string.Join(
Environment.NewLine,
"The following precompiled view paths differ only in case, which is not supported:",
path1,
path2);
var part = new AssemblyPart(typeof(object).GetTypeInfo().Assembly);
var featureProvider = new TestableViewsFeatureProvider(new Dictionary<AssemblyPart, IEnumerable<RazorViewAttribute>>
{
{
part,
new[]
{
new RazorViewAttribute(path1, typeof(object)),
new RazorViewAttribute(path2, typeof(object)),
}
},
});
var applicationPartManager = new ApplicationPartManager();
applicationPartManager.ApplicationParts.Add(part);
applicationPartManager.FeatureProviders.Add(featureProvider);
var feature = new ViewsFeature();
// Act & Assert
var ex = Assert.Throws<InvalidOperationException>(() => applicationPartManager.PopulateFeature(feature));
Assert.Equal(expected, ex.Message);
}
[Fact]
public void PopulateFeature_ReturnsEmptySequenceIfNoDynamicAssemblyPartHasViewAssembly()
{
// Arrange
var name = new AssemblyName($"DynamicAssembly-{Guid.NewGuid()}");
var assembly = AssemblyBuilder.DefineDynamicAssembly(name,
AssemblyBuilderAccess.RunAndCollect);
var applicationPartManager = new ApplicationPartManager();
applicationPartManager.ApplicationParts.Add(new AssemblyPart(assembly));
applicationPartManager.FeatureProviders.Add(new ViewsFeatureProvider());
var feature = new ViewsFeature();
// Act
applicationPartManager.PopulateFeature(feature);
// Assert
Assert.Empty(feature.ViewDescriptors);
}
[Fact]
public void PopulateFeature_DoesNotFail_IfAssemblyHasEmptyLocation()
{
// Arrange
var assembly = new AssemblyWithEmptyLocation();
var applicationPartManager = new ApplicationPartManager();
applicationPartManager.ApplicationParts.Add(new AssemblyPart(assembly));
applicationPartManager.FeatureProviders.Add(new ViewsFeatureProvider());
var feature = new ViewsFeature();
// Act
applicationPartManager.PopulateFeature(feature);
// Assert
Assert.Empty(feature.ViewDescriptors);
}
private class TestableViewsFeatureProvider : ViewsFeatureProvider
{
private readonly Dictionary<AssemblyPart, IEnumerable<RazorViewAttribute>> _attributeLookup;
public TestableViewsFeatureProvider(Dictionary<AssemblyPart, IEnumerable<RazorViewAttribute>> attributeLookup)
{
_attributeLookup = attributeLookup;
}
protected override IEnumerable<RazorViewAttribute> GetViewAttributes(AssemblyPart assemblyPart)
{
return _attributeLookup[assemblyPart];
}
}
private class AssemblyWithEmptyLocation : Assembly
{
public override string Location => string.Empty;
public override string FullName => typeof(ViewsFeatureProviderTest).GetTypeInfo().Assembly.FullName;
public override IEnumerable<TypeInfo> DefinedTypes
{
get
{
throw new NotImplementedException();
}
}
public override IEnumerable<Module> Modules
{
get
{
throw new NotImplementedException();
}
}
}
}
#pragma warning restore CS0618 // Type or member is obsolete
}

View File

@ -209,10 +209,7 @@ namespace Microsoft.AspNetCore.Mvc
feature => Assert.IsType<ControllerFeatureProvider>(feature),
feature => Assert.IsType<ViewComponentFeatureProvider>(feature),
feature => Assert.IsType<TagHelperFeatureProvider>(feature),
feature => Assert.IsType<RazorCompiledItemFeatureProvider>(feature),
#pragma warning disable CS0618 // Type or member is obsolete
feature => Assert.IsType<ViewsFeatureProvider>(feature));
#pragma warning restore CS0618 // Type or member is obsolete
feature => Assert.IsType<RazorCompiledItemFeatureProvider>(feature));
}
[Fact]