Mvc: Make ExpressionHelper.GetExpressionText, ExpressionMetadataProvider.From* public

Created on 14 Nov 2018  路  7Comments  路  Source: aspnet/Mvc

We are using the following public APIs from the Microsoft.AspNetCore.Mvc.ViewFeatures namespace:
ExpressionHelper.GetExpressionText
ExpressionMetadataProvider.FromLambdaExpression
ExpressionMetadataProvider.FromStringExpression

in our custom HtmlHelper extensions to integrate them with a model and client-side validation:

  • ExpressionHelper.GetExpressionText is used to calculate a property name to bind by using a lambda expression passed in the MyHelperFor method (Html.MyHelperFor(m => m.MyProperty)).
  • ExpressionMetadataProvider.FromLambdaExpression and ExpressionMetadataProvider.FromStringExpression help us get a model's metadata by a string or lambda expression and view data. The model metadata is used for getting validation rules.

Making these types internal in #8705 you break our integration with the model. Are there public replacements for these APIs?

3 - Done 1 - Required S enhancement

Most helpful comment

Are there alternative APIs to be used here?

Not that I'm aware of. The equivalent APIs were public in MVC5, so we these seem like good candidates to be made public. I'll bring this up in the API review meeting today.

All 7 comments

Thanks for contacting us, @San4es.
As you know, these APIs were never meant to be consumed by our customers. While that was the case, we understand that some of these changes would break some apps, as peopled do consume what they get access to after all.
@pranavkm, what would be the way to go around this? Are there alternative APIs to be used here?

Are there alternative APIs to be used here?

Not that I'm aware of. The equivalent APIs were public in MVC5, so we these seem like good candidates to be made public. I'll bring this up in the API review meeting today.

We have an API review for these types and have a draft proposal for this. The plan is to make ExpressionHelper and ExpressionMetadataProvider services in the DI container with the followship shape:

```C#
public class ExpressionHelper
{
string GetExpressionText(LambdaExpression expression);
string GetCachedExpressionText(LambdaExpression expression);
}

public class ExpressionMetadataProvider
{
ModelExplorer FromLambdaExpression(
Expression> expression,
ViewDataDictionary viewData);

ModelExplorer FromStringExpression(
string expression,
ViewDataDictionary viewData);
}
```

Moving this issue to 3.0.0-preview1 milestone

@pranavkm @mkArtakMSFT

Thank you for your replies.

The plan is to make ExpressionHelper and ExpressionMetadataProvider services in the DI
ExpressionMetadataProvider -> public and make it non-static (put it in DI)

Do I understand correctly that the code for getting an instance of this classes in my helpers will look like below?

var expressionMetadataProvider = ViewContext.HttpContext.RequestServices.GetService<ExpressionMetadataProvider>();
var expressionHelper = ViewContext.HttpContext.RequestServices.GetService<ExpressionHelper>();

How can I save backward compatibility with ASP.NET Core 2.x simultaneously with 3.x versions?

Do I understand correctly that the code for getting an instance of this classes in my helpers will look like below?

Most likely if these are static extension methods on IHtmlHelper. @dougbu had some design suggestions here that I did not note down.

How can I save backward compatibility with ASP.NET Core 2.x simultaneously with 3.x versions?

Even if we were to leave the signature as-is and make the types public, at minimum, you'll have to reconcile referencing the namespace differences between 2.0 and 3.0. 3.0 also adds additional dimensions to multi-targeting and I think @davidfowl or @natemcmaster might have more insights in to what package authors need to do.

more insights in to what package authors need to do.

I don't have full context on what you are trying to do, but in general, if you want to write a library that supports multiple versions of ASP.NET Core, you need to multi-target. Your project file might look like this:

<PropertyGroup>
    <TargetFrameworks>netstandard2.0;netcoreapp3.0</TargetFrameworks>
</PropertyGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
    <PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.1.0" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp3.0'">
    <FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>

In C#, you change which API you compile for using #if's.

```c#

if NETCOREAPP3_0

NewApiFrom30.DoSomething()

else

Internal21Api.DoSomething()

endif

```

"supports" is too broad. If you want to specifically target features in 3.0 then you need to multi target.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

eat-sleep-code picture eat-sleep-code  路  40Comments

johnnyoshika picture johnnyoshika  路  57Comments

dougbu picture dougbu  路  54Comments

dotnetjunkie picture dotnetjunkie  路  43Comments

xaviergxf picture xaviergxf  路  35Comments