Fix for #1271 - Add copy constructors for ApplicationModel types.
This commit is contained in:
parent
5b2948dd73
commit
19fbcdf5a8
|
|
@ -4,8 +4,6 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Microsoft.AspNet.Mvc.Description;
|
|
||||||
using Microsoft.AspNet.Mvc.Routing;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.ApplicationModel
|
namespace Microsoft.AspNet.Mvc.ApplicationModel
|
||||||
{
|
{
|
||||||
|
|
@ -22,6 +20,31 @@ namespace Microsoft.AspNet.Mvc.ApplicationModel
|
||||||
Parameters = new List<ParameterModel>();
|
Parameters = new List<ParameterModel>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ActionModel([NotNull] ActionModel other)
|
||||||
|
{
|
||||||
|
ActionMethod = other.ActionMethod;
|
||||||
|
ActionName = other.ActionName;
|
||||||
|
ApiExplorerGroupName = other.ApiExplorerGroupName;
|
||||||
|
ApiExplorerIsVisible = other.ApiExplorerIsVisible;
|
||||||
|
IsActionNameMatchRequired = other.IsActionNameMatchRequired;
|
||||||
|
|
||||||
|
// Not making a deep copy of the controller, this action still belongs to the same controller.
|
||||||
|
Controller = other.Controller;
|
||||||
|
|
||||||
|
// These are just metadata, safe to create new collections
|
||||||
|
ActionConstraints = new List<IActionConstraintMetadata>(other.ActionConstraints);
|
||||||
|
Attributes = new List<object>(other.Attributes);
|
||||||
|
Filters = new List<IFilter>(other.Filters);
|
||||||
|
HttpMethods = new List<string>(other.HttpMethods);
|
||||||
|
|
||||||
|
// Make a deep copy of other 'model' types.
|
||||||
|
Parameters = new List<ParameterModel>(other.Parameters.Select(p => new ParameterModel(p)));
|
||||||
|
|
||||||
|
if (other.AttributeRouteModel != null)
|
||||||
|
{
|
||||||
|
AttributeRouteModel = new AttributeRouteModel(other.AttributeRouteModel);
|
||||||
|
}
|
||||||
|
}
|
||||||
public List<IActionConstraintMetadata> ActionConstraints { get; private set; }
|
public List<IActionConstraintMetadata> ActionConstraints { get; private set; }
|
||||||
|
|
||||||
public MethodInfo ActionMethod { get; private set; }
|
public MethodInfo ActionMethod { get; private set; }
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,14 @@ namespace Microsoft.AspNet.Mvc.ApplicationModel
|
||||||
Name = templateProvider.Name;
|
Name = templateProvider.Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AttributeRouteModel([NotNull] AttributeRouteModel other)
|
||||||
|
{
|
||||||
|
Attribute = other.Attribute;
|
||||||
|
Name = other.Name;
|
||||||
|
Order = other.Order;
|
||||||
|
Template = other.Template;
|
||||||
|
}
|
||||||
|
|
||||||
public IRouteTemplateProvider Attribute { get; private set; }
|
public IRouteTemplateProvider Attribute { get; private set; }
|
||||||
|
|
||||||
public string Template { get; set; }
|
public string Template { get; set; }
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// 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.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.ApplicationModel
|
namespace Microsoft.AspNet.Mvc.ApplicationModel
|
||||||
|
|
@ -21,6 +21,28 @@ namespace Microsoft.AspNet.Mvc.ApplicationModel
|
||||||
RouteConstraints = new List<RouteConstraintAttribute>();
|
RouteConstraints = new List<RouteConstraintAttribute>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ControllerModel([NotNull] ControllerModel other)
|
||||||
|
{
|
||||||
|
ApiExplorerGroupName = other.ApiExplorerGroupName;
|
||||||
|
ApiExplorerIsVisible = other.ApiExplorerIsVisible;
|
||||||
|
ControllerName = other.ControllerName;
|
||||||
|
ControllerType = other.ControllerType;
|
||||||
|
|
||||||
|
// Still part of the same application
|
||||||
|
Application = other.Application;
|
||||||
|
|
||||||
|
// These are just metadata, safe to create new collections
|
||||||
|
ActionConstraints = new List<IActionConstraintMetadata>(other.ActionConstraints);
|
||||||
|
Attributes = new List<object>(other.Attributes);
|
||||||
|
Filters = new List<IFilter>(other.Filters);
|
||||||
|
RouteConstraints = new List<RouteConstraintAttribute>(other.RouteConstraints);
|
||||||
|
|
||||||
|
// Make a deep copy of other 'model' types.
|
||||||
|
Actions = new List<ActionModel>(other.Actions.Select(a => new ActionModel(a)));
|
||||||
|
AttributeRoutes = new List<AttributeRouteModel>(
|
||||||
|
other.AttributeRoutes.Select(a => new AttributeRouteModel(a)));
|
||||||
|
}
|
||||||
|
|
||||||
public List<IActionConstraintMetadata> ActionConstraints { get; private set; }
|
public List<IActionConstraintMetadata> ActionConstraints { get; private set; }
|
||||||
|
|
||||||
public List<ActionModel> Actions { get; private set; }
|
public List<ActionModel> Actions { get; private set; }
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,15 @@ namespace Microsoft.AspNet.Mvc.ApplicationModel
|
||||||
Attributes = new List<object>();
|
Attributes = new List<object>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ParameterModel([NotNull] ParameterModel other)
|
||||||
|
{
|
||||||
|
Action = other.Action;
|
||||||
|
Attributes = new List<object>(other.Attributes);
|
||||||
|
IsOptional = other.IsOptional;
|
||||||
|
ParameterInfo = other.ParameterInfo;
|
||||||
|
ParameterName = other.ParameterName;
|
||||||
|
}
|
||||||
|
|
||||||
public ActionModel Action { get; set; }
|
public ActionModel Action { get; set; }
|
||||||
|
|
||||||
public List<object> Attributes { get; private set; }
|
public List<object> Attributes { get; private set; }
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,97 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. 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.Reflection;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.ApplicationModel
|
||||||
|
{
|
||||||
|
public class ActionModelTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void CopyConstructor_DoesDeepCopyOfOtherModels()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var action = new ActionModel(typeof(TestController).GetMethod("Edit"));
|
||||||
|
|
||||||
|
var parameter = new ParameterModel(action.ActionMethod.GetParameters()[0]);
|
||||||
|
parameter.Action = action;
|
||||||
|
action.Parameters.Add(parameter);
|
||||||
|
|
||||||
|
var route = new AttributeRouteModel(new HttpGetAttribute("api/Products"));
|
||||||
|
action.AttributeRouteModel = route;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var action2 = new ActionModel(action);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotSame(action, action2.Parameters[0]);
|
||||||
|
Assert.NotSame(route, action2.AttributeRouteModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CopyConstructor_CopiesAllProperties()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var action = new ActionModel(typeof(TestController).GetMethod("Edit"));
|
||||||
|
|
||||||
|
action.ActionConstraints.Add(new HttpMethodConstraint(new string[] { "GET" }));
|
||||||
|
action.ActionName = "Edit";
|
||||||
|
action.ApiExplorerGroupName = "group";
|
||||||
|
action.ApiExplorerIsVisible = true;
|
||||||
|
action.Attributes.Add(new HttpGetAttribute());
|
||||||
|
action.Controller = new ControllerModel(typeof(TestController).GetTypeInfo());
|
||||||
|
action.Filters.Add(new AuthorizeAttribute());
|
||||||
|
action.HttpMethods.Add("GET");
|
||||||
|
action.IsActionNameMatchRequired = true;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var action2 = new ActionModel(action);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
foreach (var property in typeof(ActionModel).GetProperties())
|
||||||
|
{
|
||||||
|
if (property.Name.Equals("Parameters") || property.Name.Equals("AttributeRouteModel"))
|
||||||
|
{
|
||||||
|
// This test excludes other ApplicationModel objects on purpose because we deep copy them.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var value1 = property.GetValue(action);
|
||||||
|
var value2 = property.GetValue(action2);
|
||||||
|
|
||||||
|
if (typeof(IEnumerable<object>).IsAssignableFrom(property.PropertyType))
|
||||||
|
{
|
||||||
|
Assert.Equal<object>((IEnumerable<object>)value1, (IEnumerable<object>)value2);
|
||||||
|
|
||||||
|
// Ensure non-default value
|
||||||
|
Assert.NotEmpty((IEnumerable<object>)value1);
|
||||||
|
}
|
||||||
|
else if (property.PropertyType.IsValueType ||
|
||||||
|
Nullable.GetUnderlyingType(property.PropertyType) != null)
|
||||||
|
{
|
||||||
|
Assert.Equal(value1, value2);
|
||||||
|
|
||||||
|
// Ensure non-default value
|
||||||
|
Assert.NotEqual(value1, Activator.CreateInstance(property.PropertyType));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Assert.Same(value1, value2);
|
||||||
|
|
||||||
|
// Ensure non-default value
|
||||||
|
Assert.NotNull(value1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestController
|
||||||
|
{
|
||||||
|
public void Edit(int id)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -10,6 +10,49 @@ namespace Microsoft.AspNet.Mvc.ApplicationModel
|
||||||
{
|
{
|
||||||
public class AttributeRouteModelTests
|
public class AttributeRouteModelTests
|
||||||
{
|
{
|
||||||
|
[Fact]
|
||||||
|
public void CopyConstructor_CopiesAllProperties()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var route = new AttributeRouteModel(new HttpGetAttribute("/api/Products"));
|
||||||
|
|
||||||
|
route.Name = "products";
|
||||||
|
route.Order = 5;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var route2 = new AttributeRouteModel(route);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
foreach (var property in typeof(AttributeRouteModel).GetProperties())
|
||||||
|
{
|
||||||
|
var value1 = property.GetValue(route);
|
||||||
|
var value2 = property.GetValue(route2);
|
||||||
|
|
||||||
|
if (typeof(IEnumerable<object>).IsAssignableFrom(property.PropertyType))
|
||||||
|
{
|
||||||
|
Assert.Equal<object>((IEnumerable<object>)value1, (IEnumerable<object>)value2);
|
||||||
|
|
||||||
|
// Ensure non-default value
|
||||||
|
Assert.NotEmpty((IEnumerable<object>)value1);
|
||||||
|
}
|
||||||
|
else if (property.PropertyType.IsValueType ||
|
||||||
|
Nullable.GetUnderlyingType(property.PropertyType) != null)
|
||||||
|
{
|
||||||
|
Assert.Equal(value1, value2);
|
||||||
|
|
||||||
|
// Ensure non-default value
|
||||||
|
Assert.NotEqual(value1, Activator.CreateInstance(property.PropertyType));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Assert.Same(value1, value2);
|
||||||
|
|
||||||
|
// Ensure non-default value
|
||||||
|
Assert.NotNull(value1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData(null, null, null)]
|
[InlineData(null, null, null)]
|
||||||
[InlineData("", null, "")]
|
[InlineData("", null, "")]
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,102 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. 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.Reflection;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.ApplicationModel
|
||||||
|
{
|
||||||
|
public class ControllerModelTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void CopyConstructor_DoesDeepCopyOfOtherModels()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var controller = new ControllerModel(typeof(TestController).GetTypeInfo());
|
||||||
|
|
||||||
|
var action = new ActionModel(typeof(TestController).GetMethod("Edit"));
|
||||||
|
controller.Actions.Add(action);
|
||||||
|
action.Controller = controller;
|
||||||
|
|
||||||
|
var route = new AttributeRouteModel(new HttpGetAttribute("api/Products"));
|
||||||
|
controller.AttributeRoutes.Add(route);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var controller2 = new ControllerModel(controller);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotSame(action, controller2.Actions[0]);
|
||||||
|
Assert.NotSame(route, controller2.AttributeRoutes[0]);
|
||||||
|
|
||||||
|
Assert.NotSame(controller.ActionConstraints, controller2.ActionConstraints);
|
||||||
|
Assert.NotSame(controller.Actions, controller2.Actions);
|
||||||
|
Assert.NotSame(controller.Attributes, controller2.Attributes);
|
||||||
|
Assert.NotSame(controller.Filters, controller2.Filters);
|
||||||
|
Assert.NotSame(controller.RouteConstraints, controller2.RouteConstraints);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CopyConstructor_CopiesAllProperties()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var controller = new ControllerModel(typeof(TestController).GetTypeInfo());
|
||||||
|
|
||||||
|
controller.ActionConstraints.Add(new HttpMethodConstraint(new string[] { "GET" }));
|
||||||
|
controller.ApiExplorerGroupName = "group";
|
||||||
|
controller.ApiExplorerIsVisible = true;
|
||||||
|
controller.Application = new GlobalModel();
|
||||||
|
controller.Attributes.Add(new HttpGetAttribute());
|
||||||
|
controller.ControllerName = "cool";
|
||||||
|
controller.Filters.Add(new AuthorizeAttribute());
|
||||||
|
controller.RouteConstraints.Add(new AreaAttribute("Admin"));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var controller2 = new ControllerModel(controller);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
foreach (var property in typeof(ControllerModel).GetProperties())
|
||||||
|
{
|
||||||
|
if (property.Name.Equals("Actions") || property.Name.Equals("AttributeRoutes"))
|
||||||
|
{
|
||||||
|
// This test excludes other ApplicationModel objects on purpose because we deep copy them.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var value1 = property.GetValue(controller);
|
||||||
|
var value2 = property.GetValue(controller2);
|
||||||
|
|
||||||
|
if (typeof(IEnumerable<object>).IsAssignableFrom(property.PropertyType))
|
||||||
|
{
|
||||||
|
Assert.Equal<object>((IEnumerable<object>)value1, (IEnumerable<object>)value2);
|
||||||
|
|
||||||
|
// Ensure non-default value
|
||||||
|
Assert.NotEmpty((IEnumerable<object>)value1);
|
||||||
|
}
|
||||||
|
else if (property.PropertyType.IsValueType ||
|
||||||
|
Nullable.GetUnderlyingType(property.PropertyType) != null)
|
||||||
|
{
|
||||||
|
Assert.Equal(value1, value2);
|
||||||
|
|
||||||
|
// Ensure non-default value
|
||||||
|
Assert.NotEqual(value1, Activator.CreateInstance(property.PropertyType));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Assert.Same(value1, value2);
|
||||||
|
|
||||||
|
// Ensure non-default value
|
||||||
|
Assert.NotNull(value1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestController
|
||||||
|
{
|
||||||
|
public void Edit()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,64 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. 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 Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.ApplicationModel
|
||||||
|
{
|
||||||
|
public class ParameterModelTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void CopyConstructor_CopiesAllProperties()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var parameter = new ParameterModel(typeof(TestController).GetMethod("Edit").GetParameters()[0]);
|
||||||
|
|
||||||
|
parameter.Action = new ActionModel(typeof(TestController).GetMethod("Edit"));
|
||||||
|
parameter.Attributes.Add(new FromBodyAttribute());
|
||||||
|
parameter.IsOptional = true;
|
||||||
|
parameter.ParameterName = "id";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var parameter2 = new ParameterModel(parameter);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
foreach (var property in typeof(ParameterModel).GetProperties())
|
||||||
|
{
|
||||||
|
var value1 = property.GetValue(parameter);
|
||||||
|
var value2 = property.GetValue(parameter2);
|
||||||
|
|
||||||
|
if (typeof(IEnumerable<object>).IsAssignableFrom(property.PropertyType))
|
||||||
|
{
|
||||||
|
Assert.Equal<object>((IEnumerable<object>)value1, (IEnumerable<object>)value2);
|
||||||
|
|
||||||
|
// Ensure non-default value
|
||||||
|
Assert.NotEmpty((IEnumerable<object>)value1);
|
||||||
|
}
|
||||||
|
else if (property.PropertyType.IsValueType ||
|
||||||
|
Nullable.GetUnderlyingType(property.PropertyType) != null)
|
||||||
|
{
|
||||||
|
Assert.Equal(value1, value2);
|
||||||
|
|
||||||
|
// Ensure non-default value
|
||||||
|
Assert.NotEqual(value1, Activator.CreateInstance(property.PropertyType));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Assert.Same(value1, value2);
|
||||||
|
|
||||||
|
// Ensure non-default value
|
||||||
|
Assert.NotNull(value1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestController
|
||||||
|
{
|
||||||
|
public void Edit(int id)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue