4

I'm suffering trying to get some views from a library to the main project. I was starting to read about creating your own VirtualPathProvider implementation here: Using VirtualPathProvider to load ASP.NET MVC views from DLLs

I had to set my view = EmbbebedResource to get the resource from the library. But now is throwing another error.

In the header of my partial view I had the following:

@model Contoso.ExercisesLibrary.AbsoluteArithmetic.Problem1

And the error says: External component has thrown an exception. c:\Users\Oscar\AppData\Local\Temp\Temporary ASP.NET Files\root\4f78c765\7f9a47c6\App_Web_contoso.exerciseslibrary.absolutearithmetic.view1.cshtml.38e14c22.y-yjyt6g.0.cs(46): error CS0103: The name 'model' does not exist in the current context

I don't know why the compiler tells that cannot recognized my model. When I'm in design mode, I can see the compiler that the check is all right.

Check the image

enter image description here

What am I doing wrong o what am I missing? Thanks in advance.

Community
  • 1
  • 1
Darf Zon
  • 6,268
  • 20
  • 90
  • 149

4 Answers4

7

Try adding an @inherits directive to the top of your razor view:

@inherits System.Web.Mvc.WebViewPage
@model Contoso.ExercisesLibrary.AbsoluteArithmetic.Problem1

The reason you need this is because your view comes from an embedded resource and not from the standard ~/Views location. And as you know inside ~/Views there's a file called web.config. And inside this file there's a pageBaseType="System.Web.Mvc.WebViewPage" directive indicating that all Razor files inside ~/Views should inherit from this base type. But since your view is now coming from an unknown location you have nowhere specified that it should be a System.Web.Mvc.WebViewPage. And all the MVC specific stuff such as models, HTML helpers, ... are defined in this base class.+

Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • Hi Darin, I've got the same problem like Darf. Unfortunately, your hint with adding @inherits does not solve it (but makes sense for me). What else could be the solution? – Sascha Jan 25 '13 at 08:49
  • Sorry I have no idea. This has always worked for me when I needed to work with Razor views embedded in other projects. – Darin Dimitrov Jan 25 '13 at 08:52
1

I faced this issue "The name 'model' does not exist in the current context". What I did was added same "areas" folder structure (from my embedded mvc project) to my main MVC project (Areas/AnualReports/Views/) and copied web.config (default web.config from views folder, not the one from root) to Views folder which solved the issue. I am not sure this will work in your case.

Update: Adding web.config (from views folder) to root "areas" folder in main MVC project also works.

dotNet Decoder
  • 511
  • 5
  • 8
1

I have the same problem as you so after all searches I got working solution

Create your own WebViewPage based abstract class (generic for model and non generic)

public abstract class MyOwnViewPage<TModel> : WebViewPage<TModel> { }

public abstract class MyOwnViewPage : WebViewPage {   }

Next create VirtualFile based class or embedded view's

class AssemblyResourceFile : VirtualFile
    {
        private readonly IDictionary<string, Assembly> _nameAssemblyCache;
        private readonly string _assemblyPath;
        private readonly string _webViewPageClassName;
        public string LayoutPath { get; set; }
        public string ViewStartPath { get; set; }

        public AssemblyResourceFile(IDictionary<string, Assembly> nameAssemblyCache, string virtualPath) :
            base(virtualPath)
        {
            _nameAssemblyCache = nameAssemblyCache;
            _assemblyPath = VirtualPathUtility.ToAppRelative(virtualPath);
            LayoutPath = "~/Views/Shared/_Layout.cshtml";
            ViewStartPath = "~/Views/_ViewStart.cshtml";
            _webViewPageClassName = typeofMyOwnViewPage).ToString();
        }

        // Please change Open method for your scenario
        public override Stream Open()
        {
            string[] parts = _assemblyPath.Split(new[] { '/' }, 4);    

            string assemblyName = parts[2];
            string resourceName = parts[3].Replace('/', '.');

            Assembly assembly;

            lock (_nameAssemblyCache)
            {
                if (!_nameAssemblyCache.TryGetValue(assemblyName, out assembly))
                {
                    var assemblyPath = Path.Combine(HttpRuntime.BinDirectory, assemblyName);
                    assembly = Assembly.LoadFrom(assemblyPath);

                    _nameAssemblyCache[assemblyName] = assembly;
                }
            }

            Stream resourceStream = null;

            if (assembly != null)
            {
                 resourceStream = assembly.GetManifestResourceStream(resourceName);

                if (resourceName.EndsWith(".cshtml"))
                {
                    //the trick is here. We must correct our embedded view
                    resourceStream = CorrectView(resourceName, resourceStream);
                }
            }

            return resourceStream;
        }

        public Stream CorrectView(string virtualPath, Stream stream)
        {
            var reader = new StreamReader(stream, Encoding.UTF8);
            var view = reader.ReadToEnd();
            stream.Close();
            var ourStream = new MemoryStream();
            var writer = new StreamWriter(ourStream, Encoding.UTF8);

            var modelString = "";
            var modelPos = view.IndexOf("@model");
            if (modelPos != -1)
            {
                writer.Write(view.Substring(0, modelPos));
                var modelEndPos = view.IndexOfAny(new[] { '\r', '\n' }, modelPos);
                modelString = view.Substring(modelPos, modelEndPos - modelPos);
                view = view.Remove(0, modelEndPos);
            }

            writer.WriteLine("@using System.Web.Mvc");
            writer.WriteLine("@using System.Web.Mvc.Ajax");
            writer.WriteLine("@using System.Web.Mvc.Html");
            writer.WriteLine("@using System.Web.Routing");

            var basePrefix = "@inherits " + _webViewPageClassName;

            if (virtualPath.ToLower().Contains("_viewstart"))
            {
                writer.WriteLine("@inherits System.Web.WebPages.StartPage");
            }
            else if (modelString == "@model object")
            {
                writer.WriteLine(basePrefix + "<dynamic>");
            }
            else if (!string.IsNullOrEmpty(modelString))
            {
                writer.WriteLine(basePrefix + "<" + modelString.Substring(7) + ">");
            }
            else
            {
                writer.WriteLine(basePrefix);
            }


            writer.Write(view);
            writer.Flush();
            ourStream.Position = 0;
            return ourStream;
        }
    }

Next create VirtualPathProvider based class (modify it for your purposes)

public class AssemblyResPathProvider : VirtualPathProvider
    {
        private readonly Dictionary<string, Assembly> _nameAssemblyCache;

        private string _layoutPath;
        private string _viewstartPath;

        public AssemblyResPathProvider(string layout, string viewstart)
        {
            _layoutPath = layout;
            _viewstartPath = viewstart;

            _nameAssemblyCache = new Dictionary<string, Assembly>(StringComparer.InvariantCultureIgnoreCase);
        }

      private bool IsAppResourcePath(string virtualPath)
        {
            string checkPath = VirtualPathUtility.ToAppRelative(virtualPath);



            bool bres1 = checkPath.StartsWith("~/App_Resource/",
                                         StringComparison.InvariantCultureIgnoreCase);


            bool bres2 = checkPath.StartsWith("/App_Resource/",
                                         StringComparison.InvariantCultureIgnoreCase);

        //todo: fix this    
            if (checkPath.EndsWith("_ViewStart.cshtml"))
            {
                return false;
            }

            if (checkPath.EndsWith("_ViewStart.vbhtml"))
            {
                return false;
            }

            return ((bres1 || bres2));

        }

        public override bool FileExists(string virtualPath)
        {
            return (IsAppResourcePath(virtualPath) ||
                    base.FileExists(virtualPath));
        }


        public override VirtualFile GetFile(string virtualPath)
        {
            if (IsAppResourcePath(virtualPath))
            {
        // creating AssemblyResourceFile instance
                return new AssemblyResourceFile(_nameAssemblyCache, virtualPath,_layoutPath,virtualPath);
            }

            return base.GetFile(virtualPath);
        }

        public override CacheDependency GetCacheDependency(
            string virtualPath,
            IEnumerable virtualPathDependencies,
            DateTime utcStart)
        {
            if (IsAppResourcePath(virtualPath))
            {
                return null;
            }

            return base.GetCacheDependency(virtualPath,
                                           virtualPathDependencies, utcStart);
        }

    }

At last register your AssemblyResPathProvider in global.asax

string  _layoutPath = "~/Views/Shared/_Layout.cshtml";
string  _viewstarPath = "~/Views/_ViewStart.cshtml";
HostingEnvironment.RegisterVirtualPathProvider(new AssemblyResPathProvider(_layoutPath,_viewstarPath));

This is not ideal solution but its working for me good. Cheers!

Sanja Melnichuk
  • 3,465
  • 3
  • 25
  • 46
0

In my case, the solution was to make the virtual Path start with "~Views/" - just like any normal view.

Not working: ~/VIRTUAL/Home/Index.cshtml
Working: ~/Views/VIRTUAL/Home/Index.cshtml

I think, this has to do with the web.config lying around in ~/Views and defining a lot of stuff for the views. Maybe anybody can give more information.

Hope that helps anyway.

Sascha
  • 2,193
  • 3
  • 24
  • 38