protected async Task <TemplateFactoryResult> CompileAsync(IGeneratedRazorTemplate razorTemplate) { CompiledTemplateDescriptor templateDescriptor = await templateCompiler.CompileAsync(razorTemplate); templateDescriptor.ExpirationToken = razorTemplate.ProjectItem.ExpirationToken; string templateKey = templateDescriptor.TemplateKey; if (templateDescriptor.TemplateAttribute != null) { Type compiledType = templateDescriptor.TemplateAttribute.TemplateType; var newExpression = Expression.New(compiledType); var keyProperty = compiledType.GetTypeInfo().GetProperty(nameof(ITemplatePage.Key)); var propertyBindExpression = Expression.Bind(keyProperty, Expression.Constant(templateKey)); var objectInitializeExpression = Expression.MemberInit(newExpression, propertyBindExpression); var pageFactory = Expression .Lambda <Func <ITemplatePage> >(objectInitializeExpression) .Compile(); return(new TemplateFactoryResult(templateDescriptor, pageFactory)); } else { throw new RazorLightException($"Template {templateKey} is corrupted or invalid"); } }
/// <summary> /// Initializes a new instance of <see cref="TemplateFactoryResult"/> with the /// specified <see cref="ITemplatePage"/> factory. /// </summary> /// <param name="templatePageFactory">The <see cref="ITemplatePage"/> factory.</param> /// <param name="viewDescriptor">The <see cref="CompiledTemplateDescriptor"/>.</param> public TemplateFactoryResult( CompiledTemplateDescriptor viewDescriptor, Func <ITemplatePage> templatePageFactory) { TemplateDescriptor = viewDescriptor ?? throw new ArgumentNullException(nameof(viewDescriptor)); TemplatePageFactory = templatePageFactory; }
protected CompiledTemplateDescriptor CompileAndEmit(IGeneratedRazorTemplate razorTemplate) { if (razorTemplate == null) { throw new ArgumentNullException(nameof(razorTemplate)); } string assemblyName = Path.GetRandomFileName(); var compilation = CreateCompilation(razorTemplate.GeneratedCode, assemblyName); using (var assemblyStream = new MemoryStream()) using (var pdbStream = new MemoryStream()) { var result = compilation.Emit( assemblyStream, pdbStream, options: EmitOptions); if (!result.Success) { List <Diagnostic> errorsDiagnostics = result.Diagnostics .Where(d => d.IsWarningAsError || d.Severity == DiagnosticSeverity.Error) .ToList(); StringBuilder builder = new StringBuilder(); builder.AppendLine("Failed to compile generated Razor template:"); var errorMessages = new List <string>(); foreach (Diagnostic diagnostic in errorsDiagnostics) { FileLinePositionSpan lineSpan = diagnostic.Location.SourceTree.GetMappedLineSpan(diagnostic.Location.SourceSpan); string errorMessage = diagnostic.GetMessage(); string formattedMessage = $"- ({lineSpan.StartLinePosition.Line}:{lineSpan.StartLinePosition.Character}) {errorMessage}"; errorMessages.Add(formattedMessage); builder.AppendLine(formattedMessage); } builder.AppendLine("\nSee CompilationErrors for detailed information"); throw new TemplateCompilationException(builder.ToString(), errorMessages); } assemblyStream.Seek(0, SeekOrigin.Begin); pdbStream.Seek(0, SeekOrigin.Begin); var assembly = Assembly.Load(assemblyStream.ToArray(), pdbStream.ToArray()); var templateDescriptor = new CompiledTemplateDescriptor() { TemplateKey = razorTemplate.TemplateKey, TemplateAttribute = assembly.GetCustomAttribute <RazorLightTemplateAttribute>(), }; return(templateDescriptor); } }
public Func <ITemplatePage> CreateFactory(CompiledTemplateDescriptor templateDescriptor) { string templateKey = templateDescriptor.TemplateKey; if (templateDescriptor.TemplateAttribute != null) { Type compiledType = templateDescriptor.TemplateAttribute.TemplateType; var newExpression = Expression.New(compiledType); var keyProperty = compiledType.GetTypeInfo().GetProperty(nameof(ITemplatePage.Key)); var propertyBindExpression = Expression.Bind(keyProperty, Expression.Constant(templateKey)); var objectInitializeExpression = Expression.MemberInit(newExpression, propertyBindExpression); var pageFactory = Expression .Lambda <Func <ITemplatePage> >(objectInitializeExpression) .Compile(); return(pageFactory); } else { throw new RazorLightException($"Template {templateKey} is corrupted or invalid"); } }
private Task <CompiledTemplateDescriptor> OnCacheMissAsync(string templateKey) { ViewCompilerWorkItem item; TaskCompletionSource <CompiledTemplateDescriptor> taskSource; MemoryCacheEntryOptions cacheEntryOptions; // Safe races cannot be allowed when compiling Razor pages. To ensure only one compilation request succeeds // per file, we'll lock the creation of a cache entry. Creating the cache entry should be very quick. The // actual work for compiling files happens outside the critical section. lock (_cacheLock) { string normalizedKey = GetNormalizedKey(templateKey); // Double-checked locking to handle a possible race. if (_cache.TryGetValue(normalizedKey, out Task <CompiledTemplateDescriptor> result)) { return(result); } if (_precompiledViews.TryGetValue(normalizedKey, out var precompiledView)) { item = null; //item = CreatePrecompiledWorkItem(normalizedKey, precompiledView); } else { item = CreateRuntimeCompilationWorkItem(templateKey).GetAwaiter().GetResult(); } // At this point, we've decided what to do - but we should create the cache entry and // release the lock first. cacheEntryOptions = new MemoryCacheEntryOptions(); if (item.ExpirationToken != null) { cacheEntryOptions.ExpirationTokens.Add(item.ExpirationToken); } taskSource = new TaskCompletionSource <CompiledTemplateDescriptor>(); if (item.SupportsCompilation) { // We'll compile in just a sec, be patient. } else { // If we can't compile, we should have already created the descriptor Debug.Assert(item.Descriptor != null); taskSource.SetResult(item.Descriptor); } _cache.Set(item.NormalizedKey, taskSource.Task, cacheEntryOptions); } // Now the lock has been released so we can do more expensive processing. if (item.SupportsCompilation) { Debug.Assert(taskSource != null); //if (item.Descriptor?.Item != null && // ChecksumValidator.IsItemValid(_projectEngine, item.Descriptor.Item)) //{ // // If the item has checksums to validate, we should also have a precompiled view. // Debug.Assert(item.Descriptor != null); // taskSource.SetResult(item.Descriptor); // return taskSource.Task; //} try { CompiledTemplateDescriptor descriptor = CompileAndEmit(item.ProjectItem); descriptor.ExpirationToken = cacheEntryOptions.ExpirationTokens.FirstOrDefault(); taskSource.SetResult(descriptor); } catch (Exception ex) { taskSource.SetException(ex); } } return(taskSource.Task); }