diff --git a/Madd0.AzureStorageDriver/AzureDriver.cs b/Madd0.AzureStorageDriver/AzureDriver.cs index 88ec53d..011d67d 100644 --- a/Madd0.AzureStorageDriver/AzureDriver.cs +++ b/Madd0.AzureStorageDriver/AzureDriver.cs @@ -58,13 +58,53 @@ public override bool AreRepositoriesEquivalent(IConnectionInfo connection1, ICon return account1.Equals(account2); } + public override IEnumerable GetAssembliesToAdd() + { + return new string[] + { + "System.Data.Services.Client.dll", + "Microsoft.WindowsAzure.StorageClient.dll" + }; + } + + public override IEnumerable GetNamespacesToAdd() + { + return new string[] + { + "System.Data.Services.Client", + "Microsoft.WindowsAzure", + "Microsoft.WindowsAzure.StorageClient" + }; + } + public override List GetSchemaAndBuildAssembly(IConnectionInfo connectionInfo, System.Reflection.AssemblyName assemblyToBuild, ref string nameSpace, ref string typeName) { return SchemaBuilder.GetSchemaAndBuildAssembly( new StorageAccountProperties(connectionInfo), + this.GetDriverFolder(), assemblyToBuild, ref nameSpace, ref typeName); } + + public override object[] GetContextConstructorArguments(IConnectionInfo connectionInfo) + { + var properties = new StorageAccountProperties(connectionInfo); + + return new object[] + { + properties.GetStorageAccount().TableEndpoint.ToString(), + properties.GetStorageAccount().Credentials + }; + } + + public override ParameterDescriptor[] GetContextConstructorParameters(IConnectionInfo connectionInfo) + { + return new[] + { + new ParameterDescriptor("baseAddress", "System.String"), + new ParameterDescriptor("credentials", "Microsoft.WindowsAzure.StorageCredentials") + }; + } } } diff --git a/Madd0.AzureStorageDriver/DevDeploy.bat b/Madd0.AzureStorageDriver/DevDeploy.bat index c6af2b1..6c14fd6 100644 --- a/Madd0.AzureStorageDriver/DevDeploy.bat +++ b/Madd0.AzureStorageDriver/DevDeploy.bat @@ -7,3 +7,4 @@ rem xcopy /i/y Madd0.AzureStorageDriver.dll "%AllUsersProfile%\LINQPad\Drivers\DataContext\4.0\Madd0.AzureStorageDriver (47842961fb3025d7)\" xcopy /i/y Madd0.AzureStorageDriver.pdb "%AllUsersProfile%\LINQPad\Drivers\DataContext\4.0\Madd0.AzureStorageDriver (47842961fb3025d7)\" +xcopy /i/y Microsoft.WindowsAzure.StorageClient.dll "%AllUsersProfile%\LINQPad\Drivers\DataContext\4.0\Madd0.AzureStorageDriver (47842961fb3025d7)\" diff --git a/Madd0.AzureStorageDriver/GenericEntity.cs b/Madd0.AzureStorageDriver/GenericEntity.cs new file mode 100644 index 0000000..ae37d2f --- /dev/null +++ b/Madd0.AzureStorageDriver/GenericEntity.cs @@ -0,0 +1,47 @@ +//----------------------------------------------------------------------- +// +// 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 4790c6b..805463b 100644 --- a/Madd0.AzureStorageDriver/Madd0.AzureStorageDriver.csproj +++ b/Madd0.AzureStorageDriver/Madd0.AzureStorageDriver.csproj @@ -46,6 +46,7 @@ + @@ -55,9 +56,11 @@ + ConnectionDialog.xaml + True diff --git a/Madd0.AzureStorageDriver/SchemaBuilder.cs b/Madd0.AzureStorageDriver/SchemaBuilder.cs index c40a2f7..2b4c153 100644 --- a/Madd0.AzureStorageDriver/SchemaBuilder.cs +++ b/Madd0.AzureStorageDriver/SchemaBuilder.cs @@ -13,14 +13,22 @@ namespace Madd0.AzureStorageDriver using LINQPad.Extensibility.DataContext; using System.Reflection; using System.Xml.Linq; + using Microsoft.WindowsAzure; + using Microsoft.WindowsAzure.StorageClient; + using System.Data.Services.Client; + using System.CodeDom.Compiler; + using Microsoft.CSharp; + using System.IO; /// /// TODO: Provide summary section in the documentation header. /// internal static class SchemaBuilder { - public static List GetSchemaAndBuildAssembly(StorageAccountProperties properties, AssemblyName name, ref string nameSpace, ref string typeName) + + public static List GetSchemaAndBuildAssembly(StorageAccountProperties properties, string driverFolder, AssemblyName name, ref string nameSpace, ref string typeName) { + // Read the EDM schema into an XDocument: //XDocument data; //using (XmlReader reader = GetSchemaReader(props)) @@ -32,23 +40,115 @@ public static List GetSchemaAndBuildAssembly(StorageAccountPropert // code = GenerateCode(reader, nameSpace); // Compile the code into the assembly, using the assembly name provided: - //BuildAssembly(code, name); + BuildAssembly(name, driverFolder, typeName, nameSpace); // Use the schema to populate the Schema Explorer: //List schema = GetSchema(data, out typeName); - List schema = GetSchema(null, out typeName); + List schema = GetSchema(properties); return schema; } - private static List GetSchema(XDocument data, out string typeName) + private static void BuildAssembly(AssemblyName name, string driverFolder, string typeName, string nameSpace) + { + var code = @"namespace " + nameSpace + @" +{ + public class " + typeName + @" : Microsoft.WindowsAzure.StorageClient.TableServiceContext + { + public " + typeName + @"(string baseAddress, Microsoft.WindowsAzure.StorageCredentials credentials) + : base(baseAddress, credentials) { - typeName = "TypeName"; - return new List() + } + } +}"; + CompilerResults results; + using (var codeProvider = new CSharpCodeProvider(new Dictionary() { { "CompilerVersion", "v4.0" } })) { - new ExplorerItem("TestItem", ExplorerItemKind.Category, ExplorerIcon.Table) - }; + 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); + 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 + ")"); + } + + private static List GetSchema(StorageAccountProperties properties) + { + 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) + { + Children = (from columnName in dataContext.CreateQuery(tableName).Take(1).First().Properties + select new ExplorerItem(columnName.Key + " (" + columnName.Value + ")", ExplorerItemKind.Property, ExplorerIcon.Column)).ToList() + }).ToList(); + } + + 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) + { + return; + } + + entity.TableName = e.Data.Element(AtomNamespace + "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() + 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, + p.Value + }; + + foreach (var dp in q) + { + entity[dp.Name] = dp.TypeName; + } + } + + private static Type 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); + } } } } diff --git a/Madd0.AzureStorageDriver/StorageAccountProperties.cs b/Madd0.AzureStorageDriver/StorageAccountProperties.cs index 2c79118..175ba95 100644 --- a/Madd0.AzureStorageDriver/StorageAccountProperties.cs +++ b/Madd0.AzureStorageDriver/StorageAccountProperties.cs @@ -13,6 +13,7 @@ namespace Madd0.AzureStorageDriver using LINQPad.Extensibility.DataContext; using System.Xml.Linq; using Madd0.AzureStorageDriver.Properties; + using Microsoft.WindowsAzure; /// /// Wrapper to expose typed properties over ConnectionInfo.DriverData. @@ -68,6 +69,20 @@ public bool UseLocalStorage } } + public bool UseHttps + { + get + { + var currentValue = (string)this._driverData.Element("UseHttps") ?? string.Empty; + return Convert.ToBoolean(currentValue); + } + + set + { + this._driverData.SetElementValue("UseHttps", value); + } + } + public string AccountName { get { return (string)this._driverData.Element("AccountName") ?? string.Empty; } @@ -104,5 +119,17 @@ 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); + } + } } }