Domanda

La domanda è semplice, come indicato nel titolo:C'è un modo di avere rasoio aiutanti al di fuori di 'App_Code'?

Esempio ( HtmlEx.cshtml file ):

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

Chiedo questo perché io davvero non hai niente altro da mettere in App_Code;Voglio strutturare il mio progetto un po ' diverso.

Grazie.

AGGIORNAMENTO: Non voglio nessun altro tipo di estensioni.Mi interessa solo la pura rasoio aiutanti come Scott sta parlando qui: http://weblogs.asp.net/scottgu/archive/2011/05/12/asp-net-mvc-3-and-the-helper-syntax-within-razor.aspx

È stato utile?

Soluzione

.

La domanda è semplice come indicato nel titolo: C'è un modo per Aiutanti di rasoio al di fuori di 'App_code'?

No, non c'è.

Altri suggerimenti

Mai Dire Mai...

Metodo Uno:(Per l'utilizzo in un progetto di applicazione web)

Basta aggiungere un evento di pre-generazione per copiare il file nella cartella App_Code.

(Ma dato che il file deve probabilmente essere incluso nel progetto, è possibile aggiungere un file vuoto con lo stesso nome App_Code dir, e quindi la generazione di eventi di aggiornamento.)

(Nota che anche se metti il file originale nella cartella App_code non si ottiene intellisense fino al primo tempo di costruzione, quindi è comunque una differenza.)

Metodo Due:(per l'uso in una libreria di classi, in cui il progetto di avvio è un applicazione web)

In una libreria di classi cartella App_Code non è nulla di speciale, in modo da essere in grado per il supporto di pagina globali, dobbiamo ignorare il codice di rasoio, in quanto è hard coded per rendere globale aiutanti solo per il codice nella cartella App_code.

Inoltre, il codice di rasoio è progettato in modo che per global aiutanti si crea uno spazio dei nomi in base all'intero percorso, qualcosa che probabilmente non si è interessati.

Dopo tutto, rimaniamo con un problema, che non c'è intellisense disponibili, quindi, per evitare tutti questi problemi, ho scritto il seguente codice, assumendo che:

  1. Il tuo .cshtml (o vbhtml) operazione di copia dei file per i progetti finali directory di output
  2. Si aggiunge un .cs (o .vb) file con lo stesso nome come il global aiutanti nome per il file e impostare build action "compile", (questo file verrà generato automaticamente all'avvio di fornire intellisense)
  3. È necessario registrare il PreApplicationStartupClass in AssemblyInfo.cs file
  4. Devi sostituire il PreApplicationStartupCode.Metodo Start (), per dare il percorso relativo all'globale aiutanti pagina nella cartella Bin, in ordine di dipendenza (es.se uno dei global file di supporto utilizza aiutanti in un altro file allora dovrebbe essere dopo di esso).
  5. Nel CustomRazorCodeHost classe devi scegliere il metodo corretto di PostProcessGeneratedCode() che è appropriato per il MVC versione installata

Ecco il codice (ma si dovrà aggiungere l' "utilizzando" le dichiarazioni):

[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));
    }
} 

Ma si noti che se c'è un errore di sintassi nel .cshtml file, si avranno problemi a compilare la prossima volta (come l'ha generato .cs file avrà errori di compilazione), tuttavia visual studio a quanto pare ha problemi di individuare il problema.

A volte anche il codice compilato dall'ultima build (compilato dal .cs file), può a volte in conflitto con il nuovo aggiornamento .cshtml file.

Quindi io consiglierei di aggiungere un evento di pre-generazione di troncare il file

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

si può andare più sofisticati e farlo solo se l' .cshtml file è stato modificato (e questo vale anche per il codice che ho scritto sopra).

Utilizzare Generatore di rasoio Estensione su una vista con gli aiutanti dentro e genererai il codice per ilVisualizza prima del tempo di compilazione.Il codice di vista generato è parte del progetto e compila nel gruppo, in modo da poter effettuare il file di visualizzazione ovunque e utilizzare gli aiutanti ovunque, anche da un test unitario.

Sicuro, puoi metterli in qualsiasi punto del codice o della struttura del progetto.Nel file in cui crei il tuo aiutante, assicurati di includere l'uso di System.Web.MVC.

Allora normalmente prolunga la classe Helper come questa:

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

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