Source/Tx.LinqPad/TxDataContextDriver.cs (313 lines of code) (raw):

// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. using System; using System.CodeDom.Compiler; using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; using System.Linq; using System.Reactive; using System.Reactive.Concurrency; using System.Reactive.Linq; using System.Reactive.Subjects; using System.Reflection; using System.Text; using System.Threading; using System.Windows; using LINQPad.Extensibility.DataContext; using Microsoft.CSharp; using Expression = System.Linq.Expressions.Expression; namespace Tx.LinqPad { public sealed class TxDataContextDriver : DynamicDataContextDriver { private const string DataContextTemplate = @"namespace System.Reactive.Tx { using System; using System.Linq; using System.Linq.Expressions; using System.Collections.Generic; using System.Reactive; using System.Reactive.Linq; [usings] public class StreamScopeWrapper { Playback _playback; public StreamScopeWrapper(Playback playback) { _playback = playback; } public Playback playback { get { return _playback; } } [properties] } }"; private static readonly LocalDataStoreSlot _threadStorageSlot; private readonly ParserRegistry _parserRegistry; private readonly TypeCache _typeCache; static TxDataContextDriver() { AppDomain.CurrentDomain.AssemblyResolve += AssemblyResolve; _threadStorageSlot = Thread.AllocateDataSlot(); CopySampleTraces(); } public TxDataContextDriver() { _typeCache = new TypeCache(); _parserRegistry = new ParserRegistry(); } public override string Author { get { return "Microsoft Open Technologies, Inc."; } } public override bool DisallowQueryDisassembly { get { return true; } } public override string Name { get { return "Tx (LINQ to Logs and Traces)"; } } public override IEnumerable<string> GetAssembliesToAdd(IConnectionInfo cxn) { var assemblies = new List<Assembly> { typeof (ObservableCollection<>).Assembly, // System typeof (Expression).Assembly, // System.Core typeof (ISubject<>).Assembly, // System.Reactive.Interfaces typeof (Observer).Assembly, // System.Reactive.Core typeof (Subject<>).Assembly, // System.Reactive.Linq typeof (ThreadPoolScheduler).Assembly, // System.Reactive.PlatformServices typeof (ControlObservable).Assembly, // System.Reactive.Windows.Forms typeof (Playback).Assembly, // Tx.Core }; var properties = new TxProperties(cxn); assemblies.AddRange(_parserRegistry.GetAssemblies()); var assemblyNames = new List<string>(from a in assemblies select a.Location); _typeCache.Init(properties.ContextName); assemblyNames.AddRange(_typeCache.GetAssemblies(properties.ContextName, ReplaceSampleTracesDir(properties.Files), ReplaceSampleTracesDir(properties.MetadataFiles))); return assemblyNames; } public override IEnumerable<string> GetNamespacesToAdd(IConnectionInfo cxInfo) { var namespaces = new List<string> { "System", "System.Linq", "System.Linq.Expressions", "System.Reactive", "System.Reactive.Linq", }; return namespaces.Concat(_parserRegistry.GetNamespaces()); } public override string GetConnectionDescription(IConnectionInfo cxInfo) { var properties = new TxProperties(cxInfo); return properties.ContextName; } public override bool ShowConnectionDialog(IConnectionInfo cxInfo, bool isNewConnection) { try { var properties = new TxProperties(cxInfo); return new ConnectionDialog(properties, _parserRegistry.Filter).ShowDialog() ?? false; } catch (Exception error) { TxEventSource.Log.TraceError(error.ToString()); MessageBox.Show(error.ToString(), "ShowConnectionDialog"); return false; } } public override ParameterDescriptor[] GetContextConstructorParameters(IConnectionInfo cxInfo) { var desc = new ParameterDescriptor("playback", typeof (Playback).FullName); return new[] {desc}; } public override object[] GetContextConstructorArguments(IConnectionInfo cxInfo) { var playback = new Playback(); var properties = new TxProperties(cxInfo); if (properties.IsRealTime) { _parserRegistry.AddSession(playback, properties.SessionName); } else { _parserRegistry.AddFiles(playback, ReplaceSampleTracesDir(properties.Files)); } Thread.SetData(_threadStorageSlot, playback); return new[] {playback}; } public override List<ExplorerItem> GetSchemaAndBuildAssembly(IConnectionInfo cxInfo, AssemblyName assemblyToBuild, ref string nameSpace, ref string typeName) { nameSpace = "System.Reactive.Tx"; typeName = "StreamScopeWrapper"; var allGeneratedSources = new List<string>(); var sbContextUsings = new StringBuilder(); var sbContextProperties = new StringBuilder(); var properties = new TxProperties(cxInfo); _typeCache.Init(properties.ContextName); if (properties.IsUsingDirectoryLookup) { _typeCache.BuildCache(properties.ContextName, properties.Files, properties.MetadataDirectory); } else { _typeCache.BuildCache( properties.ContextName, ReplaceSampleTracesDir(properties.Files), ReplaceSampleTracesDir(properties.MetadataFiles)); } string dataContext = DataContextTemplate .Replace("[usings]", sbContextUsings.ToString()) .Replace("[properties]", sbContextProperties.ToString()); allGeneratedSources.Add(dataContext); CompilerResults results; string outputName = assemblyToBuild.CodeBase; using ( var codeProvider = new CSharpCodeProvider(new Dictionary<string, string> {{"CompilerVersion", "v4.0"}})) { string[] assemblies = GetAssembliesToAdd(cxInfo).ToArray(); var compilerOptions = new CompilerParameters(assemblies, outputName, true); results = codeProvider.CompileAssemblyFromSource(compilerOptions, allGeneratedSources.ToArray()); if (results.Errors.Count > 0) { var sbErr = new StringBuilder(); foreach (object o in results.Errors) { sbErr.AppendLine(o.ToString()); } // Is there any better troubleshooting mechanism? MessageBox.Show(sbErr.ToString(), "Error compiling generated code"); } } Dictionary<Type, long> stat = _parserRegistry.GetTypeStatistics( _typeCache.GetAvailableTypes( properties.ContextName, ReplaceSampleTracesDir(properties.Files), ReplaceSampleTracesDir(properties.MetadataFiles)), ReplaceSampleTracesDir(properties.Files)); return CreateTree(stat); } private List<ExplorerItem> CreateTree(Dictionary<Type, long> stat) { var result = new List<ExplorerItem>(); KeyValuePair<Type, long>[] x = (from pair in stat orderby pair.Key.Namespace, pair.Key.Name select pair).ToArray(); string currentNamespace = null; ExplorerItem scope = null; foreach (var pair in x) { if (pair.Key.Namespace != currentNamespace) { scope = CreateNamespace(pair.Key.Namespace, result); currentNamespace = pair.Key.Namespace; } ; var eventType = new ExplorerItem(pair.Key.Name, ExplorerItemKind.QueryableObject, ExplorerIcon.Table); eventType.ToolTipText = "Occurences: " + pair.Value + "\n"; foreach (object a in pair.Key.GetCustomAttributes(false).OrderBy(a => a.GetType().Name)) { eventType.ToolTipText += '\n' + a.ToString() + '\n'; } eventType.DragText = "playback.GetObservable<" + pair.Key.FullName + ">()"; eventType.Children = new List<ExplorerItem>(); scope.Children.Add(eventType); foreach (PropertyInfo p in pair.Key.GetProperties()) { var field = new ExplorerItem(p.Name, ExplorerItemKind.Property, ExplorerIcon.Column); eventType.Children.Add(field); field.ToolTipText = p.PropertyType.Name; } } return result; } private ExplorerItem CreateNamespace(string name, List<ExplorerItem> root) { ExplorerItem item = null; List<ExplorerItem> currentScope = root; string[] tokens = name.Split('.'); for (int i = 1; i < tokens.Length; i++) { string token = tokens[i]; item = currentScope.FirstOrDefault(ei => ei.Text == token); if (item == null) { item = new ExplorerItem(token, ExplorerItemKind.Schema, ExplorerIcon.Schema); item.Children = new List<ExplorerItem>(); currentScope.Add(item); } currentScope = item.Children; } return item; } public override void OnQueryFinishing(IConnectionInfo cxInfo, object context, QueryExecutionManager executionManager) { var playback = (Playback)context .GetType() .GetProperty("playback") .GetValue(context, new object[] {}); if (playback != null) playback.Run(); } private static Assembly AssemblyResolve(object sender, ResolveEventArgs args) { try { string assemblyname = args.Name.Substring(0, args.Name.IndexOf(',')) + ".dll"; string driverDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); IEnumerable<string> assemblies = Directory.EnumerateFiles(driverDir, assemblyname); foreach (string path in assemblies) { return DataContextDriver.LoadAssemblySafely(path); } string root = Path.Combine(Path.GetTempPath(), @"LINQPad\"); assemblies = Directory.EnumerateFiles(root, assemblyname, SearchOption.AllDirectories); foreach (string path in assemblies) { return DataContextDriver.LoadAssemblySafely(path); } } catch (Exception error) { TxEventSource.Log.TraceError(error.ToString()); } return null; } private string[] ReplaceSampleTracesDir(string[] files) { string prefix = "($SampleTraces)"; string samplrDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); var result = new List<string>(); foreach (string f in files) { if (f.StartsWith(prefix)) { result.Add(Path.Combine(samplrDir, f.Substring(prefix.Length))); } else { result.Add(f); } } return result.ToArray(); } private static void CopySampleTraces() { string myDocuments = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); string txDir = Path.Combine(myDocuments, @"LINQPad Queries\Tx"); if (!Directory.Exists(txDir)) Directory.CreateDirectory(txDir); string sourceDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); List<string> files = new List<string>(Directory.GetFiles(sourceDir,"*.etl")); files.AddRange(Directory.GetFiles(sourceDir,"*.man")); files.AddRange(Directory.GetFiles(sourceDir,"*.blg")); files.AddRange(Directory.GetFiles(sourceDir,"*.xel")); foreach (string file in files) { string target = Path.Combine(txDir, Path.GetFileName(file)); File.Copy(file, target, true); } } } }