The change here is to always use the provided formatter, instead of using
it as a fallback. This is much less surprising for users.
There are some other subtle changes here and cleanup of the tests, as well
as documentation additions.
The primary change is that we still want to run 'select' on a formatter
even if it's the only one. This allows us to choose a content type based
on the accept header.
In the case of a user-provided formatter, we'll try to honor the best
possible combination of Accept and specified ContentTypes (specified
ContentTypes win if there's a conflict). If nothing works, we'll still run
the user-provided formatter and let it decide what to do.
In the case of the default (formatters from options) we do conneg, and if
there's a conflict, fall back to a global (from services)
JsonOutputFormatter - we let it decide what to do.
This should leave us with a defined and tested behavior for all cases.
- ensure `ViewDataDictionary` constructors are not passed a `null` or
`Mock.Of<IModelMetadataProvider>()` instance
- `ViewDataDictionary` constructors always use the `IModelMetadataProvider`
- `viewData.ModelMetadata` now never `null`
- `ViewDataDictionary<int>.Model` no longer throws if read before it's written
- `ViewDataDictionary.ModelMetadata` now copied to new instances in fewer cases
- e.g. don't use unusual `object` datatype with customized `ModelMetadata`
- `null` checks can generally be removed but in one case needed to
special-case metadata for `object`
nit: remove duplicate empty `string` check in
`ExpressionMetadataProvider.FromStringExpression()`
- ensure correct `ViewData.ModelState` value in a `RazorPage` instance
- let view components inherit `ViewData.Model` from surrounding context
- do not create an isolated `ViewDataDictionary<TModel>` in one HTML helper
- allows previous `ViewDataDictionary<TModel>` constructor to be `internal`
- also add comments for that constructor
- change two `ViewDataDictionary` copy constructor calls to avoid uselessly
copying `Model` and related information from source
- `ViewDataDictionary<TModel>` constructors now pass `typeof(TModel)` to base
`protected` constructors
- move type compatibility checks into base `ViewDataDictionary`
- remove `ViewDataDictionary<TModel>.ModelMetadata` override
- don't retrieve `ModelMetadata` twice in a single constructor invocation
- remove newly-unused `protected` properties and use `private` fields in copy
constructors
Address longstanding problems found (see #1466)
- avoid reusing `ModelMetadata` after `Model` value changes
- reset `ModelMetadata` backing field in `Model` setter
- `Model` and `ModelMetadata.Model` could previously get out of sync
- carry `ModelMetadata` forward from an outer scope only if `Model` matches
- previously two scopes could have different `Model` values but share their
`ModelMetadata` (and `ModelMetadata.Model`)
- related to previous item but didn't require `Model` setting; switching
to a property of the same type as containing `Model` was enough
- avoid NRE if `ViewDataDictionary<int>.Model` is read before it's written
- problem affected all non-`Nullable` value types
- `ViewDataDictionary.ModelMetadata` setter should throw if value is `null`
nits:
- add and reword doc and code comments
- `ViewDataDictionary<TModel>` constructors should only inherit base's
parameter descriptions; have more information in the derived class
- make a few `ViewDataDictionary` properties get-only
- clean up `using` statements in `ViewDataDictionary<TModel>`
- make two constructors `internal`
This change adds the concept of a full-name to viewcomponents. View
components can be invoked using either the short name or long name. If the
provided string contains a '.' character, then it will be compared against
full names, otherwise it will be matched against short names only.
The short name is used for view lookups.
If the name is explicitly set via ViewComponent attribute, then the full
name is the name provided. The short name is the portion of the name after
the last '.'. If there are no dots, then the short name and full name are
the same.
If the name is not set explicitly, then it is inferred from the Type and
namespace name. The short name is the Type name, minus the 'ViewComponent'
suffix (if present). The full name is the namespace of the defining class,
plus the short name.
Taking the suggestion here to move these to a sub-object. This is future
proof in the event that we need to capture more data for ApiExplorer, and
reads better.
ViewComponents and Controllers now follow the same rules exactly for what
types of classes they can be.
Also corrected a bug in a test for controllers. Closed-generic types can
be controllers, the test was wrong.
never that for `object`
- `ViewDataDictionary<TModel>.ModelMetadata` was for `object` after base
copy constructor got value from `ViewDataDictionary<object>`
- problem led to #1426 symptoms
- with copy constructor leaving `base.ModelMetadata==null` more often,
`ViewDataDictionary<TModel>.ModelMetadata` usually tracks `TModel` if
`Model==null`
nit:
- fix existing comment in main `ViewDataDictionary` copy constructor
- Cleaned up some existing bad code that worked around case sensitive TagHelperOutput.Attributes.
- Modified MergeAttributes to be case insensitive when it comes to merging class attributes.
- Added a test to verify the new MergeAttribute case insensitive class merging.
aspnet/Razor#186
- This involved adding the StringComparer.OrdinalIgnoreCase comparer to the TagBuilder's Attributes dictionary.
- Added tests to validate that all methods that made use of TagBuilder.Attributes abide by the new ignore case mechanic.
- Added two sets of tests to validate the new functionality of Object => Dictionary HTML helper tests.
- Modified a functional test that utilizes HTML Helpers to provide same attribute-different case objects.
- Fixed existing HTML helper tests to account for new ordering of attrbutes (dictionary no longer adds key value pairs, it sets them).
#1328
This also comes with a rename of the namespace
Microsoft.AspNet.Mvc.ApplicationModel to
Microsoft.AspNet.Mvc.ApplicationModels.
Also tuned up some parameter and variable names for increased
understandability.