Posso specificare una posizione personalizzata per & # 8220; cercare visualizzazioni & # 8221; in ASP.NET MVC?

StackOverflow https://stackoverflow.com/questions/632964

  •  08-07-2019
  •  | 
  •  

Domanda

Ho il seguente layout per il mio progetto mvc:

  • / Controller
    • / Demo
    • / Demo / DemoArea1Controller
    • / Demo / DemoArea2Controller
    • ecc ...
  • / Vista
    • / Demo
    • /Demo/DemoArea1/Index.aspx
    • /Demo/DemoArea2/Index.aspx

Tuttavia, quando ho questo per DemoArea1Controller :

public class DemoArea1Controller : Controller
{
    public ActionResult Index()
    {
        return View();
    }
}

Ottengo il " La vista 'indice' o il suo master non sono stati trovati " errore, con le solite posizioni di ricerca.

Come posso specificare i controller nella sezione " Demo " ricerca nello spazio dei nomi nella sezione " Demo " visualizzare la sottocartella?

È stato utile?

Soluzione

Puoi facilmente estendere WebFormViewEngine per specificare tutte le posizioni in cui vuoi cercare:

public class CustomViewEngine : WebFormViewEngine
{
    public CustomViewEngine()
    {
        var viewLocations =  new[] {  
            "~/Views/{1}/{0}.aspx",  
            "~/Views/{1}/{0}.ascx",  
            "~/Views/Shared/{0}.aspx",  
            "~/Views/Shared/{0}.ascx",  
            "~/AnotherPath/Views/{0}.ascx"
            // etc
        };

        this.PartialViewLocationFormats = viewLocations;
        this.ViewLocationFormats = viewLocations;
    }
}

Assicurati di ricordare di registrare il motore di visualizzazione modificando il metodo Application_Start in Global.asax.cs

protected void Application_Start()
{
    ViewEngines.Engines.Clear();
    ViewEngines.Engines.Add(new CustomViewEngine());
}

Altri suggerimenti

Ora in MVC 6 puoi implementare l'interfaccia IViewLocationExpander senza fare confusione con i motori di visualizzazione:

public class MyViewLocationExpander : IViewLocationExpander
{
    public void PopulateValues(ViewLocationExpanderContext context) {}

    public IEnumerable<string> ExpandViewLocations(ViewLocationExpanderContext context, IEnumerable<string> viewLocations)
    {
        return new[]
        {
            "/AnotherPath/Views/{1}/{0}.cshtml",
            "/AnotherPath/Views/Shared/{0}.cshtml"
        }; // add `.Union(viewLocations)` to add default locations
    }
}

dove {0} è il nome della vista di destinazione, {1} - nome del controller e {2} - nome dell'area.

Puoi restituire il tuo elenco di posizioni, unirlo con viewLocations ( .Union (viewLocations) predefinito o semplicemente modificarli ( viewLocations.Select ( path = > " / AnotherPath " + percorso) ).

Per registrare l'espansore della posizione della vista personalizzata in MVC, aggiungere le righe successive al metodo ConfigureServices nel file Startup.cs :

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<RazorViewEngineOptions>(options =>
    {
        options.ViewLocationExpanders.Add(new MyViewLocationExpander());
    });
}

In realtà esiste un metodo molto più semplice rispetto all'hardcoding dei percorsi nel tuo costruttore. Di seguito è riportato un esempio di estensione del motore Razor per aggiungere nuovi percorsi. Una cosa di cui non sono del tutto sicuro è se i percorsi che aggiungi qui verranno memorizzati nella cache:

public class ExtendedRazorViewEngine : RazorViewEngine
{
    public void AddViewLocationFormat(string paths)
    {
        List<string> existingPaths = new List<string>(ViewLocationFormats);
        existingPaths.Add(paths);

        ViewLocationFormats = existingPaths.ToArray();
    }

    public void AddPartialViewLocationFormat(string paths)
    {
        List<string> existingPaths = new List<string>(PartialViewLocationFormats);
        existingPaths.Add(paths);

        PartialViewLocationFormats = existingPaths.ToArray();
    }
}

E il tuo Global.asax.cs

protected void Application_Start()
{
    ViewEngines.Engines.Clear();

    ExtendedRazorViewEngine engine = new ExtendedRazorViewEngine();
    engine.AddViewLocationFormat("~/MyThemes/{1}/{0}.cshtml");
    engine.AddViewLocationFormat("~/MyThemes/{1}/{0}.vbhtml");

    // Add a shared location too, as the lines above are controller specific
    engine.AddPartialViewLocationFormat("~/MyThemes/{0}.cshtml");
    engine.AddPartialViewLocationFormat("~/MyThemes/{0}.vbhtml");

    ViewEngines.Engines.Add(engine);

    AreaRegistration.RegisterAllAreas();
    RegisterRoutes(RouteTable.Routes);
}

Una cosa da notare: la tua posizione personalizzata avrà bisogno del file ViewStart.cshtml nella sua radice.

Se vuoi solo aggiungere nuovi percorsi, puoi aggiungere ai motori di visualizzazione predefiniti e risparmiare alcune righe di codice:

ViewEngines.Engines.Clear();
var razorEngine = new RazorViewEngine();
razorEngine.MasterLocationFormats = razorEngine.MasterLocationFormats
      .Concat(new[] { 
          "~/custom/path/{0}.cshtml" 
      }).ToArray();

razorEngine.PartialViewLocationFormats = razorEngine.PartialViewLocationFormats
      .Concat(new[] { 
          "~/custom/path/{1}/{0}.cshtml",   // {1} = controller name
          "~/custom/path/Shared/{0}.cshtml" 
      }).ToArray();

ViewEngines.Engines.Add(razorEngine);

Lo stesso vale per WebFormEngine

Invece di sottoclassare RazorViewEngine o sostituirlo completamente, puoi semplicemente alterare la proprietà PartialViewLocationFormats di RazorViewEngine esistente. Questo codice va in Application_Start:

System.Web.Mvc.RazorViewEngine rve = (RazorViewEngine)ViewEngines.Engines
  .Where(e=>e.GetType()==typeof(RazorViewEngine))
  .FirstOrDefault();

string[] additionalPartialViewLocations = new[] { 
  "~/Views/[YourCustomPathHere]"
};

if(rve!=null)
{
  rve.PartialViewLocationFormats = rve.PartialViewLocationFormats
    .Union( additionalPartialViewLocations )
    .ToArray();
}

L'ultima volta che ho controllato, questo richiede che tu costruisca il tuo ViewEngine. Non so se abbiano reso più semplice RC1 però.

L'approccio di base che ho usato prima del primo RC era, nel mio ViewEngine, di dividere lo spazio dei nomi del controller e cercare cartelle che corrispondessero alle parti.

Modifica

Sono tornato indietro e ho trovato il codice. Ecco l'idea generale.

public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName)
{
    string ns = controllerContext.Controller.GetType().Namespace;
    string controller = controllerContext.Controller.GetType().Name.Replace("Controller", "");

    //try to find the view
    string rel = "~/Views/" +
        (
            ns == baseControllerNamespace ? "" :
            ns.Substring(baseControllerNamespace.Length + 1).Replace(".", "/") + "/"
        )
        + controller;
    string[] pathsToSearch = new string[]{
        rel+"/"+viewName+".aspx",
        rel+"/"+viewName+".ascx"
    };

    string viewPath = null;
    foreach (var path in pathsToSearch)
    {
        if (this.VirtualPathProvider.FileExists(path))
        {
            viewPath = path;
            break;
        }
    }

    if (viewPath != null)
    {
        string masterPath = null;

        //try find the master
        if (!string.IsNullOrEmpty(masterName))
        {

            string[] masterPathsToSearch = new string[]{
                rel+"/"+masterName+".master",
                "~/Views/"+ controller +"/"+ masterName+".master",
                "~/Views/Shared/"+ masterName+".master"
            };


            foreach (var path in masterPathsToSearch)
            {
                if (this.VirtualPathProvider.FileExists(path))
                {
                    masterPath = path;
                    break;
                }
            }
        }

        if (string.IsNullOrEmpty(masterName) || masterPath != null)
        {
            return new ViewEngineResult(
                this.CreateView(controllerContext, viewPath, masterPath), this);
        }
    }

    //try default implementation
    var result = base.FindView(controllerContext, viewName, masterName);
    if (result.View == null)
    {
        //add the location searched
        return new ViewEngineResult(pathsToSearch);
    }
    return result;
}

Prova qualcosa del genere:

private static void RegisterViewEngines(ICollection<IViewEngine> engines)
{
    engines.Add(new WebFormViewEngine
    {
        MasterLocationFormats = new[] {"~/App/Views/Admin/{0}.master"},
        PartialViewLocationFormats = new[] {"~/App/Views/Admin//{1}/{0}.ascx"},
        ViewLocationFormats = new[] {"~/App/Views/Admin//{1}/{0}.aspx"}
    });
}

protected void Application_Start()
{
    RegisterViewEngines(ViewEngines.Engines);
}

Nota: per ASP.NET MVC 2 hanno percorsi di posizione aggiuntivi che è necessario impostare per le visualizzazioni in 'Aree'.

 AreaViewLocationFormats
 AreaPartialViewLocationFormats
 AreaMasterLocationFormats

La creazione di un motore di visualizzazione per un'area è descritto su Phil's blog .

Nota: questo è per l'anteprima della versione 1, quindi è soggetto a modifiche.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top