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

Comments

@Bartmax
Copy link
Contributor

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

This comment has been minimized.

Copy link

commented May 24, 2016

@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

This comment has been minimized.

Copy link

commented May 24, 2016

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

This comment has been minimized.

Copy link

commented May 24, 2016

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

This comment has been minimized.

Copy link

commented May 25, 2016

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

This comment has been minimized.

Copy link
Member

commented May 25, 2016

@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

This comment has been minimized.

Copy link
Member

commented May 25, 2016

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

@joeaudette

This comment has been minimized.

Copy link

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

This comment has been minimized.

Copy link
Member

commented May 25, 2016

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

This comment has been minimized.

Copy link

commented May 25, 2016

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

@pranavkm

This comment has been minimized.

Copy link
Member

commented May 25, 2016

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

This comment has been minimized.

Copy link

commented May 25, 2016

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

This comment has been minimized.

Copy link

commented May 25, 2016

@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

This comment has been minimized.

Copy link
Member

commented May 25, 2016

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

@joeaudette

This comment has been minimized.

Copy link

commented May 26, 2016

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

This comment has been minimized.

Copy link
Member

commented May 30, 2016

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

@IsaacSee

This comment has been minimized.

Copy link

commented Jun 1, 2016

@aspnet-hello

This comment has been minimized.

Copy link

commented Dec 30, 2017

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.

natemcmaster added a commit that referenced this issue Oct 17, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
6 participants
You can’t perform that action at this time.