Question

La question est simple comme indiqué dans le titre: Y a-t-il une façon d'avoir des aides de rasoir en dehors de 'app_code'?

exemple (fichier htmlex.cshtml):

@helper Script(string fileName, UrlHelper url)
{
<script src="@url.Content("~/Scripts/" + fileName)" type="text/javascript"></script> 
}

Je pose cela parce que je n'ai rien d'autre à mettre dans App_code;Je veux structurer mon projet un peu différent.

merci.

mise à jour: Je ne veux pas d'autre type d'extensions.Je ne m'intéresse que pour les aides de rasoir pure que Scott parle ici: http://weblogs.asp.net/scottgu/archive/2011/05/12/asp-net-mvc-3-et-the-Helper-syntaxe-in-rasor.aspx

Était-ce utile?

La solution

La question est simple comme indiqué dans le titre: y a-t-il un moyen de Avoir des aides à Razor en dehors de 'App_code'?

Non, il n'y a pas.

Autres conseils

ne dis jamais jamais ...

méthode 1: (à utiliser dans un projet d'application Web)

Ajoutez simplement un événement de pré-construction pour copier votre fichier dans le dossier App_code.

(mais puisque le fichier doit probablement être inclus dans le projet, vous pouvez ajouter un fichier vide avec le même nom à l'APP_CODE DIR, puis disposez d'un événement de construction pour le mettre à jour.)

(Remarque, que même si vous mettez le fichier à l'origine dans le dossier App_code, vous n'obtiendrez pas d'intellisense avant la première construction de la première fois, il est donc de toute façon pas une différence.)

méthode deux: (Pour une utilisation dans une bibliothèque de classe, dans laquelle le projet de démarrage est une application Web)

Dans une bibliothèque de classe, le dossier App_code n'est pas quelque chose de spécial, afin de pouvoir avoir la page d'aide globale, nous devons remplacer le code de rasoir, car il est codé dur de rendre les aides mondiaux uniquement pour le code dans le code. Dossier App_code.

En outre, plus le code Razor est conçu de manière à ce que les aides mondiaux créent un espace de noms basé sur le chemin complet, quelque chose que vous n'êtes probablement pas intéressé.

Après tout, nous restons avec un problème, qu'il n'y a pas d'intellisense disponible, afin d'éviter tous ces problèmes, j'ai écrit le code suivant, en supposant que:

  1. Vos fichiers .CSHTML (ou VBHTML) sont copiés dans le répertoire de sortie de projets final
  2. Vous ajoutez un fichier .cs (ou .vb) avec le même nom que le nom du fichier Global Aidateurs, puis définissez une action de construction pour "compiler" (ce fichier sera autogogené au démarrage pour fournir IntelliSense)
  3. Vous devez enregistrer la priaplicationSartUPClass dans le fichier associéinfo.cs
  4. Vous devez remplacer la méthode prioplicationstartupcode.start () pour donner le chemin relatif de votre page Global Aidateurs dans le dossier BIN, dans l'ordre de dépendance (c'est-à-dire si l'un des fichiers d'aide globale utilise des aides dans l'autre. Fichier puis il devrait être répertorié après le cas).
  5. Dans la classe CustomRazorCodehostHost, vous devez choisir la méthode correcte de PostProcessGeneratedCode () qui convient à la version MVC installée

    Voici le code (mais il devra ajouter les déclarations "Utilisation" appropriées):

    [EditorBrowsable(EditorBrowsableState.Never)]
    public static class PreApplicationStartCode
    {
        private static bool _startWasCalled;
    
        public static void Start()
        {
            // Even though ASP.NET will only call each PreAppStart once, we sometimes internally call one PreAppStart from 
            // another PreAppStart to ensure that things get initialized in the right order. ASP.NET does not guarantee the 
            // order so we have to guard against multiple calls.
            // All Start calls are made on same thread, so no lock needed here.
    
            if (_startWasCalled)
            {
                return;
            }
            _startWasCalled = true;
    
            //Add here the the global helpers based on dependency
            //also note that each global helper should have a .cs file in the project with the same name
            CustomRazorHelperBuildProvider bp = new CustomRazorHelperBuildProvider();
            bp.VirtualPath = "~/Bin/path/to/helpers/file/Helpers.cshtml";
            bp.GenerateCodeAndCompile();
    
            bp = new CustomRazorHelperBuildProvider();
            bp.VirtualPath = "~/Bin/path/to/helpers/file/DepndentHelpers.cshtml";
            bp.GenerateCodeAndCompile();
        }
    }
    
    public class CustomRazorHelperBuildProvider :RazorBuildProvider
    {
        static List<string> GeneratedAssemblyReferences = new List<string>();
        public new string VirtualPath { get; set; }
        protected override System.Web.WebPages.Razor.WebPageRazorHost CreateHost()
        {
            return new CustomCodeRazorHost(VirtualPath);
        }
        private WebPageRazorHost _host;
        internal WebPageRazorHost Host
        {
            get
            {
                if (_host == null)
                {
                    _host = CreateHost();
                }
                return _host;
            }            
        }
        private CodeCompileUnit _generatedCode = null;
        internal CodeCompileUnit GeneratedCode
        {
            get
            {
                if (_generatedCode == null)
                {
                    EnsureGeneratedCode();
                }
                return _generatedCode;
            }
        }
        private CodeDomProvider _provider = null;
        internal CodeDomProvider Provider
        {
            get
            {
                if(_provider == null)
                {
                    _provider = GetProvider();
                }
                return _provider;
            }
        }
        private void EnsureGeneratedCode()
        {
            RazorTemplateEngine engine = new RazorTemplateEngine(Host);
            GeneratorResults results = null;
            using (TextReader reader = OpenReader(VirtualPath))
            {
                results = engine.GenerateCode(reader, className: null, rootNamespace: null, sourceFileName: Host.PhysicalPath);
            }
            if (!results.Success)
            {
                RazorError error = results.ParserErrors.Last();
                throw new HttpParseException(error.Message + Environment.NewLine, null, VirtualPath, null, error.Location.LineIndex + 1);
            }
            _generatedCode = results.GeneratedCode;
        }
        private CodeDomProvider GetProvider()
        {
            CompilerType compilerType = GetDefaultCompilerTypeForLanguage(Host.CodeLanguage.LanguageName);
            CodeDomProvider provider = CreateCodeDomProviderWithPropertyOptions(compilerType.CodeDomProviderType);
            return provider;
        }
    
        /// <summary>
        /// Generates the c# (or vb.net) code, for the intellisense to work
        /// </summary>
        public void GenerateCode()
        {
            //Remember that if there is a razor error, then the next time the project will not compile at all, because the generated .cs file will also have the error!
            //The solution is to add a pre-build event to truncate the file, but not remove it!, also note that the pre-build event will not work in time if the .cs file is open in the VS editor!
            string filePath = VirtualPath.Replace("/", "\\").Replace("~\\Bin", "").Replace("\\Debug", "").Replace("\\Release", "");
            filePath = filePath.Remove(filePath.Length - 4);
            //filePath = filePath.Insert(filePath.LastIndexOf("\\"), "\\HelperAutoGeneratedCode");            
            Assembly curAssem = Assembly.GetExecutingAssembly();
            filePath = HttpRuntime.AppDomainAppPath + "\\..\\" + curAssem.GetName().Name + filePath;
    
            using (FileStream fs = new FileStream(filePath, FileMode.Truncate))
            {
                using (StreamWriter sw = new StreamWriter(fs))
                {
                    Provider.GenerateCodeFromCompileUnit(GeneratedCode, sw, null);                    
                    sw.Flush();
                    sw.Close();
                }                
                fs.Close();
            }
            //We need to replace the type of the helpers from "HelperResult" to object, otherwise the intellisense will complain that "it can't convert from HelperResult to object"
            string text = File.ReadAllText(filePath);
            text = text.Replace("public static System.Web.WebPages.HelperResult ", "public static object ");
            File.WriteAllText(filePath, text); 
        }
    
        public void GenerateCodeAndCompile()
        {
            GenerateCode();
            Compile();
        }
    
        /// <summary>
        /// Compiles the helper pages for use at runtime
        /// </summary>
        /// <returns>Compiler Result</returns>
        public CompilerResults Compile()
        {
            Assembly assem = Assembly.GetExecutingAssembly();
            AssemblyName[] references = assem.GetReferencedAssemblies();
            List<string> referenceNames = references.Select(r => Assembly.ReflectionOnlyLoad(r.FullName).Location).ToList();
            referenceNames.Add(assem.Location);
    
            //Add here references that are not included in the project, but are needed for the generated assembly, you can see this through the results.Errors
            referenceNames.Add((typeof(WebMatrix.Data.ConnectionEventArgs).Assembly.Location));
            referenceNames.Add((typeof(WebMatrix.WebData.SimpleRoleProvider).Assembly.Location));
    
            if (GeneratedAssemblyReferences != null && GeneratedAssemblyReferences.Count > 0)
            {
                referenceNames.AddRange(GeneratedAssemblyReferences);
            }
    
            CompilerResults results = Provider.CompileAssemblyFromDom(new CompilerParameters(referenceNames.ToArray()), new CodeCompileUnit[] { GeneratedCode });
            if (results.Errors.HasErrors)
            {
                IEnumerator en = results.Errors.GetEnumerator();
                en.MoveNext();
                CompilerError error = en.Current as CompilerError;
                throw new HttpParseException(error.ErrorText + Environment.NewLine, null, VirtualPath, null, error.Line);
            }
            Assembly assemblyRef = GetGeneratedType(results).Assembly;
            GeneratedAssemblyReferences.Add(assemblyRef.Location); //So that any subsequent helper page that is dependent on it will have it as a reference
            //We need to make it available for Razor, so it will work with reguler razor pages at runtime
            RazorBuildProvider.CodeGenerationStarted += new EventHandler((sender, args) => (sender as RazorBuildProvider).AssemblyBuilder.AddCodeCompileUnit(this, GeneratedCode));
            return results;
        }
    
        private static CodeDomProvider CreateCodeDomProviderWithPropertyOptions(Type codeDomProviderType)
        {
            // The following resembles the code in System.CodeDom.CompilerInfo.CreateProvider
    
            // Make a copy to avoid modifying the original.
            var originalProviderOptions = GetProviderOptions(codeDomProviderType);
            IDictionary<string, string> providerOptions = null;
            if (originalProviderOptions != null)
            {
                providerOptions = new Dictionary<string, string>(originalProviderOptions);
            }
    
            AssemblyName[] references = Assembly.GetExecutingAssembly().GetReferencedAssemblies();
            foreach (AssemblyName reference in references)
            {
                if (reference.Name == "mscorlib")
                {
                    providerOptions["CompilerVersion"] = "v" + reference.Version.Major + "." + reference.Version.Minor;
                    break;
                }
            }
    
            if (providerOptions != null && providerOptions.Count > 0)
            {
                ConstructorInfo ci = codeDomProviderType.GetConstructor(new Type[] { typeof(IDictionary<string, string>) });
                CodeDomProvider provider = null;
                if (ci != null)
                {
                    // First, obtain the language for the given codedom provider type.
                    CodeDomProvider defaultProvider = (CodeDomProvider)Activator.CreateInstance(codeDomProviderType);
                    string extension = defaultProvider.FileExtension;
                    // Then, use the new createProvider API to create an instance.
                    provider = CodeDomProvider.CreateProvider(extension, providerOptions);
                }
                return provider;
            }
    
            return null;
        }
    
        internal static IDictionary<string, string> GetProviderOptions(Type codeDomProviderType)
        {
            // Using reflection to get the property for the time being.
            // This could simply return CompilerInfo.PropertyOptions if it goes public in future.
            CodeDomProvider provider = (CodeDomProvider)Activator.CreateInstance(codeDomProviderType);
            string extension = provider.FileExtension;
            if (CodeDomProvider.IsDefinedExtension(extension))
            {
                CompilerInfo ci = CodeDomProvider.GetCompilerInfo(CodeDomProvider.GetLanguageFromExtension(extension));
                PropertyInfo pi = ci.GetType().GetProperty("ProviderOptions",
                BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Instance);
                if (pi != null)
                    return (IDictionary<string, string>)pi.GetValue(ci, null);
                return null;
            }
            return null;
        }
    }
    
     public class CustomCodeRazorHost : WebPageRazorHost
    {
        internal const string ApplicationInstancePropertyName = "ApplicationInstance";
        internal const string ContextPropertyName = "Context";
        internal const string WebDefaultNamespace = "ASP";
        private static readonly string _helperPageBaseType = typeof(HelperPage).FullName;
    
        public CustomCodeRazorHost(string virtualPath)
            : base(virtualPath)
        {
            DefaultBaseClass = _helperPageBaseType;
            DefaultNamespace = WebDefaultNamespace;
            DefaultDebugCompilation = false;
            StaticHelpers = true;
        }
    
        //Version for MVC 3
        public override void PostProcessGeneratedCode(CodeCompileUnit codeCompileUnit, CodeNamespace generatedNamespace, CodeTypeDeclaration generatedClass, CodeMemberMethod executeMethod)
        {
            // Add additional global imports
            generatedNamespace.Imports.AddRange(GetGlobalImports().Select(s => new CodeNamespaceImport(s)).ToArray());
    
            // Create ApplicationInstance property
            CodeMemberProperty prop = new CodeMemberProperty()
            {
                Name = ApplicationInstancePropertyName,
                Type = new CodeTypeReference(typeof(HttpApplication).FullName),
                HasGet = true,
                HasSet = false,
                Attributes = MemberAttributes.Family | MemberAttributes.Final
            };
            prop.GetStatements.Add(
                new CodeMethodReturnStatement(
                    new CodeCastExpression(
                        new CodeTypeReference(typeof(HttpApplication).FullName),
                        new CodePropertyReferenceExpression(
                            new CodePropertyReferenceExpression(
                                null,
                                ContextPropertyName),
                            ApplicationInstancePropertyName))));
            generatedClass.Members.Insert(0, prop);
    
            // Yank out the execute method (ignored in Razor Web Code pages)
            generatedClass.Members.Remove(executeMethod);
    
            // Make ApplicationInstance static
            CodeMemberProperty appInstanceProperty =
                generatedClass.Members
                    .OfType<CodeMemberProperty>()
                    .Where(p => ApplicationInstancePropertyName
                                    .Equals(p.Name))
                    .SingleOrDefault();
    
            if (appInstanceProperty != null)
            {
                appInstanceProperty.Attributes |= MemberAttributes.Static;
            }
        }
    
        //Version for MVC 4
        public override void PostProcessGeneratedCode(CodeGeneratorContext context)
        {
            // Add additional global imports
            context.Namespace.Imports.AddRange(GetGlobalImports().Select(s => new CodeNamespaceImport(s)).ToArray());
    
            // Create ApplicationInstance property
            CodeMemberProperty prop = new CodeMemberProperty()
            {
                Name = ApplicationInstancePropertyName,
                Type = new CodeTypeReference(typeof(HttpApplication).FullName),
                HasGet = true,
                HasSet = false,
                Attributes = MemberAttributes.Family | MemberAttributes.Final
            };
            prop.GetStatements.Add(
                new CodeMethodReturnStatement(
                    new CodeCastExpression(
                        new CodeTypeReference(typeof(HttpApplication).FullName),
                        new CodePropertyReferenceExpression(
                            new CodePropertyReferenceExpression(
                                null,
                                ContextPropertyName),
                            ApplicationInstancePropertyName))));
            context.GeneratedClass.Members.Insert(0, prop);
    
            // Yank out the execute method (ignored in Razor Web Code pages)
            context.GeneratedClass.Members.Remove(context.TargetMethod);
    
            // Make ApplicationInstance static
            CodeMemberProperty appInstanceProperty =
                context.GeneratedClass.Members
                    .OfType<CodeMemberProperty>()
                    .Where(p => ApplicationInstancePropertyName
                                    .Equals(p.Name))
                    .SingleOrDefault();
    
            if (appInstanceProperty != null)
            {
                appInstanceProperty.Attributes |= MemberAttributes.Static;
            }
        }
    
        protected override string GetClassName(string virtualPath)
        {
            return ParserHelpers.SanitizeClassName(Path.GetFileNameWithoutExtension(virtualPath));
        }
    } 
    

    Mais notez que s'il existe une erreur de syntaxe dans le fichier .cshtml, vous aurez des problèmes pour compiler la prochaine fois (car le fichier .cs généré aura des erreurs de compilation), mais Visual Studio a apparemment des problèmes pour identifier le problème.

    Parfois, le code compilé de la dernière version (compilé à partir du fichier .CS) peut parfois entrer en conflit avec le fichier .CSHTML nouvellement mis à jour.

    Par conséquent, je recommanderais d'ajouter un événement de pré-construction pour tronquer le fichier

    echo. > $(ProjectDir)\Path\to\.cs\file
    

    Vous pouvez aller plus sophistiqué et le faire uniquement si le fichier .cshtml a été modifié (et cela s'applique également au code que j'ai écrit ci-dessus).

Utilisez le Razor Generator Extension sur une vue avec Aidateurs à l'intérieur et vous générerez le code de laVoir avant la compilation.Le code de vue généré fait partie de votre projet et compile dans l'assemblage. Vous pouvez donc placer le fichier d'affichage n'importe où et utiliser les aides n'importe où, même à partir d'un test d'unité.

Bien sûr, vous pouvez les mettre n'importe où dans votre code ou votre structure de projet.Dans le fichier dans lequel vous créez votre assistant, veillez à inclure à l'aide de System.Web.mvc.

Ensuite, il suffit de prolonger normalement la classe d'assistance comme celle-ci:

namespace System.Web.Mvc
{
    static class HtmlHelperExtensions
    {
        public static IHtmlString MyNewHelper(this HtmlHelper helper, string someParam)
        {
            // do something
        }
    }
}

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top