Alvin Stefanus Alvin Stefanus - 1 year ago 102
C# Question

MVC 4: Create new Razor View from string not from file path

The way to create Razor View from the code currently is to find a file using path parameters.

RazorView viewResult = new RazorView(ControllerContext, viewPath, layoutPath, boolRunViewStartPages, IEnumerableViewStartFileExtensions);


I do not want to get the view from file, i want to get the view from my database string. How can i achieve this:

RazorView viewResult = new RazorView(ControllerContext, stringViewHtml);


I do not want to use Virtual Path Provider because it will change all of my routing system, but if is there a way that i can only activate VPP when im creating the razor view, it will be appreciated!

Answer Source

Its been a while since I got the solution, I will share it here:

    public static string RenderRazorViewToString(string viewToRender, object model, string controllerName, string loadAction, HttpContextBase httpContext, List<FormViewBags> viewData, string formModelType)
    {
        string view = "";

        //Creating the view data
        ViewDataDictionary ViewData = new ViewDataDictionary();
        //The type of model used for this view
        Type modelType = Helper.GetModelType(formModelType); 
        //Compiling the object model from parameter with the correct type
        ViewData.Model = Cast(model, modelType);

        if ((ViewData.Model != null) && (viewData != null))
        {
            //Adding view bags for the view
            foreach (FormViewBags item in viewData)
            {
                ViewData.Add(item.Key, item.Value);
            }
        }

        //The controller used to all this view
        Type controllerType = Helper.GetModelType(controllerName);
        //Creating the controller
        object controller = Activator.CreateInstance(controllerType);
        //Setting the routing
        RouteData rd = new System.Web.Routing.RouteData();
        rd.Values.Add("controller", controllerName.Split('.').Last().Replace("Controller",""));
        //rd.Values.Add("action", loadAction);
        //Setting the controller with corresponding context
        ((ControllerBase)(controller)).ControllerContext = new ControllerContext(httpContext, rd, (ControllerBase)controller);
        ControllerContext cc = ((ControllerBase)(controller)).ControllerContext;
        using (var sw = new StringWriter())
        {
            //The custom view engine I used to get the view
            var engine = ViewEngines.Engines.Where(s => s.ToString().Equals("MyApp.App_Start.CustomViewEngine")).SingleOrDefault();
            if (engine == null) throw new Exception("no viewengine");

            //Finding the string/text of the view. My website use VPP to get view from database.
            var viewResult = engine.FindPartialView(cc, viewToRender, false);
            var viewContext = new ViewContext(cc, viewResult.View, ViewData, cc.Controller.TempData, sw);
            //This code here renders all the information above into the real compiled string view
            viewResult.View.Render(viewContext, sw);
            viewResult.ViewEngine.ReleaseView(cc, viewResult.View);
            view = sw.GetStringBuilder().ToString();
        }

        //Returns the string of the compiled view
        return view;
    }

This is my custom view engine:

public class CustomViewEngine : RazorViewEngine
{
    private List<string> _plugins = new List<string>();

    public CustomViewEngine(List<string> pluginFolders)
    {
        _plugins = pluginFolders;

        ViewLocationFormats = GetViewLocations();
        MasterLocationFormats = GetMasterLocations();
        PartialViewLocationFormats = GetViewLocations();
    }

    public string[] GetViewLocations()
    {
        var views = new List<string>();
        views.Add("~/Views/{1}/{0}.cshtml");

        foreach (string p in _plugins)
        {
            views.Add("~/Modules/" + p + "/Views/{1}/{0}.cshtml");
        }

        return views.ToArray();
    }

    public string[] GetMasterLocations()
    {
        var masterPages = new List<string>();

        masterPages.Add("~/Views/Shared/{0}.cshtml");

        foreach (string p in _plugins)
        {
            masterPages.Add("~/Modules/" + p + "/Views/Shared/{0}.cshtml");
        }

        return masterPages.ToArray();
    }
}

You need to use VPP (Virtual Path Provider) to automatically:

/* VIRTUAL PATH HELPER */
public class ViewPathProvider : VirtualPathProvider
{
    //private static FormDBContext dbForm = new FormDBContext();

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

    public override VirtualFile GetFile(string virtualPath)
    {
        if (IsExistByVirtualPath(virtualPath))
        {
            return new ViewFile(virtualPath);
        }

        return base.GetFile(virtualPath);
    }

    public override CacheDependency GetCacheDependency(string virtualPath, System.Collections.IEnumerable virtualPathDependencies, DateTime utcStart)
    {
        if (IsExistByVirtualPath(virtualPath)) {
            //return null; //return null to force no cache
            return ViewCacheDependencyManager.Instance.Get(virtualPath); //uncomment this to enable caching
        }

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

    public override String GetFileHash(String virtualPath, IEnumerable virtualPathDependencies) //uncomment this getfilehash to turn on cache
    {
        if (IsExistByVirtualPath(virtualPath))
        {
            return Guid.NewGuid().ToString();
        }

        return Previous.GetFileHash(virtualPath, virtualPathDependencies);
    }

    public bool IsExistByVirtualPath(string virtualPath)
    {
        bool isExist = false;
        try
        {
            string checker = virtualPath.First().Equals('~') ? virtualPath : "~" + virtualPath;
            if (checker.IndexOf("/Views/", StringComparison.OrdinalIgnoreCase) > 0)
            {
                checker = "~" + Helper.RemoveSubfolderName(checker);
            }
            using (FormDBContext formsDB = new FormDBContext())
            {
                List<Form> f = formsDB.Forms.Where(m => m.VirtualPath.Equals(checker, StringComparison.CurrentCultureIgnoreCase)).ToList();
                if ((f != null) && (f.Count > 0))
                {
                    isExist = true;
                    base.GetFile(virtualPath);
                }
            }
        }
        catch (Exception ex)
        {
            Helper.Log("Is Exist By Virtual Path: " + ex);
        }
        return isExist;
    }

}

public class VirtualForm
{
    //private FormDBContext dbForm = new FormDBContext();

    public string GetByVirtualPath(string virtualPath)
    {
        using (FormDBContext dbForm = new FormDBContext())
        {
            string content = string.Empty;
            string checker = virtualPath.First().Equals("~") ? virtualPath : "~" + virtualPath;
            if (checker.IndexOf("/Views/", StringComparison.OrdinalIgnoreCase) > 0)
            {
                checker = "~" + Helper.RemoveSubfolderName(checker);
            }
            Form f = dbForm.Forms.Where(m => m.VirtualPath.Equals(checker, StringComparison.CurrentCultureIgnoreCase)).First();
            content = f.Html;

            return content;
        }
    }
}

public class ViewFile : VirtualFile
{
    private string path;

    public ViewFile(string virtualPath)
        : base(virtualPath)
    {
        path = virtualPath;
    }

    public override Stream Open()
    {
        if (string.IsNullOrEmpty(path))
            return new MemoryStream();

        VirtualForm vf = new VirtualForm();
        string content = vf.GetByVirtualPath(path);
        if (string.IsNullOrEmpty(content))
            return new MemoryStream();

        return new MemoryStream(ASCIIEncoding.UTF8.GetBytes(content));
    }
}

public class ViewCacheDependencyManager
{
    private static Dictionary<string, ViewCacheDependency> dependencies = new Dictionary<string, ViewCacheDependency>();
    private static volatile ViewCacheDependencyManager instance;
    private static object syncRoot = new Object();

    private ViewCacheDependencyManager()
    {
    }

    public static ViewCacheDependencyManager Instance
    {
        get
        {
            if (instance == null)
            {
                lock (syncRoot)
                {
                    if (instance == null)
                    {
                        instance = new ViewCacheDependencyManager();
                    }
                }
            }

            return instance;
        }
    }

    public CacheDependency Get(string virtualPath)
    {
        if (!dependencies.ContainsKey(virtualPath))
            dependencies.Add(virtualPath, new ViewCacheDependency(virtualPath));
        /*else //This else will always reset cache when it is virtual path
        {
            dependencies.Remove(virtualPath);
            dependencies.Add(virtualPath, new ViewCacheDependency(virtualPath));
        }*/
        return dependencies[virtualPath];

    }

    public void Invalidate(string virtualPath)
    {
        string vp = virtualPath.First().Equals('~') ? virtualPath.Remove(0, 1) : virtualPath;
        if (dependencies.ContainsKey(vp))
        {
            var dependency = dependencies[vp];
            dependency.Invalidate();
            dependency.Dispose();
            dependencies.Remove(vp);
        }
    }

    public void InvalidateAll()
    {
        dependencies.Clear();
    }
}

public class ViewCacheDependency : CacheDependency
{
    public ViewCacheDependency(string virtualPath)
    {
        base.SetUtcLastModified(DateTime.UtcNow);
    }

    public void Invalidate()
    {
        base.NotifyDependencyChanged(this, EventArgs.Empty);
    }
}
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download