Skip to content
This repository has been archived by the owner on Dec 19, 2018. It is now read-only.

Add support for @namespace to configure the namespace the generated class is in #1159

Closed
DamianEdwards opened this issue Apr 3, 2017 · 14 comments

Comments

@DamianEdwards
Copy link
Member

The Problem

The classes that are generated by Razor files are placed in a fixed namespace of AspNetCore.

As part of Razor Pages, it is idiomatic to specify the page's model using @model, e.g.:

@* ~/Pages/Index.cshtml *@
@page
@model IndexModel

The class IndexModel is specified in a sibling CS file named for the CSHTML file it applies to, e.g. Index.cshtml.cs

In page-focused layout it's also normal to nest identically named files in sub-folders as the site structure is built up, e.g.

~/
  /Pages
    _ViewImports.cshtml
    Index.cshtml
    Index.cshtml.cs <-- namespace App.Pages { public class IndexModel : PageModel {
    /Account
      Index.cshtml
      Index.cshtml.cs <-- namespace App.Pages.Account { public class IndexModel : PageModel {
      /Manage
        Index.cshtml
        Index.cshtml.cs <-- namespace App.Pages.Account.Manage { public class IndexModel : PageModel {

It's also common to have a _ViewImports.cshtml in the pages root to set the default namespace imports for all pages, e.g.:

@using App
@using App.Pages

You might also include subsequent _ViewImports.cshtml files in the sub-folders to import namespaces for types declared in those sub-folders, e.g.:

@using App.Pages.Account.Manage

This results in it being impossible to refer to page models via @model without using the fully qualified class name (including namespace), as there are multiple namespaces imported where the model class name is present. This isn't ideal as it makes it harder to move the page around independently of its content, such that it breaks if you decide to reorganize the layout.

@* ~/Pages/Account/Manage/Index.cshtml *@
@page
@model App.Pages.Account.Manage.IndexModel

The Proposal

Let's introduce support for changing the namespace of the generated view/page class via the @namespace directive. This directive would be set in the _ViewImports.cshtml of new ASP.NET Core projects (via the project templates) to the application's root namespace by default, much like the default @using directives are today.

The directive will take a very similar form to the @using directive, allowing specification of a namespace following white-space after the directive. At design-time it will be projected to the namespace declaration line in the generated file buffer.

@namespace App.Pages

Only a single instance of the @namespace directive is supported, and it can be overridden by more specific _ViewImports.cshtml files (more specific based on folder depth, similar to @model).

The actual namespace used will be the combination of that specified by the closest @namespace directive in a _ViewImports.cshtml flie, combined with the names of the sub-folders down to the folder containing the page (the path). This way, the namespace will typically only need to be set once for the application in the root _ViewImports.cshtml file, e.g.:

~/
  /Pages
    _ViewImports.cshtml <-- @namespace App.Pages
    Index.cshtml <-- generated namespace: App.Pages
    Index.cshtml.cs <-- namespace App.Pages { public class IndexModel : PageModel {
    /Account
      Index.cshtml <-- generated namespace: App.Pages.Account
      Index.cshtml.cs <-- namespace App.Pages.Account { public class IndexModel : PageModel {
      /Manage
        Index.cshtml <-- generated namespace: App.Pages.Account.Manage
        Index.cshtml.cs <-- namespace App.Pages.Account.Manage { public class IndexModel : PageModel {
      /Custom
        _ViewImports.cshtml <-- @namespace Custom
        Index.cshtml <-- generated namespace: Custom
        Index.cshtml.cs <-- namespace Custom { public class IndexModel : PageModel {

If no @namespace is specified in a view/page's directives (inherited or direct), the existing default namespace AspNetCore will be used.

@DamianEdwards
Copy link
Member Author

@rynowak
Copy link
Member

rynowak commented Apr 3, 2017

Love it. Does it work for views as well?

@DamianEdwards
Copy link
Member Author

@rynowak of course!

@analogrelay
Copy link
Contributor

My only concern about the automatic folder-name-based namespace comes from this:

This isn't ideal as it makes it harder to move the page around independently of its content, such that it breaks if you decide to reorganize the layout.

I think most users would be OK with this, but do we want to consider an opt-out mechanism for those who do care? The opt-out here would just be for the folder-name part, where we concat folder names to the closest @namespace value

@rynowak
Copy link
Member

rynowak commented Apr 3, 2017

The opt-out would be putting the @namespace in your page/view directly, or just not using @namespace and thus not taking a dependency on the value that it would configure.

@DamianEdwards
Copy link
Member Author

Yep, what @rynowak said.

@analogrelay
Copy link
Contributor

Good with me, as long as we're comfortable with it. Is it going to be breaking to do this for Razor views (or rather, is it going to be breaking in a way that we care about?). They'll end up in subfolder namespaces now, when they didn't before (or is the idea that we wouldn't do any namespace generation unless we saw an @namespace directive?).

@DamianEdwards
Copy link
Member Author

It's totally opt-in, we intend to update the templates, not the in-box defaults.

@analogrelay
Copy link
Contributor

Ok, it wasn't clear in my first reading that this statement referred to the final namespace for the page, rather than the base namespace (since the previous section was talking about finding the base namespace, to which we would add the folder names)

If no @namespace is specified in a view/page's directives (inherited or direct), the existing default namespace AspNetCore will be used.

@rynowak rynowak self-assigned this Apr 4, 2017
@rynowak rynowak added this to the 2.0.0-preview1 milestone Apr 4, 2017
@rynowak
Copy link
Member

rynowak commented Apr 4, 2017

@DamianEdwards - I like the idea that we'd made this effect the generated class name as well.

For instance - here's your folder structure with the current kinds of names we generate now:

~/
  /Pages
    _ViewImports.cshtml <-- @namespace App.Pages
    Index.cshtml <-- namespace App.Pages { public class __Pages__Index : Page }
    Index.cshtml.cs <-- namespace App.Pages { public class IndexModel : PageModel {
    /Account
      Index.cshtml <-- namespace App.Pages.Account { public class __Pages__Account__Index : Page }
      Index.cshtml.cs <-- namespace App.Pages.Account { public class IndexModel : PageModel {
      /Manage
        Index.cshtml <-- namespace App.Pages.Account.Manage { public class __Pages__Account__Manage__Index : Page }
        Index.cshtml.cs <-- namespace App.Pages.Account.Manage { public class IndexModel : PageModel {
      /Custom
        _ViewImports.cshtml <-- @namespace Custom
        Index.cshtml <--  namespace Custom { public class __Pages__Account__Custom__Index : Page }
        Index.cshtml.cs <-- namespace Custom { public class IndexModel : PageModel {

And here's what I'm pitching. This is pretty much exactly what the prototype does.

~/
  /Pages
    _ViewImports.cshtml <-- @namespace App.Pages
    Index.cshtml <-- namespace App.Pages { public class Index_Page : Page }
    Index.cshtml.cs <-- namespace App.Pages { public class IndexModel : PageModel {
    /Account
      Index.cshtml <-- namespace App.Pages.Account { public class Index_Page : Page }
      Index.cshtml.cs <-- namespace App.Pages.Account { public class IndexModel : PageModel {
      /Manage
        Index.cshtml <-- namespace App.Pages.Account.Manage { public class Index_Page : Page }
        Index.cshtml.cs <-- namespace App.Pages.Account.Manage { public class IndexModel : PageModel {
      /Custom
        _ViewImports.cshtml <-- @namespace Custom
        Index.cshtml <--  namespace Custom { public class Index_Page : Page }
        Index.cshtml.cs <-- namespace Custom { public class IndexModel : PageModel {

@DamianEdwards
Copy link
Member Author

Looks great. What would the class name be if we didn't do that?

@DamianEdwards
Copy link
Member Author

Ignore me, I suck. Love it, let's do it.

@pranavkm
Copy link
Contributor

pranavkm commented Apr 4, 2017

We should figure out if there's a risk of type name collisions when we do precompiled views. They're all compiled in to a single assembly, unlike during runtime compilation.

@rynowak rynowak added this to Working in 2.0.0 Preview 1 Apr 11, 2017
@rynowak
Copy link
Member

rynowak commented Apr 11, 2017

e5cac9f

@rynowak rynowak closed this as completed Apr 11, 2017
@rynowak rynowak moved this from Working to Done in 2.0.0 Preview 1 Apr 13, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
No open projects
Development

No branches or pull requests

4 participants