app_codeの外側にグローバルなかみそり@helperを持つことができますか?
-
11-12-2019 - |
質問
問題はタイトルに記載されているとおりに簡単です。
例(htmlex.cshtmlファイル):
@helper Script(string fileName, UrlHelper url)
{
<script src="@url.Content("~/Scripts/" + fileName)" type="text/javascript"></script>
}
.
私は本当にapp_codeに入れるために他に何か持っていないのでこれに尋ねます。私のプロジェクトを少し異なる構造にしたいです。
ありがとう。
アップデート:他のタイプの拡張機能を望みません。私はスコットがここに話しているように純粋なかみそりのヘルパーだけに興味があります。
解決
質問はタイトルに記載されているようにシンプルです。 Razorヘルパーを 'app_code'の外側に持っている?
いいえ、存在しません。
他のヒント
決して言わないで...
メソッド1 :( Webアプリケーションプロジェクトでの使用のため)
ファイルをApp_Codeフォルダにコピーするためのビルド前イベントを追加するだけです。
(ただし、ファイルはおそらくプロジェクトに含める必要があるため、同じ名前の空のファイルをapp_code dirに追加してから更新するためのビルドイベントを追加することができます。)
(たとえば、ファイルを最初にapp_codeフォルダに置かない場合でも、初めてビルにするまでIntelliSenseは取得されませんので、とにかく違いはありません。)
メソッド2 :(スタートアッププロジェクトがWebアプリケーションであるクラスライブラリで使用するため)
クラスライブラリの中では、特別なものではありませんので、ヘルパーページグローバルを持つことができるようにするためには、カメラのコードのみのコード専用のブランドコードをハードコーディングしています。 app_codeフォルダ
さらに、ノレアーコードは、グローバルヘルパーのためにそれがフルパスに基づいて名前空間を作成するように設計されています、あなたがおそらく興味を持っていない何か。
結局のところ、私たちは利用可能なIntellisenseがないという問題を残しているので、これらすべての問題を回避するために、次のコードを書いています。
- あなたの.cshtml(またはvbhtml)ファイルが最後のプロジェクトの出力ディレクトリにコピーされています
- グローバルヘルパーファイル名と同じ名前の.cs(または.vb)ファイルを追加し、それが "コンパイル"にビルドアクションを設定する(このファイルはIntellisenseを提供するために起動時に自動生成されます)
- PreapPlicationStartupClassをAssemballInfo.csファイルに登録する必要があります。
- PrapplicationStartupcode.start()メソッドに置き換える必要があります。依存関係の順に、BINフォルダ内のグローバルヘルパーページへの相対パス(つまり、グローバルヘルパーファイルの1つがヘルパーを使用する場合その後、それはその後にリストされるべきです。
- CustomRazorCodeHostクラスでは、MVCバージョンのインストールに適したPostProcessGeneratedCode()の正しいメソッドを選択する必要があります。
これはコードです(しかし、適切な "ステートメントを使って適切な" "を追加する必要があります):
.[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)); } }
しかし、.cshtmlファイルに構文エラーがある場合は、次回のコンパイルに問題があります(生成された.csファイルがコンパイルエラーがあるため)、Visual Studioは問題を特定するための問題を示します。
最後のビルドからのコンパイル済みコード(.csファイルからコンパイルされた)は、新しく更新された.cshtmlファイルと競合することがあります。
そのため、ファイルを切り捨てるためにプリビルドイベントを追加することをお勧めします。
.echo. > $(ProjectDir)\Path\to\.cs\file
.cshtmlファイルが変更された場合(そしてこれは上記で書かれたコードにも適用されます)の場合にのみ実行できます。
Razor Generator ヘルパー内部のビューでの拡張子の拡張子を使用すると、コンパイル時間の前に表示します。生成されたビューコードはあなたのプロジェクトの一部であり、アセンブリにコンパイルされているので、ユニットテストからでも、ビューファイルをどこでも配置し、任意の場所を使用することができます。
確実に、あなたはあなたのコードまたはプロジェクト構造のどこにでも置くことができます。ヘルパーを作成したファイルでは、必ずSystem.Web.MVCを使用していてください。
は通常このようなヘルパークラスを拡張するだけです。
namespace System.Web.Mvc
{
static class HtmlHelperExtensions
{
public static IHtmlString MyNewHelper(this HtmlHelper helper, string someParam)
{
// do something
}
}
}
.