diff --git a/Madd0.AzureStorageDriver/AzureDriver.cs b/Madd0.AzureStorageDriver/AzureDriver.cs
index 011d67d..da0a611 100644
--- a/Madd0.AzureStorageDriver/AzureDriver.cs
+++ b/Madd0.AzureStorageDriver/AzureDriver.cs
@@ -8,8 +8,9 @@
namespace Madd0.AzureStorageDriver
{
- using System;
using System.Collections.Generic;
+ using System.Data.Services.Client;
+ using System.Reflection;
using LINQPad.Extensibility.DataContext;
using Madd0.AzureStorageDriver.Properties;
@@ -18,6 +19,9 @@ namespace Madd0.AzureStorageDriver
///
public class AzureDriver : DynamicDataContextDriver
{
+ ///
+ /// Gets the name of the driver author.
+ ///
public override string Author
{
get { return Resources.AuthorName; }
@@ -39,6 +43,12 @@ public override string GetConnectionDescription(IConnectionInfo connectionInfo)
return new StorageAccountProperties(connectionInfo).DisplayName;
}
+ ///
+ /// Shows the connection dialog.
+ ///
+ /// The connection info.
+ /// if set to true [is new connection].
+ ///
public override bool ShowConnectionDialog(IConnectionInfo connectionInfo, bool isNewConnection)
{
if (isNewConnection)
@@ -50,6 +60,12 @@ public override bool ShowConnectionDialog(IConnectionInfo connectionInfo, bool i
return result == true;
}
+ ///
+ /// Determines whether two repositories are equivalent.
+ ///
+ /// The connection information of the first repository.
+ /// The connection information of the second repository.
+ /// true if both repositories use the same account name; false otherwise.
public override bool AreRepositoriesEquivalent(IConnectionInfo connection1, IConnectionInfo connection2)
{
var account1 = (string)connection1.DriverData.Element("AccountName") ?? string.Empty;
@@ -58,6 +74,10 @@ public override bool AreRepositoriesEquivalent(IConnectionInfo connection1, ICon
return account1.Equals(account2);
}
+ ///
+ /// Gets the assemblies to add.
+ ///
+ /// A list of assembly names to add in order to execute the current driver.
public override IEnumerable GetAssembliesToAdd()
{
return new string[]
@@ -67,6 +87,10 @@ public override IEnumerable GetAssembliesToAdd()
};
}
+ ///
+ /// Gets the namespaces to add.
+ ///
+ /// A list of namespaces to add in order to execute this driver.
public override IEnumerable GetNamespacesToAdd()
{
return new string[]
@@ -77,34 +101,74 @@ public override IEnumerable GetNamespacesToAdd()
};
}
- public override List GetSchemaAndBuildAssembly(IConnectionInfo connectionInfo, System.Reflection.AssemblyName assemblyToBuild, ref string nameSpace, ref string typeName)
+ ///
+ /// Gets the schema and builds the assembly that contains the typed data context.
+ ///
+ /// The connection information.
+ /// The assembly to build.
+ /// The namespace to be used in the generated code.
+ /// Name of the type of the typed data context.
+ /// A list of instaces that describes the current schema.
+ public override List GetSchemaAndBuildAssembly(IConnectionInfo connectionInfo, AssemblyName assemblyToBuild, ref string @namespace, ref string typeName)
{
+ // The helper class SchemaBuilder will do the heavy lifting
return SchemaBuilder.GetSchemaAndBuildAssembly(
new StorageAccountProperties(connectionInfo),
this.GetDriverFolder(),
assemblyToBuild,
- ref nameSpace,
- ref typeName);
+ @namespace,
+ typeName);
}
+ ///
+ /// Gets the context constructor arguments.
+ ///
+ /// The connection info.
+ /// An ordered collection of objects to pass to the data context as arguments.
public override object[] GetContextConstructorArguments(IConnectionInfo connectionInfo)
{
var properties = new StorageAccountProperties(connectionInfo);
+ var storageAccount = properties.GetStorageAccount();
+
return new object[]
{
- properties.GetStorageAccount().TableEndpoint.ToString(),
- properties.GetStorageAccount().Credentials
+ storageAccount.TableEndpoint.ToString(),
+ storageAccount.Credentials,
+ storageAccount
};
}
+ ///
+ /// Gets the context constructor parameters.
+ ///
+ /// The connection info.
+ /// A list of objects that describe the parameters
+ /// of the typed data context's constructor.
public override ParameterDescriptor[] GetContextConstructorParameters(IConnectionInfo connectionInfo)
{
return new[]
{
new ParameterDescriptor("baseAddress", "System.String"),
- new ParameterDescriptor("credentials", "Microsoft.WindowsAzure.StorageCredentials")
+ new ParameterDescriptor("credentials", "Microsoft.WindowsAzure.StorageCredentials"),
+ new ParameterDescriptor("account", "Microsoft.WindowsAzure.CloudStorageAccount")
};
}
+
+ ///
+ /// Initializes the data context.
+ ///
+ /// In this driver, initialization consists of listening to the
+ /// event in order to extract the requested
+ /// URI and display it in the SQL tab.
+ /// The connection info.
+ /// The context.
+ /// The execution manager.
+ public override void InitializeContext(IConnectionInfo connectionInfo, object context, QueryExecutionManager executionManager)
+ {
+ var dsContext = (DataServiceContext)context;
+
+ dsContext.SendingRequest += (sender, e) => executionManager.SqlTranslationWriter.WriteLine(e.Request.RequestUri);
+ }
}
}
diff --git a/Madd0.AzureStorageDriver/ConnectionDialog.xaml.cs b/Madd0.AzureStorageDriver/ConnectionDialog.xaml.cs
index caac10e..32404e3 100644
--- a/Madd0.AzureStorageDriver/ConnectionDialog.xaml.cs
+++ b/Madd0.AzureStorageDriver/ConnectionDialog.xaml.cs
@@ -11,6 +11,7 @@ namespace Madd0.AzureStorageDriver
using System;
using System.Runtime.InteropServices;
using System.Windows;
+ using System.Windows.Interop;
using LINQPad.Extensibility.DataContext;
///
@@ -18,6 +19,10 @@ namespace Madd0.AzureStorageDriver
///
public partial class ConnectionDialog : Window
{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The connection info.
public ConnectionDialog(IConnectionInfo connectionInfo)
{
InitializeComponent();
@@ -25,27 +30,42 @@ public ConnectionDialog(IConnectionInfo connectionInfo)
DataContext = new StorageAccountProperties(connectionInfo);
}
- [DllImport("user32.dll")]
- static extern uint GetWindowLong(IntPtr hWnd, int nIndex);
-
- [DllImport("user32.dll")]
- static extern int SetWindowLong(IntPtr hWnd, int nIndex, uint dwNewLong);
-
- private const int GWL_STYLE = -16;
-
- private const uint WS_SYSMENU = 0x80000;
-
+ ///
+ /// Raises the event.
+ ///
+ /// An that contains the event data.
protected override void OnSourceInitialized(EventArgs e)
{
- IntPtr hwnd = new System.Windows.Interop.WindowInteropHelper(this).Handle;
+ IntPtr hwnd = new WindowInteropHelper(this).Handle;
+
+ // Change the window style to remove icon and buttons
SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & (0xFFFFFFFF ^ WS_SYSMENU));
base.OnSourceInitialized(e);
}
+ ///
+ /// Called when the OK button is clicked.
+ ///
+ /// The sender.
+ /// The instance containing the event data.
private void OnOkClick(object sender, RoutedEventArgs e)
{
this.DialogResult = true;
}
+
+ #region p/invoke
+
+ [DllImport("user32.dll")]
+ private static extern uint GetWindowLong(IntPtr hWnd, int nIndex);
+
+ [DllImport("user32.dll")]
+ private static extern int SetWindowLong(IntPtr hWnd, int nIndex, uint dwNewLong);
+
+ private const int GWL_STYLE = -16;
+
+ private const uint WS_SYSMENU = 0x80000;
+
+ #endregion
}
}
diff --git a/Madd0.AzureStorageDriver/DataContextTemplate.cs b/Madd0.AzureStorageDriver/DataContextTemplate.cs
new file mode 100644
index 0000000..9e379c6
--- /dev/null
+++ b/Madd0.AzureStorageDriver/DataContextTemplate.cs
@@ -0,0 +1,458 @@
+// ------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version: 10.0.0.0
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+// ------------------------------------------------------------------------------
+namespace Madd0.AzureStorageDriver
+{
+ using System;
+ using System.Collections;
+ using System.Collections.Generic;
+
+
+ #line 1 "C:\Users\madd0\Documents\Personal Projects\Madd0.AzureStorageDriver\Madd0.AzureStorageDriver\DataContextTemplate.tt"
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "10.0.0.0")]
+ public partial class DataContextTemplate : DataContextTemplateBase
+ {
+ public virtual string TransformText()
+ {
+ this.Write("\r\nnamespace ");
+
+ #line 6 "C:\Users\madd0\Documents\Personal Projects\Madd0.AzureStorageDriver\Madd0.AzureStorageDriver\DataContextTemplate.tt"
+ this.Write(this.ToStringHelper.ToStringWithCulture(this.Namespace));
+
+ #line default
+ #line hidden
+ this.Write("\r\n{\r\n using System;\r\n using System.Data.Services.Client;\r\n using Microso" +
+ "ft.WindowsAzure;\r\n using Microsoft.WindowsAzure.StorageClient;\r\n\r\n public " +
+ "class ");
+
+ #line 13 "C:\Users\madd0\Documents\Personal Projects\Madd0.AzureStorageDriver\Madd0.AzureStorageDriver\DataContextTemplate.tt"
+ this.Write(this.ToStringHelper.ToStringWithCulture(this.TypeName));
+
+ #line default
+ #line hidden
+ this.Write(" : TableServiceContext\r\n {\r\n public ");
+
+ #line 15 "C:\Users\madd0\Documents\Personal Projects\Madd0.AzureStorageDriver\Madd0.AzureStorageDriver\DataContextTemplate.tt"
+ this.Write(this.ToStringHelper.ToStringWithCulture(this.TypeName));
+
+ #line default
+ #line hidden
+ this.Write(@"(string baseAddress, StorageCredentials credentials, CloudStorageAccount account)
+ : base(baseAddress, credentials)
+ {
+ this.TableClient = account.CreateCloudTableClient();
+ }
+
+ public CloudTableClient TableClient
+ {
+ get;
+ private set;
+ }
+
+");
+
+ #line 27 "C:\Users\madd0\Documents\Personal Projects\Madd0.AzureStorageDriver\Madd0.AzureStorageDriver\DataContextTemplate.tt"
+
+foreach (var table in this.Tables)
+{
+
+
+ #line default
+ #line hidden
+ this.Write(" public DataServiceQuery<");
+
+ #line 31 "C:\Users\madd0\Documents\Personal Projects\Madd0.AzureStorageDriver\Madd0.AzureStorageDriver\DataContextTemplate.tt"
+ this.Write(this.ToStringHelper.ToStringWithCulture(table.Name));
+
+ #line default
+ #line hidden
+ this.Write("Entity> ");
+
+ #line 31 "C:\Users\madd0\Documents\Personal Projects\Madd0.AzureStorageDriver\Madd0.AzureStorageDriver\DataContextTemplate.tt"
+ this.Write(this.ToStringHelper.ToStringWithCulture(table.Name));
+
+ #line default
+ #line hidden
+ this.Write("\r\n {\r\n get\r\n {\r\n return this.CreateQu" +
+ "ery<");
+
+ #line 35 "C:\Users\madd0\Documents\Personal Projects\Madd0.AzureStorageDriver\Madd0.AzureStorageDriver\DataContextTemplate.tt"
+ this.Write(this.ToStringHelper.ToStringWithCulture(table.Name));
+
+ #line default
+ #line hidden
+ this.Write("Entity>(\"");
+
+ #line 35 "C:\Users\madd0\Documents\Personal Projects\Madd0.AzureStorageDriver\Madd0.AzureStorageDriver\DataContextTemplate.tt"
+ this.Write(this.ToStringHelper.ToStringWithCulture(table.Name));
+
+ #line default
+ #line hidden
+ this.Write("\");\r\n }\r\n }\r\n");
+
+ #line 38 "C:\Users\madd0\Documents\Personal Projects\Madd0.AzureStorageDriver\Madd0.AzureStorageDriver\DataContextTemplate.tt"
+
+}
+
+
+ #line default
+ #line hidden
+ this.Write(" }\r\n\r\n");
+
+ #line 43 "C:\Users\madd0\Documents\Personal Projects\Madd0.AzureStorageDriver\Madd0.AzureStorageDriver\DataContextTemplate.tt"
+
+foreach (var table in this.Tables)
+{
+
+
+ #line default
+ #line hidden
+ this.Write(" public class ");
+
+ #line 47 "C:\Users\madd0\Documents\Personal Projects\Madd0.AzureStorageDriver\Madd0.AzureStorageDriver\DataContextTemplate.tt"
+ this.Write(this.ToStringHelper.ToStringWithCulture(table.Name));
+
+ #line default
+ #line hidden
+ this.Write("Entity : TableServiceEntity\r\n {\r\n ");
+
+ #line 49 "C:\Users\madd0\Documents\Personal Projects\Madd0.AzureStorageDriver\Madd0.AzureStorageDriver\DataContextTemplate.tt"
+
+ foreach (var column in table.Columns)
+ {
+ if (!this.DefaultProperties.Contains(column.Name))
+ {
+
+
+ #line default
+ #line hidden
+ this.Write(" public ");
+
+ #line 55 "C:\Users\madd0\Documents\Personal Projects\Madd0.AzureStorageDriver\Madd0.AzureStorageDriver\DataContextTemplate.tt"
+ this.Write(this.ToStringHelper.ToStringWithCulture(column.TypeName));
+
+ #line default
+ #line hidden
+ this.Write(" ");
+
+ #line 55 "C:\Users\madd0\Documents\Personal Projects\Madd0.AzureStorageDriver\Madd0.AzureStorageDriver\DataContextTemplate.tt"
+ this.Write(this.ToStringHelper.ToStringWithCulture(column.Name));
+
+ #line default
+ #line hidden
+ this.Write("\r\n {\r\n get;\r\n set;\r\n }\r\n ");
+
+ #line 60 "C:\Users\madd0\Documents\Personal Projects\Madd0.AzureStorageDriver\Madd0.AzureStorageDriver\DataContextTemplate.tt"
+
+ }
+ }
+
+
+ #line default
+ #line hidden
+ this.Write(" }\r\n");
+
+ #line 65 "C:\Users\madd0\Documents\Personal Projects\Madd0.AzureStorageDriver\Madd0.AzureStorageDriver\DataContextTemplate.tt"
+
+}
+
+
+ #line default
+ #line hidden
+ this.Write("}\r\n\r\n");
+ return this.GenerationEnvironment.ToString();
+ }
+
+ #line 70 "C:\Users\madd0\Documents\Personal Projects\Madd0.AzureStorageDriver\Madd0.AzureStorageDriver\DataContextTemplate.tt"
+
+private readonly List DefaultProperties = new List { "PartitionKey", "RowKey", "Timestamp" };
+
+public string Namespace { get; set; }
+
+public string TypeName { get; set; }
+
+public IEnumerable Tables { get; set; }
+
+
+ #line default
+ #line hidden
+ }
+
+ #line default
+ #line hidden
+ #region Base class
+ ///
+ /// Base class for this transformation
+ ///
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "10.0.0.0")]
+ public class DataContextTemplateBase
+ {
+ #region Fields
+ private global::System.Text.StringBuilder generationEnvironmentField;
+ private global::System.CodeDom.Compiler.CompilerErrorCollection errorsField;
+ private global::System.Collections.Generic.List indentLengthsField;
+ private string currentIndentField = "";
+ private bool endsWithNewline;
+ private global::System.Collections.Generic.IDictionary sessionField;
+ #endregion
+ #region Properties
+ ///
+ /// The string builder that generation-time code is using to assemble generated output
+ ///
+ protected System.Text.StringBuilder GenerationEnvironment
+ {
+ get
+ {
+ if ((this.generationEnvironmentField == null))
+ {
+ this.generationEnvironmentField = new global::System.Text.StringBuilder();
+ }
+ return this.generationEnvironmentField;
+ }
+ set
+ {
+ this.generationEnvironmentField = value;
+ }
+ }
+ ///
+ /// The error collection for the generation process
+ ///
+ public System.CodeDom.Compiler.CompilerErrorCollection Errors
+ {
+ get
+ {
+ if ((this.errorsField == null))
+ {
+ this.errorsField = new global::System.CodeDom.Compiler.CompilerErrorCollection();
+ }
+ return this.errorsField;
+ }
+ }
+ ///
+ /// A list of the lengths of each indent that was added with PushIndent
+ ///
+ private System.Collections.Generic.List indentLengths
+ {
+ get
+ {
+ if ((this.indentLengthsField == null))
+ {
+ this.indentLengthsField = new global::System.Collections.Generic.List();
+ }
+ return this.indentLengthsField;
+ }
+ }
+ ///
+ /// Gets the current indent we use when adding lines to the output
+ ///
+ public string CurrentIndent
+ {
+ get
+ {
+ return this.currentIndentField;
+ }
+ }
+ ///
+ /// Current transformation session
+ ///
+ public virtual global::System.Collections.Generic.IDictionary Session
+ {
+ get
+ {
+ return this.sessionField;
+ }
+ set
+ {
+ this.sessionField = value;
+ }
+ }
+ #endregion
+ #region Transform-time helpers
+ ///
+ /// Write text directly into the generated output
+ ///
+ public void Write(string textToAppend)
+ {
+ if (string.IsNullOrEmpty(textToAppend))
+ {
+ return;
+ }
+ // If we're starting off, or if the previous text ended with a newline,
+ // we have to append the current indent first.
+ if (((this.GenerationEnvironment.Length == 0)
+ || this.endsWithNewline))
+ {
+ this.GenerationEnvironment.Append(this.currentIndentField);
+ this.endsWithNewline = false;
+ }
+ // Check if the current text ends with a newline
+ if (textToAppend.EndsWith(global::System.Environment.NewLine, global::System.StringComparison.CurrentCulture))
+ {
+ this.endsWithNewline = true;
+ }
+ // This is an optimization. If the current indent is "", then we don't have to do any
+ // of the more complex stuff further down.
+ if ((this.currentIndentField.Length == 0))
+ {
+ this.GenerationEnvironment.Append(textToAppend);
+ return;
+ }
+ // Everywhere there is a newline in the text, add an indent after it
+ textToAppend = textToAppend.Replace(global::System.Environment.NewLine, (global::System.Environment.NewLine + this.currentIndentField));
+ // If the text ends with a newline, then we should strip off the indent added at the very end
+ // because the appropriate indent will be added when the next time Write() is called
+ if (this.endsWithNewline)
+ {
+ this.GenerationEnvironment.Append(textToAppend, 0, (textToAppend.Length - this.currentIndentField.Length));
+ }
+ else
+ {
+ this.GenerationEnvironment.Append(textToAppend);
+ }
+ }
+ ///
+ /// Write text directly into the generated output
+ ///
+ public void WriteLine(string textToAppend)
+ {
+ this.Write(textToAppend);
+ this.GenerationEnvironment.AppendLine();
+ this.endsWithNewline = true;
+ }
+ ///
+ /// Write formatted text directly into the generated output
+ ///
+ public void Write(string format, params object[] args)
+ {
+ this.Write(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args));
+ }
+ ///
+ /// Write formatted text directly into the generated output
+ ///
+ public void WriteLine(string format, params object[] args)
+ {
+ this.WriteLine(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args));
+ }
+ ///
+ /// Raise an error
+ ///
+ public void Error(string message)
+ {
+ System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError();
+ error.ErrorText = message;
+ this.Errors.Add(error);
+ }
+ ///
+ /// Raise a warning
+ ///
+ public void Warning(string message)
+ {
+ System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError();
+ error.ErrorText = message;
+ error.IsWarning = true;
+ this.Errors.Add(error);
+ }
+ ///
+ /// Increase the indent
+ ///
+ public void PushIndent(string indent)
+ {
+ if ((indent == null))
+ {
+ throw new global::System.ArgumentNullException("indent");
+ }
+ this.currentIndentField = (this.currentIndentField + indent);
+ this.indentLengths.Add(indent.Length);
+ }
+ ///
+ /// Remove the last indent that was added with PushIndent
+ ///
+ public string PopIndent()
+ {
+ string returnValue = "";
+ if ((this.indentLengths.Count > 0))
+ {
+ int indentLength = this.indentLengths[(this.indentLengths.Count - 1)];
+ this.indentLengths.RemoveAt((this.indentLengths.Count - 1));
+ if ((indentLength > 0))
+ {
+ returnValue = this.currentIndentField.Substring((this.currentIndentField.Length - indentLength));
+ this.currentIndentField = this.currentIndentField.Remove((this.currentIndentField.Length - indentLength));
+ }
+ }
+ return returnValue;
+ }
+ ///
+ /// Remove any indentation
+ ///
+ public void ClearIndent()
+ {
+ this.indentLengths.Clear();
+ this.currentIndentField = "";
+ }
+ #endregion
+ #region ToString Helpers
+ ///
+ /// Utility class to produce culture-oriented representation of an object as a string.
+ ///
+ public class ToStringInstanceHelper
+ {
+ private System.IFormatProvider formatProviderField = global::System.Globalization.CultureInfo.InvariantCulture;
+ ///
+ /// Gets or sets format provider to be used by ToStringWithCulture method.
+ ///
+ public System.IFormatProvider FormatProvider
+ {
+ get
+ {
+ return this.formatProviderField ;
+ }
+ set
+ {
+ if ((value != null))
+ {
+ this.formatProviderField = value;
+ }
+ }
+ }
+ ///
+ /// This is called from the compile/run appdomain to convert objects within an expression block to a string
+ ///
+ public string ToStringWithCulture(object objectToConvert)
+ {
+ if ((objectToConvert == null))
+ {
+ throw new global::System.ArgumentNullException("objectToConvert");
+ }
+ System.Type t = objectToConvert.GetType();
+ System.Reflection.MethodInfo method = t.GetMethod("ToString", new System.Type[] {
+ typeof(System.IFormatProvider)});
+ if ((method == null))
+ {
+ return objectToConvert.ToString();
+ }
+ else
+ {
+ return ((string)(method.Invoke(objectToConvert, new object[] {
+ this.formatProviderField })));
+ }
+ }
+ }
+ private ToStringInstanceHelper toStringHelperField = new ToStringInstanceHelper();
+ public ToStringInstanceHelper ToStringHelper
+ {
+ get
+ {
+ return this.toStringHelperField;
+ }
+ }
+ #endregion
+ }
+ #endregion
+}
diff --git a/Madd0.AzureStorageDriver/DataContextTemplate.tt b/Madd0.AzureStorageDriver/DataContextTemplate.tt
new file mode 100644
index 0000000..1bf623f
--- /dev/null
+++ b/Madd0.AzureStorageDriver/DataContextTemplate.tt
@@ -0,0 +1,78 @@
+<#@ template language="C#" #>
+<#@ import namespace="System" #>
+<#@ import namespace="System.Collections" #>
+<#@ import namespace="System.Collections.Generic" #>
+
+namespace <#= this.Namespace #>
+{
+ using System;
+ using System.Data.Services.Client;
+ using Microsoft.WindowsAzure;
+ using Microsoft.WindowsAzure.StorageClient;
+
+ public class <#= this.TypeName #> : TableServiceContext
+ {
+ public <#= this.TypeName #>(string baseAddress, StorageCredentials credentials, CloudStorageAccount account)
+ : base(baseAddress, credentials)
+ {
+ this.TableClient = account.CreateCloudTableClient();
+ }
+
+ public CloudTableClient TableClient
+ {
+ get;
+ private set;
+ }
+
+<#
+foreach (var table in this.Tables)
+{
+#>
+ public DataServiceQuery<<#= table.Name #>Entity> <#= table.Name #>
+ {
+ get
+ {
+ return this.CreateQuery<<#= table.Name #>Entity>("<#= table.Name #>");
+ }
+ }
+<#
+}
+#>
+ }
+
+<#
+foreach (var table in this.Tables)
+{
+#>
+ public class <#= table.Name #>Entity : TableServiceEntity
+ {
+ <#
+ foreach (var column in table.Columns)
+ {
+ if (!this.DefaultProperties.Contains(column.Name))
+ {
+ #>
+ public <#= column.TypeName #> <#= column.Name #>
+ {
+ get;
+ set;
+ }
+ <#
+ }
+ }
+ #>
+ }
+<#
+}
+#>
+}
+
+<#+
+private readonly List DefaultProperties = new List { "PartitionKey", "RowKey", "Timestamp" };
+
+public string Namespace { get; set; }
+
+public string TypeName { get; set; }
+
+public IEnumerable Tables { get; set; }
+#>
\ No newline at end of file
diff --git a/Madd0.AzureStorageDriver/GenericEntity.cs b/Madd0.AzureStorageDriver/GenericEntity.cs
deleted file mode 100644
index ae37d2f..0000000
--- a/Madd0.AzureStorageDriver/GenericEntity.cs
+++ /dev/null
@@ -1,47 +0,0 @@
-//-----------------------------------------------------------------------
-//
-// Copyright (c) madd0, . All rights reserved.
-//
-//-----------------------------------------------------------------------
-namespace Madd0.AzureStorageDriver
-{
- using System;
- using System.Linq;
- using Microsoft.WindowsAzure.StorageClient;
- using System.Data.Services.Common;
- using System.Collections.Generic;
-
- [DataServiceKey("PartitionKey", "RowKey")]
- public class GenericEntity : TableServiceEntity
- {
- Dictionary properties = new Dictionary();
-
- ///
- /// Gets or sets the table name.
- ///
- /// The table name.
- public string TableName
- {
- get;
- set;
- }
-
- public Dictionary Properties
- {
- get { return this.properties; }
- }
-
- internal string this[string key]
- {
- get
- {
- return this.properties[key];
- }
-
- set
- {
- this.properties[key] = value;
- }
- }
- }
-}
diff --git a/Madd0.AzureStorageDriver/Madd0.AzureStorageDriver.csproj b/Madd0.AzureStorageDriver/Madd0.AzureStorageDriver.csproj
index 805463b..70c000c 100644
--- a/Madd0.AzureStorageDriver/Madd0.AzureStorageDriver.csproj
+++ b/Madd0.AzureStorageDriver/Madd0.AzureStorageDriver.csproj
@@ -40,6 +40,7 @@
..\References\LINQPad.exe
+ False
@@ -56,11 +57,22 @@
-
+
+ True
+ True
+ DataContextTemplate.tt
+
+
ConnectionDialog.xaml
-
+
+
+
+ True
+ True
+ Exceptions.resx
+
True
@@ -68,17 +80,25 @@
Resources.resx
-
+
+
+ TextTemplatingFilePreprocessor
+ DataContextTemplate.cs
+
+
+ ResXFileCodeGenerator
+ Exceptions.Designer.cs
+
ResXFileCodeGenerator
- Resources.Designer.cs
+ Resources1.Designer.cs
@@ -87,6 +107,9 @@
MSBuild:Compile
+
+
+
"$(ProjectDir)DevDeploy.bat"
diff --git a/Madd0.AzureStorageDriver/Model/CloudTable.cs b/Madd0.AzureStorageDriver/Model/CloudTable.cs
new file mode 100644
index 0000000..713336a
--- /dev/null
+++ b/Madd0.AzureStorageDriver/Model/CloudTable.cs
@@ -0,0 +1,36 @@
+//-----------------------------------------------------------------------
+//
+// Copyright (c) 2012 Mauricio DIAZ ORLICH.
+// Code licensed under the MIT X11 license.
+//
+// Mauricio DIAZ ORLICH
+//-----------------------------------------------------------------------
+
+namespace Madd0.AzureStorageDriver
+{
+ using System.Collections.Generic;
+
+ ///
+ /// Holds information about a table from Azure storage.
+ ///
+ public class CloudTable
+ {
+ ///
+ /// Gets or sets the name of the table.
+ ///
+ public string Name
+ {
+ get;
+ set;
+ }
+
+ ///
+ /// Gets or sets the table's properties.
+ ///
+ public IEnumerable Columns
+ {
+ get;
+ set;
+ }
+ }
+}
diff --git a/Madd0.AzureStorageDriver/Model/GenericEntity.cs b/Madd0.AzureStorageDriver/Model/GenericEntity.cs
new file mode 100644
index 0000000..3fb75a2
--- /dev/null
+++ b/Madd0.AzureStorageDriver/Model/GenericEntity.cs
@@ -0,0 +1,67 @@
+//-----------------------------------------------------------------------
+//
+// Copyright (c) 2012 Mauricio DIAZ ORLICH.
+// Code licensed under the MIT X11 license.
+//
+// Mauricio DIAZ ORLICH
+//-----------------------------------------------------------------------
+
+namespace Madd0.AzureStorageDriver
+{
+ using System.Collections.Generic;
+ using Microsoft.WindowsAzure.StorageClient;
+
+ ///
+ /// Represents a generic entity from table storage.
+ ///
+ ///
+ /// This class extends , which gives it the
+ /// , and properties,
+ /// but all other properties that are obtained from table storage are simply stored in its
+ /// dictionary as string values, where the property name is the property in
+ /// the dictionary.
+ ///
+ public class GenericEntity : TableServiceEntity
+ {
+ ///
+ /// Holds the property data.
+ ///
+ private Dictionary properties = new Dictionary();
+
+ ///
+ /// Gets or sets the table name.
+ ///
+ /// The table name.
+ public string TableName
+ {
+ get;
+ set;
+ }
+
+ ///
+ /// Gets the list of properties of a table entity (except for PartitionKey, RowKey and
+ /// Timestamp) and their string values.
+ ///
+ public Dictionary Properties
+ {
+ get { return this.properties; }
+ }
+
+ ///
+ /// Gets or sets the with the specified property.
+ ///
+ /// The name of the property.
+ public string this[string property]
+ {
+ get
+ {
+ return this.properties[property];
+ }
+
+ set
+ {
+ this.properties[property] = value;
+ }
+ }
+ }
+}
diff --git a/Madd0.AzureStorageDriver/StorageAccountProperties.cs b/Madd0.AzureStorageDriver/Model/StorageAccountProperties.cs
similarity index 70%
rename from Madd0.AzureStorageDriver/StorageAccountProperties.cs
rename to Madd0.AzureStorageDriver/Model/StorageAccountProperties.cs
index 175ba95..5a890a7 100644
--- a/Madd0.AzureStorageDriver/StorageAccountProperties.cs
+++ b/Madd0.AzureStorageDriver/Model/StorageAccountProperties.cs
@@ -9,9 +9,8 @@
namespace Madd0.AzureStorageDriver
{
using System;
- using System.Linq;
- using LINQPad.Extensibility.DataContext;
using System.Xml.Linq;
+ using LINQPad.Extensibility.DataContext;
using Madd0.AzureStorageDriver.Properties;
using Microsoft.WindowsAzure;
@@ -23,18 +22,29 @@ public class StorageAccountProperties
private readonly IConnectionInfo _connectionInfo;
private readonly XElement _driverData;
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The connection info.
public StorageAccountProperties(IConnectionInfo connectionInfo)
{
this._connectionInfo = connectionInfo;
this._driverData = connectionInfo.DriverData;
}
+ ///
+ /// Gets or sets a value indicating whether this connection should be remembered.
+ ///
+ /// true if this connection should be remembered; otherwise, false.
public bool Persist
{
get { return this._connectionInfo.Persist; }
set { this._connectionInfo.Persist = value; }
}
+ ///
+ /// Gets the display name of the connection.
+ ///
public string DisplayName
{
get
@@ -50,6 +60,10 @@ public string DisplayName
}
}
+ ///
+ /// Gets or sets a value indicating whether local storage is being used.
+ ///
+ /// true if local storage is used; otherwise, false.
public bool UseLocalStorage
{
get
@@ -69,6 +83,10 @@ public bool UseLocalStorage
}
}
+ ///
+ /// Gets or sets a value indicating whether to use HTTPS.
+ ///
+ /// true if HTTPS is used; otherwise, false.
public bool UseHttps
{
get
@@ -83,12 +101,18 @@ public bool UseHttps
}
}
+ ///
+ /// Gets or sets the name of the storage account.
+ ///
public string AccountName
{
get { return (string)this._driverData.Element("AccountName") ?? string.Empty; }
set { this._driverData.SetElementValue("AccountName", value); }
}
+ ///
+ /// Gets or sets the key for the storage account.
+ ///
public string AccountKey
{
get
@@ -104,6 +128,27 @@ public string AccountKey
}
}
+ ///
+ /// Gets a instace for the current connection.
+ ///
+ /// A instace configured with the credentials
+ /// of the current connection.
+ public CloudStorageAccount GetStorageAccount()
+ {
+ if (this.UseLocalStorage)
+ {
+ return CloudStorageAccount.DevelopmentStorageAccount;
+ }
+ else
+ {
+ return new CloudStorageAccount(new StorageCredentialsAccountAndKey(this.AccountName, this.AccountKey), this.UseHttps);
+ }
+ }
+
+ ///
+ /// Clears the account name and key.
+ ///
+ /// This method is called when local storage is used.
private void ClearAccountNameAndKey()
{
var accountName = this._driverData.Element("AccountName");
@@ -119,17 +164,5 @@ private void ClearAccountNameAndKey()
accountKey.Remove();
}
}
-
- public CloudStorageAccount GetStorageAccount()
- {
- if (this.UseLocalStorage)
- {
- return CloudStorageAccount.DevelopmentStorageAccount;
- }
- else
- {
- return new CloudStorageAccount(new StorageCredentialsAccountAndKey(this.AccountName, this.AccountKey), this.UseHttps);
- }
- }
}
}
diff --git a/Madd0.AzureStorageDriver/Model/TableColumn.cs b/Madd0.AzureStorageDriver/Model/TableColumn.cs
new file mode 100644
index 0000000..6d123a0
--- /dev/null
+++ b/Madd0.AzureStorageDriver/Model/TableColumn.cs
@@ -0,0 +1,34 @@
+//-----------------------------------------------------------------------
+//
+// Copyright (c) 2012 Mauricio DIAZ ORLICH.
+// Code licensed under the MIT X11 license.
+//
+// Mauricio DIAZ ORLICH
+//-----------------------------------------------------------------------
+
+namespace Madd0.AzureStorageDriver
+{
+ ///
+ /// Holds information about a column of a table in Azure storage.
+ ///
+ public class TableColumn
+ {
+ ///
+ /// Gets or sets the name of the property.
+ ///
+ public string Name
+ {
+ get;
+ set;
+ }
+
+ ///
+ /// Gets or sets the type name of the property.
+ ///
+ public string TypeName
+ {
+ get;
+ set;
+ }
+ }
+}
diff --git a/Madd0.AzureStorageDriver/Properties/AssemblyInfo.cs b/Madd0.AzureStorageDriver/Properties/AssemblyInfo.cs
index dfef70b..e8b2690 100644
--- a/Madd0.AzureStorageDriver/Properties/AssemblyInfo.cs
+++ b/Madd0.AzureStorageDriver/Properties/AssemblyInfo.cs
@@ -1,44 +1,29 @@
//-----------------------------------------------------------------------
-//
-// Copyright (c) madd0, . All rights reserved.
+//
+// Copyright (c) 2012 Mauricio DIAZ ORLICH.
+// Code licensed under the MIT X11 license.
//
+// Mauricio DIAZ ORLICH
//-----------------------------------------------------------------------
+
using System;
using System.Reflection;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
using System.Resources;
+using System.Runtime.InteropServices;
+using Madd0.AzureStorageDriver.Properties;
-// General Information about an assembly is controlled through the following
-// set of attributes. Change these attribute values to modify the information
-// associated with an assembly.
[assembly: AssemblyTitle("AzureStorageDriver")]
[assembly: AssemblyDescription("A LINQPad driver to perform queries againt Azure Table Storage.")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("madd0 (http://www.madd0.com)")]
[assembly: AssemblyProduct("AzureStorageDriver")]
[assembly: AssemblyCopyright("Copyright © 2012 Mauricio DIAZ ORLICH")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-// Setting ComVisible to false makes the types in this assembly not visible
-// to COM components. If you need to access a type in this assembly from
-// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("0d14276c-9f70-46bf-903e-877750970f43")]
-// Version information for an assembly consists of the following four values:
-//
-// Major Version
-// Minor Version
-// Build Number
-// Revision
-//
-// You can specify all the values or you can default the Build and Revision Numbers
-// by using the '*' as shown below:
-// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("0.1.0.0")]
-[assembly: AssemblyFileVersion("0.1.0.0")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: NeutralResourcesLanguageAttribute("")]
diff --git a/Madd0.AzureStorageDriver/Properties/Exceptions.Designer.cs b/Madd0.AzureStorageDriver/Properties/Exceptions.Designer.cs
new file mode 100644
index 0000000..babf237
--- /dev/null
+++ b/Madd0.AzureStorageDriver/Properties/Exceptions.Designer.cs
@@ -0,0 +1,81 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.239
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace Madd0.AzureStorageDriver.Properties {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Exceptions {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Exceptions() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Madd0.AzureStorageDriver.Properties.Exceptions", typeof(Exceptions).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Cannot compile typed context: {0} (line {1}).
+ ///
+ internal static string CannotCompileCode {
+ get {
+ return ResourceManager.GetString("CannotCompileCode", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Deserialized type '{0}' is not supported..
+ ///
+ internal static string TypeNotSupported {
+ get {
+ return ResourceManager.GetString("TypeNotSupported", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/Madd0.AzureStorageDriver/Properties/Exceptions.resx b/Madd0.AzureStorageDriver/Properties/Exceptions.resx
new file mode 100644
index 0000000..b3ad500
--- /dev/null
+++ b/Madd0.AzureStorageDriver/Properties/Exceptions.resx
@@ -0,0 +1,126 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Cannot compile typed context: {0} (line {1})
+
+
+ Deserialized type '{0}' is not supported.
+
+
\ No newline at end of file
diff --git a/Madd0.AzureStorageDriver/Properties/Resources.Designer.cs b/Madd0.AzureStorageDriver/Properties/Resources.Designer.cs
index eded67e..e0c260f 100644
--- a/Madd0.AzureStorageDriver/Properties/Resources.Designer.cs
+++ b/Madd0.AzureStorageDriver/Properties/Resources.Designer.cs
@@ -60,6 +60,24 @@ internal class Resources {
}
}
+ ///
+ /// Looks up a localized string similar to AzureTableStorage.
+ ///
+ internal static string AssemblyTitle {
+ get {
+ return ResourceManager.GetString("AssemblyTitle", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to 0.1.0.0.
+ ///
+ internal static string AssemblyVersion {
+ get {
+ return ResourceManager.GetString("AssemblyVersion", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Mauricio Diaz Orlich.
///
diff --git a/Madd0.AzureStorageDriver/SchemaBuilder.cs b/Madd0.AzureStorageDriver/SchemaBuilder.cs
index 2b4c153..9781ba7 100644
--- a/Madd0.AzureStorageDriver/SchemaBuilder.cs
+++ b/Madd0.AzureStorageDriver/SchemaBuilder.cs
@@ -8,115 +8,213 @@
namespace Madd0.AzureStorageDriver
{
using System;
- using System.Linq;
+ using System.CodeDom.Compiler;
using System.Collections.Generic;
- using LINQPad.Extensibility.DataContext;
+ using System.Data.Services.Client;
+ using System.IO;
+ using System.Linq;
using System.Reflection;
using System.Xml.Linq;
- using Microsoft.WindowsAzure;
- using Microsoft.WindowsAzure.StorageClient;
- using System.Data.Services.Client;
- using System.CodeDom.Compiler;
+ using LINQPad.Extensibility.DataContext;
+ using Madd0.AzureStorageDriver.Properties;
using Microsoft.CSharp;
- using System.IO;
+ using Microsoft.WindowsAzure.StorageClient;
///
- /// TODO: Provide summary section in the documentation header.
+ /// Provides the methods necessary to determining the storage account's schema and to building
+ /// the typed data context .
///
internal static class SchemaBuilder
{
+ // XML namespaces
+ private static readonly XNamespace AtomNS = "http://www.w3.org/2005/Atom";
+ private static readonly XNamespace dNS = "http://schemas.microsoft.com/ado/2007/08/dataservices";
+ private static readonly XNamespace mNS = "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata";
- public static List GetSchemaAndBuildAssembly(StorageAccountProperties properties, string driverFolder, AssemblyName name, ref string nameSpace, ref string typeName)
- {
+ // Names of columns that should be marked as table keys.
+ private static readonly List keyColumns = new List { "PartitionKey", "RowKey" };
- // Read the EDM schema into an XDocument:
- //XDocument data;
- //using (XmlReader reader = GetSchemaReader(props))
- // data = XDocument.Load(reader);
+ ///
+ /// Gets the schema and builds the assembly.
+ ///
+ /// The current configuration.
+ /// The driver folder. Used to resolve dependencies.
+ /// The instace of the assembly being created.
+ /// The namespace to be used in the generated code.
+ /// Name of the type of the typed data context.
+ /// A list of instaces that describes the current schema.
+ public static List GetSchemaAndBuildAssembly(StorageAccountProperties properties, string driverFolder, AssemblyName name, string @namepace, string typeName)
+ {
+ // Get the model from Azure storage
+ var model = GetModel(properties);
- // Generate the code using the ADO.NET Data Services classes:
- //string code;
- //using (XmlReader reader = data.CreateReader())
- // code = GenerateCode(reader, nameSpace);
+ // Generate C# code
+ var code = GenerateCode(typeName, @namepace, model);
- // Compile the code into the assembly, using the assembly name provided:
- BuildAssembly(name, driverFolder, typeName, nameSpace);
+ // And compile the code into the assembly
+ BuildAssembly(name, driverFolder, code);
- // Use the schema to populate the Schema Explorer:
- //List schema = GetSchema(data, out typeName);
- List schema = GetSchema(properties);
+ // Generate the schema for LINQPad
+ List schema = GetSchema(model);
return schema;
}
- private static void BuildAssembly(AssemblyName name, string driverFolder, string typeName, string nameSpace)
+ ///
+ /// Build a model of the current Azure storage account. This model will be used to generate
+ /// the typed code as well as the schema needed by LINQPad.
+ ///
+ /// The current configuration.
+ /// A list of instances that describe the current Azure
+ /// storage model.
+ private static IEnumerable GetModel(StorageAccountProperties properties)
{
- var code = @"namespace " + nameSpace + @"
-{
- public class " + typeName + @" : Microsoft.WindowsAzure.StorageClient.TableServiceContext
- {
- public " + typeName + @"(string baseAddress, Microsoft.WindowsAzure.StorageCredentials credentials)
- : base(baseAddress, credentials)
+ var tableClient = properties.GetStorageAccount().CreateCloudTableClient();
+
+ var dataContext = tableClient.GetDataServiceContext();
+
+ // Entity deserialization has to be handled in a particular way since we are using a GenericEntity to
+ // to read all tables
+ dataContext.ReadingEntity += OnReadingEntity;
+
+ // First get a list of all tables
+ var model = (from tableName in tableClient.ListTables()
+ select new CloudTable
+ {
+ Name = tableName
+ }).ToList();
+
+ // Then go through them
+ foreach (var table in model)
+ {
+ // Read the first entity to determine the table's schema
+ var firstRow = dataContext.CreateQuery(table.Name).Take(1).FirstOrDefault();
+
+ if (null == firstRow)
+ {
+ // If there is no first entity, set a list with the mandatory PartitionKey, RowKey and
+ // Timestamp columns, which we know always exist
+ table.Columns = new[]
+ {
+ new TableColumn { Name = "PartitionKey", TypeName = GetType("Edm.String") },
+ new TableColumn { Name = "RowKey", TypeName = GetType("Edm.String") },
+ new TableColumn { Name = "Timestamp", TypeName = GetType("Edm.DateTime") }
+ };
+ }
+ else
+ {
+ // Otherwise create a new TableColumn for each type
+ table.Columns = from columnName in firstRow.Properties
+ select new TableColumn
+ {
+ Name = columnName.Key,
+ TypeName = GetType(columnName.Value)
+ };
+ }
+ }
+
+ return model;
+ }
+
+ ///
+ /// Generates the code to build the typed data context.
+ ///
+ /// Name of the type.
+ /// The namespace.
+ /// The model.
+ /// The code to be compiled as a string.
+ private static string GenerateCode(string typeName, string @namespace, IEnumerable model)
{
+ // We use a T4-generated class as the template
+ var codeGenerator = new DataContextTemplate();
+
+ codeGenerator.Namespace = @namespace;
+ codeGenerator.TypeName = typeName;
+ codeGenerator.Tables = model;
+ return codeGenerator.TransformText();
}
- }
-}";
+
+ ///
+ /// Builds the assembly described by the .
+ ///
+ /// The instace of the assembly being created.
+ /// The driver folder. Used to resolve dependencies.
+ /// The code of the typed data context.
+ private static void BuildAssembly(AssemblyName name, string driverFolder, string code)
+ {
CompilerResults results;
+
+ var dependencies = new[]
+ {
+ "System.dll",
+ "System.Core.dll",
+ "System.Data.Services.Client.dll",
+ Path.Combine(driverFolder, "Microsoft.WindowsAzure.StorageClient.dll")
+ };
+
+ // Use the CSharpCodeProvider to compile. Since the driver is .NET 4.0, the typed assembly should be also
using (var codeProvider = new CSharpCodeProvider(new Dictionary() { { "CompilerVersion", "v4.0" } }))
{
- var options = new CompilerParameters(
- new [] { "System.dll", "System.Core.dll", "System.Xml.dll", "System.Data.Services.Client.dll", Path.Combine(driverFolder, "Microsoft.WindowsAzure.StorageClient.dll") },
- name.CodeBase,
- true);
+#if DEBUG
+ var options = new CompilerParameters(dependencies, name.CodeBase, true);
+#else
+ var options = new CompilerParameters(dependencies, name.CodeBase, false);
+#endif
results = codeProvider.CompileAssemblyFromSource(options, code);
}
+
if (results.Errors.Count > 0)
- throw new Exception
- ("Cannot compile typed context: " + results.Errors[0].ErrorText + " (line " + results.Errors[0].Line + ")");
+ {
+ throw new Exception(string.Format(Exceptions.CannotCompileCode, results.Errors[0].ErrorText, results.Errors[0].Line));
+ }
}
- private static List GetSchema(StorageAccountProperties properties)
+ ///
+ /// Transforms the model based on instances into a schema based on
+ /// instances for LINQPad.
+ ///
+ /// The model.
+ /// A schema for LINQPad.
+ private static List GetSchema(IEnumerable model)
{
- var tableClient = properties.GetStorageAccount().CreateCloudTableClient();
- var dataContext = tableClient.GetDataServiceContext();
- dataContext.ReadingEntity += OnReadingEntity;
-
- return (from tableName in tableClient.ListTables()
- select new ExplorerItem(tableName, ExplorerItemKind.QueryableObject, ExplorerIcon.Table)
+ return (from table in model
+ select new ExplorerItem(table.Name, ExplorerItemKind.QueryableObject, ExplorerIcon.Table)
{
- Children = (from columnName in dataContext.CreateQuery(tableName).Take(1).First().Properties
- select new ExplorerItem(columnName.Key + " (" + columnName.Value + ")", ExplorerItemKind.Property, ExplorerIcon.Column)).ToList()
+ Children = (from column in table.Columns
+ select new ExplorerItem(column.Name + " (" + column.TypeName + ")", ExplorerItemKind.Property, ExplorerIcon.Column)
+ {
+ Icon = keyColumns.Contains(column.Name) ? ExplorerIcon.Key : ExplorerIcon.Column,
+ DragText = column.Name
+ }).ToList(),
+ DragText = table.Name,
+ IsEnumerable = true
}).ToList();
}
- static void OnReadingEntity(object sender, ReadingWritingEntityEventArgs e)
+ ///
+ /// Called when the data services data context has finished trying to deserialize and entity
+ /// from table storage.
+ ///
+ /// The sender.
+ /// The instance containing the event data.
+ private static void OnReadingEntity(object sender, ReadingWritingEntityEventArgs e)
{
- // TODO: Make these statics
- XNamespace AtomNamespace = "http://www.w3.org/2005/Atom";
- XNamespace AstoriaDataNamespace = "http://schemas.microsoft.com/ado/2007/08/dataservices";
- XNamespace AstoriaMetadataNamespace = "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata";
-
GenericEntity entity = e.Entity as GenericEntity;
- if (entity == null)
+ if (null == entity)
{
return;
}
- entity.TableName = e.Data.Element(AtomNamespace + "link").Attribute("title").Value;
+ entity.TableName = e.Data.Element(AtomNS + "link").Attribute("title").Value;
- // read each property, type and value in the payload
- //var properties = e.Entity.GetType().GetProperties();
- //where properties.All(pp => pp.Name != p.Name.LocalName)
- var q = from p in e.Data.Element(AtomNamespace + "content")
- .Element(AstoriaMetadataNamespace + "properties")
- .Elements()
+ var q = from p in e.Data.Element(AtomNS + "content").Element(mNS + "properties").Elements()
select new
{
Name = p.Name.LocalName,
- IsNull = string.Equals("true", p.Attribute(AstoriaMetadataNamespace + "null") == null ? null : p.Attribute(AstoriaMetadataNamespace + "null").Value, StringComparison.OrdinalIgnoreCase),
- TypeName = p.Attribute(AstoriaMetadataNamespace + "type") == null ? "Edm.String" : p.Attribute(AstoriaMetadataNamespace + "type").Value,
+ IsNull = string.Equals("true", p.Attribute(mNS + "null") == null ? null : p.Attribute(mNS + "null").Value, StringComparison.OrdinalIgnoreCase),
+ TypeName = p.Attribute(mNS + "type") == null ? "Edm.String" : p.Attribute(mNS + "type").Value,
p.Value
};
@@ -126,28 +224,33 @@ select new
}
}
- private static Type GetType(string type)
+ ///
+ /// Gets the C# type equivalent of an entity data model (Edm) type.
+ ///
+ /// The Edm type.
+ /// The C# type.
+ private static string GetType(string type)
{
- if (type == null)
- return typeof(string);
-
switch (type)
{
- case "Edm.String": return typeof(string);
- case "Edm.Byte": return typeof(byte);
- case "Edm.SByte": return typeof(sbyte);
- case "Edm.Int16": return typeof(short);
- case "Edm.Int32": return typeof(int);
- case "Edm.Int64": return typeof(long);
- case "Edm.Double": return typeof(double);
- case "Edm.Single": return typeof(float);
- case "Edm.Boolean": return typeof(bool);
- case "Edm.Decimal": return typeof(decimal);
- case "Edm.DateTime": return typeof(DateTime);
- case "Edm.Binary": return typeof(byte[]);
- case "Edm.Guid": return typeof(Guid);
-
- default: throw new NotSupportedException("Not supported type " + type);
+ case "Edm.Binary":
+ return "byte[]";
+ case "Edm.Boolean":
+ return "bool?";
+ case "Edm.DateTime":
+ return "DateTime?";
+ case "Edm.Double":
+ return "double?";
+ case "Edm.Guid":
+ return "Guid?";
+ case "Edm.Int32":
+ return "int?";
+ case "Edm.Int64":
+ return "long?";
+ case "Edm.String":
+ return "string";
+ default:
+ throw new NotSupportedException(string.Format(Exceptions.TypeNotSupported, type));
}
}
}