Add ModelExpression code generation.

- Sealed the ModelExpression.
- We use the stringified version of the ModelExpression type name to detect ModelExpression properties on TagHelpers. This is so the MvcRazorHost can work in tooling and in runtime.
- Created a GeneratedTagHelperAttributeContext to represent the specific stringified versions of the ModelExpression assets.
- Created an MvcTagHelperAttributeValueCodeRenderer to modify rendering of ModelExpression properties.

#1241
This commit is contained in:
NTaylorMullen 2014-10-07 20:50:20 -07:00
parent ad76a3c8fb
commit 2005c3cd85
5 changed files with 121 additions and 3 deletions

View File

@ -10,7 +10,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
/// <summary> /// <summary>
/// Describes an <see cref="System.Linq.Expressions.Expression"/> passed to a tag helper. /// Describes an <see cref="System.Linq.Expressions.Expression"/> passed to a tag helper.
/// </summary> /// </summary>
public class ModelExpression public sealed class ModelExpression
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ModelExpression"/> class. /// Initializes a new instance of the <see cref="ModelExpression"/> class.

View File

@ -0,0 +1,23 @@
// 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 Microsoft.AspNet.Razor.Runtime.TagHelpers;
namespace Microsoft.AspNet.Mvc.Razor
{
/// <summary>
/// Contains information for the <see cref="ITagHelper"/> attribute code generation process.
/// </summary>
public class GeneratedTagHelperAttributeContext
{
/// <summary>
/// Name of the model expression type.
/// </summary>
public string ModelExpressionTypeName { get; set; }
/// <summary>
/// Name the method to create <c>ModelExpression</c>s.
/// </summary>
public string CreateModelExpressionMethodName { get; set; }
}
}

View File

@ -11,20 +11,34 @@ namespace Microsoft.AspNet.Mvc.Razor
{ {
public class MvcCSharpCodeBuilder : CSharpCodeBuilder public class MvcCSharpCodeBuilder : CSharpCodeBuilder
{ {
private readonly GeneratedTagHelperAttributeContext _tagHelperAttributeContext;
private readonly string _defaultModel; private readonly string _defaultModel;
private readonly string _activateAttribute; private readonly string _activateAttribute;
public MvcCSharpCodeBuilder([NotNull] CodeBuilderContext context, public MvcCSharpCodeBuilder([NotNull] CodeBuilderContext context,
[NotNull] string defaultModel, [NotNull] string defaultModel,
[NotNull] string activateAttribute) [NotNull] string activateAttribute,
[NotNull] GeneratedTagHelperAttributeContext tagHelperAttributeContext)
: base(context) : base(context)
{ {
_tagHelperAttributeContext = tagHelperAttributeContext;
_defaultModel = defaultModel; _defaultModel = defaultModel;
_activateAttribute = activateAttribute; _activateAttribute = activateAttribute;
} }
private string Model { get; set; } private string Model { get; set; }
protected override CSharpCodeVisitor CreateCSharpCodeVisitor([NotNull] CSharpCodeWriter writer,
[NotNull] CodeBuilderContext context)
{
var csharpCodeVisitor = base.CreateCSharpCodeVisitor(writer, context);
csharpCodeVisitor.TagHelperRenderer.AttributeValueCodeRenderer =
new MvcTagHelperAttributeValueCodeRenderer(_tagHelperAttributeContext);
return csharpCodeVisitor;
}
protected override CSharpCodeWritingScope BuildClassDeclaration(CSharpCodeWriter writer) protected override CSharpCodeWritingScope BuildClassDeclaration(CSharpCodeWriter writer)
{ {
// Grab the last model chunk so it gets intellisense. // Grab the last model chunk so it gets intellisense.

View File

@ -153,6 +153,22 @@ namespace Microsoft.AspNet.Mvc.Razor
get { return "Microsoft.AspNet.Mvc.ActivateAttribute"; } get { return "Microsoft.AspNet.Mvc.ActivateAttribute"; }
} }
/// <summary>
/// Gets the type name used to represent <see cref="ITagHelper"/> model expression properties.
/// </summary>
public virtual string ModelExpressionType
{
get { return "Microsoft.AspNet.Mvc.Rendering.ModelExpression"; }
}
/// <summary>
/// Gets the method name used to create model expressions.
/// </summary>
public virtual string CreateModelExpressionMethod
{
get { return "CreateModelExpression"; }
}
/// <inheritdoc /> /// <inheritdoc />
public GeneratorResults GenerateCode(string rootRelativePath, Stream inputStream) public GeneratorResults GenerateCode(string rootRelativePath, Stream inputStream)
{ {
@ -173,7 +189,15 @@ namespace Microsoft.AspNet.Mvc.Razor
[NotNull] CodeBuilderContext context) [NotNull] CodeBuilderContext context)
{ {
UpdateCodeBuilder(context); UpdateCodeBuilder(context);
return new MvcCSharpCodeBuilder(context, DefaultModel, ActivateAttribute);
return new MvcCSharpCodeBuilder(context,
DefaultModel,
ActivateAttribute,
new GeneratedTagHelperAttributeContext
{
ModelExpressionTypeName = ModelExpressionType,
CreateModelExpressionMethodName = CreateModelExpressionMethod
});
} }
private void UpdateCodeBuilder(CodeGeneratorContext context) private void UpdateCodeBuilder(CodeGeneratorContext context)

View File

@ -0,0 +1,57 @@
// 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 Microsoft.AspNet.Razor.Generator;
using Microsoft.AspNet.Razor.Generator.Compiler.CSharp;
using Microsoft.AspNet.Razor.TagHelpers;
namespace Microsoft.AspNet.Mvc.Razor
{
/// <inheritdoc />
public class MvcTagHelperAttributeValueCodeRenderer : TagHelperAttributeValueCodeRenderer
{
private const string ModelLambdaVariableName = "__model";
private readonly GeneratedTagHelperAttributeContext _context;
/// <summary>
/// Instantiates a new instance of <see cref="MvcTagHelperAttributeValueCodeRenderer"/>.
/// </summary>
/// <param name="context">Contains code generation information for rendering attribute values.</param>
public MvcTagHelperAttributeValueCodeRenderer([NotNull] GeneratedTagHelperAttributeContext context)
{
_context = context;
}
/// <inheritdoc />
/// <remarks>If the attribute being rendered is of the type
/// <see cref="GeneratedTagHelperAttributeContext.ModelExpressionTypeName"/> then a model expression will be
/// created by calling into <see cref="GeneratedTagHelperAttributeContext.CreateModelExpressionMethodName"/>.
/// </remarks>
public override void RenderAttributeValue([NotNull] TagHelperAttributeDescriptor attributeDescriptor,
[NotNull] CSharpCodeWriter writer,
[NotNull] CodeBuilderContext codeBuilderContext,
[NotNull] Action<CSharpCodeWriter> renderAttributeValue)
{
var propertyType = attributeDescriptor.PropertyInfo.PropertyType;
if (propertyType.FullName.Equals(_context.ModelExpressionTypeName, StringComparison.Ordinal))
{
writer.WriteStartMethodInvocation(_context.CreateModelExpressionMethodName)
.Write(ModelLambdaVariableName)
.Write(" => ")
.Write(ModelLambdaVariableName)
.Write(".");
renderAttributeValue(writer);
writer.WriteEndMethodInvocation(endLine: false);
}
else
{
base.RenderAttributeValue(attributeDescriptor, writer, codeBuilderContext, renderAttributeValue);
}
}
}
}