Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to restore cshtml from nuget packages. #1490

Closed
Bartmax opened this issue May 21, 2016 · 17 comments
Closed

How to restore cshtml from nuget packages. #1490

Bartmax opened this issue May 21, 2016 · 17 comments

Comments

@Bartmax
Copy link
Contributor

Bartmax commented May 21, 2016

If I use packInclude on my project, I get to include .cshtml file on my nuget package. That's great!

Is there any way so that I include this package into another project and restores those .cshtml?

Like:

ProjectA produces -> ProjectAWithCshtmlFiles
ProbjecB have ProjectA dependency and restores .cshtml files on dotnet restore.

If that's not possible, creating a dotnet tool for that exact purpose is doable ?

The scenario I'm trying to do is to bake an interface for my package so consumers can see and opt in to create new ones (not override the existing one) or use the provided as default.

@Bartmax Bartmax changed the title Add cshtml to nuget packages. How to restore cshtml from nuget packages. May 21, 2016
@joeaudette
Copy link

@Bartmax I'm about to start experimenting because I also need to package views in nuget and have them installed into the consuming project. I was told it would be possible in beta8, then it was supposed to be possible in rc1, then it was supposed to be possible in rc2, so I've been waiting patiently for this to be possible and counting on it working in rc2

For the moment I'm assuming it is possible in rc2 but haven't seen any good docs on how yet, so I'm about to start trying to figure it out and will let you know if I find a solution. In the meantime if anyone else reading this has more info or guidance please share.

@joeaudette
Copy link

so far I've managed to get files into my package like this:

 "packOptions": {
    "tags": [ "foo", "views" ],
    "files": {
        "mappings": {
            "content/Views/Foo/": "Views/**"
        }
    }
}

but I'm not having any luck getting the view delivered into the consuming project.
This is one of the most important features I've been waiting for so I hope I'm missing something.

@joeaudette
Copy link

cross referencing this with the stackoverflow question where I first asked about it http://stackoverflow.com/questions/31412210/how-to-include-views-in-asp-net-5-class-library-nuget-package

@joeaudette
Copy link

ok, so the best alternative that I have found since we cannot get content delivered into a project from the nuget is instead to add the views in my library as embedded resources like this:

"buildOptions": {
    "embed": "Views/**"
},

then for the consuming app to be able to resolve those embedded views we have to add an EmbeddedFileProvider to the RazorViewOptions which could be done like this:

services.Configure<RazorViewEngineOptions>(options =>
{
     options.FileProviders.Add(new EmbeddedFileProvider(
                typeof(FooLibrary.Controllers.FooController).GetTypeInfo().Assembly,
                "FooLibrary"
            ));

});

however to reduce the ugliness and reduce the using statements needed in Startup of the app, I'm encapsulating that instead inside an extension method like this:

    using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.Extensions.FileProviders;
using System.Reflection;
namespace Microsoft.Extensions.DependencyInjection
{
    public static class ServiceCollectionExtensions
    {

        public static RazorViewEngineOptions AddEmbeddedViewsForFoo(this RazorViewEngineOptions options)
        {
            options.FileProviders.Add(new EmbeddedFileProvider(
                    typeof(FooLibrary.Controllers.FooController).GetTypeInfo().Assembly,
                    "FooLibrary"
                ));

            return options;
        }
    }
}

then the startup code be be cleaner like this:

  services.Configure<RazorViewEngineOptions>(options =>
   {
         options.AddEmbeddedViewsForFoo();     
   });

then I will provide documentation that the user can download the views and add them to their views folder and will not need to add the EmbeddedFileProvider and will be able to customize the views

my only concern is whether this approach if taken by multiple library authors will add lots of EmbeddedFileProviders and whether there is any perf issues with that.

would appreciate any feedback from the team on whether this approach is a good idea or not, it at least provides me a way to move forward with packaging my nugets in a way that is easy to consume

@pranavkm
Copy link
Contributor

@joeaudette that looks pretty good. You could use ConfigureOptions to make the Startup code nicer yet:

public static IServiceCollection AddEmbeddedViewsForFoo(this IServiceCollection services)
{
    var optionsSetup = new ConfigureOptions<RazorViewEngineOptions>(options =>
    {
        options.FileProviders.Add(new EmbeddedFileProvider(
            typeof(FooLibrary.Controllers.FooController).GetTypeInfo().Assembly,
            "FooLibrary"
            ));
    });
    services.AddSingleton<IConfigureOptions<RazorViewEngineOptions>>(optionsSetup);

    return services;
}

// Startup code:
services.AddEmbeddedViewsForFoo();

@pranavkm
Copy link
Contributor

@joeaudette - apart from the increased memory foot print of your assemblies, there shouldn't be significant costs to adding views this way.

@joeaudette
Copy link

joeaudette commented May 25, 2016

@pranavkm will that work if more than one library does the same thing? ie what if another library already added a singleton for RazorViewEngineOptions like that?

I want to make sure I'm not blocking the app developer that is consuming my nuget from doing other configuration on those options such as adding viewlocation expanders etc

@pranavkm
Copy link
Contributor

This pattern should scale fine as long as the call to AddEmbeddedViewsForFoo isn't duplicated. You could look at what Mvc does with TryAddEnumerable and creating distinct subtypes of ConfigureOptions if you're concerned about guarding against duplicate invocations.

@joeaudette
Copy link

@pranavkm but if another call is doing something similar with AddEmbeddedViewsForBaz will there be any problem?

@pranavkm
Copy link
Contributor

AFAIK, it should be fine. It might be problematic if

  1. AddEmbeddedViewsForBaz calls AddEmbeddedViewsForFoo. (it'll register the file provider twice, so there's more work now during view lookups)
  2. AddEmbeddedViewsForBaz does TryAddEnumerable with an instance of ConfigureOptions. This might cause this call to skip registration entirely

That said, it might be nice to give it a try and see how it goes.

@joeaudette
Copy link

somehow this pattern seems more friendly towards the consuming app developer to me

services.AddMvc()
        .AddRazorOptions(options =>
    {
        options.AddEmbeddedViewsForFoo();
        options.AddEmbeddedViewsForBaz();
        options.AddEmbeddedViewsForBar();
        options.ViewLocationExpanders.Add(new MyCoolViewLocationExpander());
        ...
    });

I want to make sure the app developer has full control over things since he owns the startup code in his app and I want to to be fairly clear what my extension methods are doing under the hood and not interfere with other things he may want to do

@joeaudette
Copy link

@pranavkm thanks very much for your re-assuring feedback, I started out feeling really worried about not being able to deliver views from nuget, but now I'm thinking this approach with embedded views where the user can still opt out of the embedded views and download the views if they want to customize them is really going to be a good solution for me.

@pranavkm
Copy link
Contributor

Similar should be fine, calling the exact method might be troublesome.

@joeaudette
Copy link

yes, there would be no reason to ever call one of these methods more than once, just one method per library that needs to add its own embedded views. if the consuming developer uses these method calls more than once in their startup code I would consider that a mistake in their code

@davidfowl
Copy link
Member

This should be moved to https://github.com/nuget/home

@IsaacSee
Copy link

IsaacSee commented Jun 1, 2016

@aspnet-hello
Copy link

This issue is being closed because it has not been updated in 3 months.

We apologize if this causes any inconvenience. We ask that if you are still encountering this issue, please log a new issue with updated information and we will investigate.

@dotnet dotnet locked as resolved and limited conversation to collaborators Dec 4, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants