├── .gitignore ├── .nuget ├── NuGet.Config └── NuGet.targets ├── GDataDB.Tests ├── DummyComparer.cs ├── DummyTable.cs ├── Entity.cs ├── GDataDB.Tests.csproj ├── IntegrationEntity.cs ├── IntegrationTests.cs ├── JWTTests.cs ├── LinqTranslatorTests.cs ├── Properties │ └── AssemblyInfo.cs ├── app.config └── packages.config ├── GDataDB.nuspec ├── GDataDB.sln ├── GDataDB ├── DatabaseClient.cs ├── GDataDB.csproj ├── IDatabase.cs ├── IDatabaseClient.cs ├── IRow.cs ├── ITable.cs ├── Impl │ ├── Database.cs │ ├── GDataDBRequestFactory.cs │ ├── Lazy.cs │ ├── OAuth2Token.cs │ ├── Row.cs │ ├── Serializer.cs │ ├── Table.cs │ └── Utils.cs ├── Linq │ ├── ITableExtensions.cs │ └── Impl │ │ ├── Evaluator.cs │ │ ├── ExpressionVisitor.cs │ │ ├── GDataDBQueryProvider.cs │ │ ├── OrderTranslator.cs │ │ ├── Query.cs │ │ ├── QueryProvider.cs │ │ ├── QueryTranslator.cs │ │ ├── TypeSystem.cs │ │ └── WhereTranslator.cs ├── Order.cs ├── Properties │ └── AssemblyInfo.cs ├── Query.cs ├── app.config └── packages.config ├── README.md ├── Sample ├── Entity.cs ├── Program.cs ├── Properties │ └── AssemblyInfo.cs ├── Sample.csproj └── app.config ├── ilmerge.exclude ├── license.txt ├── packages └── repositories.config └── release.bat /.gitignore: -------------------------------------------------------------------------------- 1 | *.suo 2 | *.user 3 | *.cache 4 | _ReSharper.* 5 | bin/* 6 | obj/* 7 | */bin/* 8 | */obj/* 9 | Build 10 | packages/* 11 | .nuget/*.exe 12 | *.nupkg -------------------------------------------------------------------------------- /.nuget/NuGet.Config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.nuget/NuGet.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(MSBuildProjectDirectory)\..\ 5 | 6 | 7 | false 8 | 9 | 10 | false 11 | 12 | 13 | true 14 | 15 | 16 | true 17 | 18 | 19 | 20 | 21 | 22 | 26 | 27 | 28 | 29 | 30 | $([System.IO.Path]::Combine($(SolutionDir), ".nuget")) 31 | 32 | 33 | 34 | 35 | $(SolutionDir).nuget 36 | 37 | 38 | 39 | $(MSBuildProjectDirectory)\packages.$(MSBuildProjectName.Replace(' ', '_')).config 40 | $(MSBuildProjectDirectory)\packages.$(MSBuildProjectName).config 41 | 42 | 43 | 44 | $(MSBuildProjectDirectory)\packages.config 45 | $(PackagesProjectConfig) 46 | 47 | 48 | 49 | 50 | $(NuGetToolsPath)\NuGet.exe 51 | @(PackageSource) 52 | 53 | "$(NuGetExePath)" 54 | mono --runtime=v4.0.30319 "$(NuGetExePath)" 55 | 56 | $(TargetDir.Trim('\\')) 57 | 58 | -RequireConsent 59 | -NonInteractive 60 | 61 | "$(SolutionDir) " 62 | "$(SolutionDir)" 63 | 64 | 65 | $(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(NonInteractiveSwitch) $(RequireConsentSwitch) -solutionDir $(PaddedSolutionDir) 66 | $(NuGetCommand) pack "$(ProjectPath)" -Properties "Configuration=$(Configuration);Platform=$(Platform)" $(NonInteractiveSwitch) -OutputDirectory "$(PackageOutputDir)" -symbols 67 | 68 | 69 | 70 | RestorePackages; 71 | $(BuildDependsOn); 72 | 73 | 74 | 75 | 76 | $(BuildDependsOn); 77 | BuildPackage; 78 | 79 | 80 | 81 | 82 | 83 | 84 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 99 | 100 | 103 | 104 | 105 | 106 | 108 | 109 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 141 | 142 | 143 | 144 | 145 | -------------------------------------------------------------------------------- /GDataDB.Tests/DummyComparer.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace GDataDB.Tests { 4 | public class DummyComparer: IComparer { 5 | public int Compare(T x, T y) { 6 | throw new System.NotImplementedException(); 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /GDataDB.Tests/DummyTable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace GDataDB.Tests { 5 | public class DummyTable : ITable { 6 | public void Delete() { 7 | throw new NotImplementedException(); 8 | } 9 | 10 | public void Clear() { 11 | throw new NotImplementedException(); 12 | } 13 | 14 | public void Rename(string newName) { 15 | throw new NotImplementedException(); 16 | } 17 | 18 | public IRow Add(Entity e) { 19 | throw new NotImplementedException(); 20 | } 21 | 22 | public IRow Get(int rowNumber) { 23 | throw new NotImplementedException(); 24 | } 25 | 26 | public IList> FindAll() { 27 | throw new NotImplementedException(); 28 | } 29 | 30 | public IList> FindAll(int start, int count) { 31 | throw new NotImplementedException(); 32 | } 33 | 34 | public IList> Find(string query) { 35 | throw new NotImplementedException(); 36 | } 37 | 38 | public IList> FindStructured(string query) { 39 | Console.WriteLine("FindStructured {0}", query); 40 | return new List>(); 41 | } 42 | 43 | public IList> FindStructured(string query, int start, int count) { 44 | Console.WriteLine("FindStructured {0} start {1} count {2}", query, start, count); 45 | return new List>(); 46 | } 47 | 48 | public IList> Find(Query q) { 49 | Console.WriteLine("Find Query"); 50 | return new List>(); 51 | } 52 | 53 | public Uri GetFeedUrl() { 54 | throw new NotImplementedException(); 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /GDataDB.Tests/Entity.cs: -------------------------------------------------------------------------------- 1 | namespace GDataDB.Tests { 2 | public class Entity { 3 | public string Description { get; set; } 4 | public int Quantity { get; set; } 5 | } 6 | } -------------------------------------------------------------------------------- /GDataDB.Tests/GDataDB.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | 9.0.21022 7 | 2.0 8 | {5B1A7187-CC83-4677-9CEE-55727F62AC82} 9 | Library 10 | Properties 11 | GDataDB.Tests 12 | GDataDB.Tests 13 | v3.5 14 | 512 15 | 16 | 17 | 3.5 18 | 19 | ..\ 20 | true 21 | 22 | 23 | true 24 | full 25 | false 26 | bin\Debug\ 27 | DEBUG;TRACE 28 | prompt 29 | 4 30 | 31 | 32 | pdbonly 33 | true 34 | bin\Release\ 35 | TRACE 36 | prompt 37 | 4 38 | 39 | 40 | 41 | ..\packages\Newtonsoft.Json.7.0.1\lib\net35\Newtonsoft.Json.dll 42 | True 43 | 44 | 45 | False 46 | ..\packages\NUnit.2.6.3\lib\nunit.framework.dll 47 | 48 | 49 | 50 | 3.5 51 | 52 | 53 | 3.5 54 | 55 | 56 | 3.5 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | {9B543220-CB1D-414D-9787-2DDC08F0EBD1} 74 | GDataDB 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 86 | 87 | 88 | 89 | 96 | -------------------------------------------------------------------------------- /GDataDB.Tests/IntegrationEntity.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace GDataDB.Tests { 4 | public class IntegrationEntity { 5 | public int IntProp { get; set; } 6 | public int? IntNullProp { get; set; } 7 | public string StringProp { get; set; } 8 | public DateTime DateTimeProp { get; set; } 9 | public DateTime? DateTimeNullProp { get; set; } 10 | public float FloatProp { get; set; } 11 | public float? FloatNullProp { get; set; } 12 | public double DoubleProp { get; set; } 13 | public double? DoubleNullProp { get; set; } 14 | public decimal DecimalProp { get; set; } 15 | public decimal? DecimalNullProp { get; set; } 16 | } 17 | } -------------------------------------------------------------------------------- /GDataDB.Tests/IntegrationTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Linq; 4 | using GDataDB.Linq; 5 | using NUnit.Framework; 6 | 7 | namespace GDataDB.Tests { 8 | [TestFixture] 9 | public class IntegrationTests { 10 | private ITable table; 11 | private IDatabase db; 12 | 13 | private readonly IntegrationEntity e1 = new IntegrationEntity { 14 | DateTimeProp = new DateTime(2001, 1, 1, 5, 6, 7), 15 | StringProp = "ñãá", 16 | IntProp = 1, 17 | }; 18 | 19 | private readonly IntegrationEntity e2 = new IntegrationEntity { 20 | DateTimeProp = new DateTime(2005, 6, 7, 10, 6, 7), 21 | IntProp = 1000, 22 | }; 23 | 24 | 25 | [TestFixtureSetUp] 26 | public void FixtureSetup() { 27 | Console.WriteLine("Connecting"); 28 | var client = new DatabaseClient( 29 | clientEmail: "xxx@developer.gserviceaccount.com", 30 | privateKey: File.ReadAllBytes(@"xxx.p12")); 31 | 32 | const string dbName = "IntegrationTests"; 33 | Console.WriteLine("Opening or creating database"); 34 | db = client.GetDatabase(dbName) ?? client.CreateDatabase(dbName); 35 | const string tableName = "IntegrationTests"; 36 | Console.WriteLine("Opening or creating table"); 37 | table = db.GetTable(tableName) ?? db.CreateTable(tableName); 38 | } 39 | 40 | [SetUp] 41 | public void setup() { 42 | table.Clear(); 43 | table.Add(e1); 44 | table.Add(e2); 45 | } 46 | 47 | [TestFixtureTearDown] 48 | public void FixtureTearDown() { 49 | //table.Delete(); 50 | //db.Delete(); 51 | } 52 | 53 | [Test] 54 | public void Rename() { 55 | table.Rename("something"); 56 | table.Rename("IntegrationTests"); 57 | } 58 | 59 | [Test] 60 | public void Add() { 61 | table.Add(new IntegrationEntity { 62 | FloatProp = 123.45f, 63 | DecimalProp = 234.56m, 64 | DoubleProp = 333.44, 65 | }); 66 | } 67 | 68 | [Test] 69 | public void LINQ_orderby_int() { 70 | var q = from r in table.AsQueryable() 71 | orderby r.IntProp 72 | select r; 73 | 74 | var l = q.ToList(); 75 | Assert.AreEqual(2, l.Count); 76 | Assert.AreEqual(e1.IntProp, l[0].IntProp); 77 | Assert.AreEqual(e2.IntProp, l[1].IntProp); 78 | } 79 | 80 | [Test] 81 | public void LINQ_orderby_int_descending() { 82 | var q = from r in table.AsQueryable() 83 | orderby r.IntProp descending 84 | select r; 85 | 86 | var l = q.ToList(); 87 | Assert.AreEqual(2, l.Count); 88 | Assert.AreEqual(e2.IntProp, l[0].IntProp); 89 | Assert.AreEqual(e1.IntProp, l[1].IntProp); 90 | } 91 | 92 | [Test] 93 | public void LINQ_orderby_datetime() { 94 | var q = from r in table.AsQueryable() 95 | orderby r.DateTimeProp 96 | select r; 97 | 98 | var l = q.ToList(); 99 | Assert.AreEqual(2, l.Count); 100 | Assert.AreEqual(e1.DateTimeProp, l[0].DateTimeProp); 101 | Assert.AreEqual(e2.DateTimeProp, l[1].DateTimeProp); 102 | } 103 | 104 | [Test] 105 | public void LINQ_orderby_datetime_descending() { 106 | var q = from r in table.AsQueryable() 107 | orderby r.DateTimeProp descending 108 | select r; 109 | 110 | var l = q.ToList(); 111 | Assert.AreEqual(2, l.Count); 112 | Assert.AreEqual(e2.DateTimeProp, l[0].DateTimeProp); 113 | Assert.AreEqual(e1.DateTimeProp, l[1].DateTimeProp); 114 | } 115 | 116 | [Test] 117 | public void LINQ_orderby_datetime_descending_take() { 118 | var q = table.AsQueryable() 119 | .OrderByDescending(r => r.DateTimeProp) 120 | .Take(1) 121 | .ToList(); 122 | 123 | Assert.AreEqual(1, q.Count); 124 | Assert.AreEqual(e2.DateTimeProp, q[0].DateTimeProp); 125 | } 126 | 127 | [Test] 128 | public void LINQ_orderby_datetime_descending_take_skip() { 129 | var q = table.AsQueryable() 130 | .OrderByDescending(r => r.DateTimeProp) 131 | .Skip(1) 132 | .Take(1) 133 | .ToList(); 134 | 135 | Assert.AreEqual(1, q.Count); 136 | Assert.AreEqual(e1.DateTimeProp, q[0].DateTimeProp); 137 | } 138 | } 139 | } -------------------------------------------------------------------------------- /GDataDB.Tests/JWTTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | using System.IO; 4 | using GDataDB.Impl; 5 | 6 | namespace GDataDB.Tests { 7 | [TestFixture] 8 | public class JWTTests { 9 | private static readonly GDataDBRequestFactory client = 10 | new GDataDBRequestFactory( 11 | clientEmail: "315184672897-s1q9gb7bghm39f27d655fb8iqo27lv1o@developer.gserviceaccount.com", 12 | privateKey: File.ReadAllBytes(@"g:\Users\mausch\Downloads\gdatadb-test-cfa565a0a063.p12")); 13 | 14 | [Test] 15 | public void GetJWT() { 16 | var jwt = client.GetJWT(); 17 | Console.WriteLine(jwt); 18 | } 19 | 20 | [Test] 21 | public void GetAuthToken() { 22 | var token = client.GetToken(); 23 | Console.WriteLine(token); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /GDataDB.Tests/LinqTranslatorTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using GDataDB.Linq.Impl; 4 | using NUnit.Framework; 5 | 6 | namespace GDataDB.Tests { 7 | [TestFixture] 8 | public class LinqTranslatorTests { 9 | private IQueryable q; 10 | 11 | [TestFixtureSetUp] 12 | public void FixtureSetup() { 13 | var t = new DummyTable(); 14 | q = new Query(new GDataDBQueryProvider(t)); 15 | } 16 | 17 | [Test] 18 | public void QueryTranslator() { 19 | var iq = q.Where(e => e.Description == "pepe"); 20 | Assert.AreEqual("(description=\"pepe\")", iq.ToString()); 21 | } 22 | 23 | [Test] 24 | public void GreaterThanInt() { 25 | var iq = q.Where(e => e.Quantity > 5); 26 | Assert.AreEqual("(quantity>5)", iq.ToString()); 27 | } 28 | 29 | [Test] 30 | public void LessThanInt() { 31 | var iq = q.Where(e => e.Quantity < 5); 32 | Assert.AreEqual("(quantity<5)", iq.ToString()); 33 | } 34 | 35 | [Test] 36 | public void GreaterThanOrEqualInt() { 37 | var iq = q.Where(e => e.Quantity >= 5); 38 | Assert.AreEqual("(((quantity>5)||(quantity=5)))", iq.ToString()); 39 | } 40 | 41 | [Test] 42 | public void LessThanOrEqualInt() { 43 | var iq = q.Where(e => e.Quantity <= 5); 44 | Assert.AreEqual("(((quantity<5)||(quantity=5)))", iq.ToString()); 45 | } 46 | 47 | [Test] 48 | public void Equals() { 49 | var iq = q.Where(e => e.Quantity == 5); 50 | Assert.AreEqual("(quantity=5)", iq.ToString()); 51 | } 52 | 53 | [Test] 54 | public void NotEquals() { 55 | var iq = q.Where(e => e.Quantity != 5); 56 | Assert.AreEqual("(quantity!=5)", iq.ToString()); 57 | } 58 | 59 | [Test] 60 | public void Or() { 61 | var iq = q.Where(e => e.Quantity > 5 || e.Quantity < 5); 62 | Assert.AreEqual("((quantity>5)||(quantity<5))", iq.ToString()); 63 | } 64 | 65 | [Test] 66 | public void And() { 67 | var iq = q.Where(e => e.Quantity > 5 && e.Quantity < 5); 68 | Assert.AreEqual("((quantity>5)&&(quantity<5))", iq.ToString()); 69 | } 70 | 71 | [Test] 72 | public void OrderBy() { 73 | var iq = (Query) q.OrderBy(e => e.Quantity); 74 | var sq = iq.ToQuery(); 75 | Assert.IsNotNull(sq.Order); 76 | Assert.IsFalse(sq.Order.Descending); 77 | Assert.AreEqual("quantity", sq.Order.ColumnName); 78 | } 79 | 80 | [Test] 81 | public void OrderByDescending() { 82 | var iq = (Query) q.OrderByDescending(e => e.Quantity); 83 | var sq = iq.ToQuery(); 84 | Assert.IsNotNull(sq.Order); 85 | Assert.IsTrue(sq.Order.Descending); 86 | Assert.AreEqual("quantity", sq.Order.ColumnName); 87 | } 88 | 89 | [Test] 90 | [ExpectedException(typeof (NotSupportedException))] 91 | public void OrderBy_With_comparer_is_not_supported() { 92 | var iq = (Query) q.OrderBy(e => e.Quantity, new DummyComparer()); 93 | var sq = iq.ToQuery(); 94 | Assert.IsNotNull(sq.Order); 95 | Assert.IsFalse(sq.Order.Descending); 96 | Assert.AreEqual("quantity", sq.Order.ColumnName); 97 | } 98 | 99 | [Test] 100 | public void Where_and_OrderBy() { 101 | var iq = (Query) q 102 | .Where(e => e.Quantity > 5) 103 | .OrderBy(e => e.Quantity); 104 | var sq = iq.ToQuery(); 105 | Assert.IsNotNull(sq.Order); 106 | Assert.IsNotNull(sq.StructuredQuery); 107 | Assert.AreEqual("(quantity>5)", sq.StructuredQuery); 108 | Assert.AreEqual("quantity", sq.Order.ColumnName); 109 | } 110 | 111 | [Test] 112 | public void Take() { 113 | var iq = (Query) q 114 | .Where(e => e.Quantity > 5) 115 | .Take(5); 116 | var sq = iq.ToQuery(); 117 | Assert.AreEqual(5, sq.Count); 118 | } 119 | 120 | [Test] 121 | public void Take_Expression() { 122 | var iq = (Query)q 123 | .Where(e => e.Quantity > 5) 124 | .Take(5+5); 125 | var sq = iq.ToQuery(); 126 | Assert.AreEqual(10, sq.Count); 127 | Assert.AreEqual("(quantity>5)", sq.StructuredQuery); 128 | } 129 | 130 | [Test] 131 | public void Skip() { 132 | var iq = (Query)q 133 | .Where(e => e.Quantity > 5) 134 | .Skip(10) 135 | .Take(2); 136 | var sq = iq.ToQuery(); 137 | Assert.AreEqual(10, sq.Start); 138 | Assert.AreEqual(2, sq.Count); 139 | Assert.AreEqual("(quantity>5)", sq.StructuredQuery); 140 | } 141 | } 142 | } -------------------------------------------------------------------------------- /GDataDB.Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("GDataDB.Tests")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("GDataDB.Tests")] 13 | [assembly: AssemblyCopyright("Copyright © 2008")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("7f78d4c6-b367-441c-9758-ec3d6576d403")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /GDataDB.Tests/app.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /GDataDB.Tests/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /GDataDB.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | GDataDB 5 | 0.6.1.0 6 | Mauricio Scheffer 7 | Mauricio Scheffer 8 | http://github.com/mausch/GDataDB/raw/master/license.txt 9 | http://github.com/mausch/GDataDB 10 | false 11 | A database-like interface to Google Spreadsheets for .Net 12 | A database-like interface to Google Spreadsheets for .Net 13 | en-US 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /GDataDB.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.23107.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{1A24D144-3814-4684-AD96-038CEBD71207}" 7 | ProjectSection(SolutionItems) = preProject 8 | GDataDB.proj = GDataDB.proj 9 | EndProjectSection 10 | EndProject 11 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GDataDB", "GDataDB\GDataDB.csproj", "{9B543220-CB1D-414D-9787-2DDC08F0EBD1}" 12 | EndProject 13 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample", "Sample\Sample.csproj", "{D3FDBE0F-E64E-4742-9F7E-0B77ACFE6380}" 14 | EndProject 15 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GDataDB.Tests", "GDataDB.Tests\GDataDB.Tests.csproj", "{5B1A7187-CC83-4677-9CEE-55727F62AC82}" 16 | EndProject 17 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{731AB9A4-6377-41FB-A36F-2C1995E5E019}" 18 | ProjectSection(SolutionItems) = preProject 19 | .nuget\NuGet.Config = .nuget\NuGet.Config 20 | .nuget\NuGet.exe = .nuget\NuGet.exe 21 | .nuget\NuGet.targets = .nuget\NuGet.targets 22 | EndProjectSection 23 | EndProject 24 | Global 25 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 26 | Debug|Any CPU = Debug|Any CPU 27 | Release|Any CPU = Release|Any CPU 28 | EndGlobalSection 29 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 30 | {9B543220-CB1D-414D-9787-2DDC08F0EBD1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 31 | {9B543220-CB1D-414D-9787-2DDC08F0EBD1}.Debug|Any CPU.Build.0 = Debug|Any CPU 32 | {9B543220-CB1D-414D-9787-2DDC08F0EBD1}.Release|Any CPU.ActiveCfg = Release|Any CPU 33 | {9B543220-CB1D-414D-9787-2DDC08F0EBD1}.Release|Any CPU.Build.0 = Release|Any CPU 34 | {D3FDBE0F-E64E-4742-9F7E-0B77ACFE6380}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 35 | {D3FDBE0F-E64E-4742-9F7E-0B77ACFE6380}.Debug|Any CPU.Build.0 = Debug|Any CPU 36 | {D3FDBE0F-E64E-4742-9F7E-0B77ACFE6380}.Release|Any CPU.ActiveCfg = Release|Any CPU 37 | {D3FDBE0F-E64E-4742-9F7E-0B77ACFE6380}.Release|Any CPU.Build.0 = Release|Any CPU 38 | {5B1A7187-CC83-4677-9CEE-55727F62AC82}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 39 | {5B1A7187-CC83-4677-9CEE-55727F62AC82}.Debug|Any CPU.Build.0 = Debug|Any CPU 40 | {5B1A7187-CC83-4677-9CEE-55727F62AC82}.Release|Any CPU.ActiveCfg = Release|Any CPU 41 | {5B1A7187-CC83-4677-9CEE-55727F62AC82}.Release|Any CPU.Build.0 = Release|Any CPU 42 | EndGlobalSection 43 | GlobalSection(SolutionProperties) = preSolution 44 | HideSolutionNode = FALSE 45 | EndGlobalSection 46 | EndGlobal 47 | -------------------------------------------------------------------------------- /GDataDB/DatabaseClient.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net; 5 | using System.Text; 6 | using System.Xml.Linq; 7 | using GDataDB.Impl; 8 | using Newtonsoft.Json; 9 | 10 | namespace GDataDB { 11 | public class DatabaseClient : IDatabaseClient { 12 | 13 | public readonly GDataDBRequestFactory RequestFactory; 14 | 15 | public DatabaseClient(string clientEmail, byte[] privateKey) { 16 | if (clientEmail == null) 17 | throw new ArgumentNullException("clientEmail"); 18 | if (privateKey == null) 19 | throw new ArgumentNullException("privateKey"); 20 | 21 | RequestFactory = new GDataDBRequestFactory(clientEmail, privateKey); 22 | } 23 | 24 | private static string CreateDatabaseContent(string boundary, string name) { 25 | var data = new StringBuilder(); 26 | data.AppendLine("--" + boundary); 27 | data.AppendLine("Content-Type: application/json; charset=UTF-8"); 28 | data.AppendLine(JsonConvert.SerializeObject(new { 29 | title = name, 30 | fileExtension = "csv", 31 | parents = new[] { 32 | new { 33 | kind = "drive#fileLink", 34 | id = "", 35 | }, 36 | } 37 | })); 38 | data.AppendLine(); 39 | data.AppendLine("--" + boundary); 40 | data.AppendLine("Content-Type: text/csv"); 41 | data.AppendLine(); 42 | data.AppendLine(",,,,,,,,,,,,,,,"); 43 | data.AppendLine("--" + boundary + "--"); 44 | return data.ToString(); 45 | } 46 | 47 | public IDatabase CreateDatabase(string name) { 48 | var http = RequestFactory.CreateRequest(); 49 | var boundary = Guid.NewGuid().ToString(); 50 | http.Headers.Set("Content-Type", string.Format("multipart/related; boundary=\"{0}\"", boundary)); 51 | var data = CreateDatabaseContent(boundary: boundary, name: name); 52 | var response = http.UploadString("https://content.googleapis.com/upload/drive/v2/files?uploadType=multipart&convert=true", data: data); 53 | 54 | var jsonResponse = JsonConvert.DeserializeObject>(response); 55 | 56 | var docId = (string)jsonResponse["id"]; 57 | var worksheetFeedUri = new Uri(string.Format("https://spreadsheets.google.com/feeds/worksheets/{0}/private/full", docId)); 58 | return new Database(this, docId, worksheetFeedUri); 59 | } 60 | 61 | public IDatabase GetDatabase(string name) { 62 | 63 | var uri = "https://spreadsheets.google.com/feeds/spreadsheets/private/full?title-exact=true&title=" + Utils.UrlEncode(name); 64 | 65 | var http = RequestFactory.CreateRequest(); 66 | var rawResponse = http.DownloadString(uri); 67 | var xmlResponse = XDocument.Parse(rawResponse); 68 | 69 | var feedUri = ExtractEntryContent(xmlResponse); 70 | 71 | if (feedUri == null) 72 | return null; 73 | 74 | var id = feedUri.Segments.Reverse().Skip(2).First(); 75 | return new Database(this, id, feedUri); 76 | } 77 | 78 | public static Uri ExtractEntryContent(XDocument xdoc) { 79 | return ExtractEntryContent(xdoc.Root.Elements(Utils.AtomNs + "entry")); 80 | } 81 | 82 | public static Uri ExtractEntryContent(IEnumerable entries) { 83 | return entries 84 | .SelectMany(e => e.Elements(Utils.AtomNs + "content")) 85 | .SelectMany(e => e.Attributes("src")) 86 | .Select(a => new Uri(a.Value)) 87 | .FirstOrDefault(); 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /GDataDB/GDataDB.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | 9.0.30729 7 | 2.0 8 | {9B543220-CB1D-414D-9787-2DDC08F0EBD1} 9 | Library 10 | Properties 11 | GDataDB 12 | GDataDB 13 | v3.5 14 | 512 15 | 16 | 17 | 3.5 18 | 19 | ..\ 20 | true 21 | 22 | 23 | 24 | true 25 | full 26 | false 27 | bin\Debug\ 28 | DEBUG;TRACE 29 | prompt 30 | 4 31 | bin\Debug\GDataDB.XML 32 | 33 | 34 | pdbonly 35 | true 36 | bin\Release\ 37 | TRACE 38 | prompt 39 | 4 40 | bin\Release\GDataDB.XML 41 | 42 | 43 | 44 | ..\packages\Newtonsoft.Json.7.0.1\lib\net35\Newtonsoft.Json.dll 45 | True 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 89 | 90 | 91 | 92 | 99 | -------------------------------------------------------------------------------- /GDataDB/IDatabase.cs: -------------------------------------------------------------------------------- 1 | namespace GDataDB { 2 | /// 3 | /// Spreadsheet document 4 | /// 5 | public interface IDatabase { 6 | /// 7 | /// Creates a new worksheet in this document 8 | /// 9 | /// 10 | /// 11 | /// 12 | ITable CreateTable(string name) where T: new(); 13 | 14 | /// 15 | /// Gets an existing worksheet in this document. 16 | /// 17 | /// 18 | /// 19 | /// Searched worksheet or null if not found 20 | ITable GetTable(string name) where T : new(); 21 | 22 | /// 23 | /// Deletes this spreadsheet document 24 | /// 25 | void Delete(); 26 | } 27 | } -------------------------------------------------------------------------------- /GDataDB/IDatabaseClient.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace GDataDB { 3 | /// 4 | /// Google spreadsheet service entry point 5 | /// 6 | public interface IDatabaseClient { 7 | 8 | /// 9 | /// Creates a new (spreadsheet document) 10 | /// 11 | /// 12 | /// 13 | IDatabase CreateDatabase(string name); 14 | 15 | /// 16 | /// Gets an existing (spreadsheet document) 17 | /// 18 | /// 19 | /// IDocument instance or null if not found 20 | IDatabase GetDatabase(string name); 21 | } 22 | } -------------------------------------------------------------------------------- /GDataDB/IRow.cs: -------------------------------------------------------------------------------- 1 | namespace GDataDB { 2 | /// 3 | /// Row in the spreadsheet 4 | /// 5 | /// 6 | public interface IRow { 7 | /// 8 | /// Element stored in the row 9 | /// 10 | T Element { get; set; } 11 | 12 | /// 13 | /// Updates the row in the spreadsheet using the current 14 | /// 15 | void Update(); 16 | 17 | /// 18 | /// Deletes this row 19 | /// 20 | void Delete(); 21 | } 22 | } -------------------------------------------------------------------------------- /GDataDB/ITable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace GDataDB { 5 | /// 6 | /// Worksheet in a spreadsheet document 7 | /// 8 | /// 9 | public interface ITable { 10 | /// 11 | /// Deletes this worksheet 12 | /// 13 | void Delete(); 14 | 15 | /// 16 | /// Deletes all rows in this worksheet. 17 | /// This operation is not atomic. 18 | /// 19 | void Clear(); 20 | 21 | /// 22 | /// Renames this worksheet 23 | /// 24 | /// 25 | void Rename(string newName); 26 | 27 | /// 28 | /// Adds a new row 29 | /// 30 | /// Object to store 31 | /// Row stored 32 | IRow Add(T e); 33 | 34 | /// 35 | /// Gets a row by index number 36 | /// 37 | /// 38 | /// 39 | IRow Get(int rowNumber); 40 | 41 | /// 42 | /// Gets all stored rows in this worksheet 43 | /// 44 | /// 45 | IList> FindAll(); 46 | 47 | /// 48 | /// Gets all stored rows in this worksheet, paged 49 | /// 50 | /// 51 | /// 52 | /// 53 | IList> FindAll(int start, int count); 54 | 55 | /// 56 | /// Free text row search 57 | /// 58 | /// text to search 59 | /// Matching rows 60 | IList> Find(string query); 61 | 62 | /// 63 | /// Searches rows using a structured query 64 | /// Syntax: http://code.google.com/apis/spreadsheets/data/2.0/reference.html#ListParameters 65 | /// 66 | /// structured query 67 | /// Matching rows 68 | IList> FindStructured(string query); 69 | 70 | /// 71 | /// Searches rows using a structured query, paged 72 | /// Syntax: http://code.google.com/apis/spreadsheets/data/2.0/reference.html#ListParameters 73 | /// 74 | /// 75 | /// 76 | /// 77 | /// Matching rows 78 | IList> FindStructured(string query, int start, int count); 79 | 80 | /// 81 | /// Searches rows 82 | /// 83 | /// query parameters 84 | /// Matching rows 85 | IList> Find(Query q); 86 | } 87 | } -------------------------------------------------------------------------------- /GDataDB/Impl/Database.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using System.Xml.Linq; 6 | 7 | namespace GDataDB.Impl { 8 | public class Database : IDatabase { 9 | private readonly DatabaseClient client; 10 | private readonly string id; 11 | private readonly Uri worksheetFeed; 12 | 13 | public Database(DatabaseClient client, string id, Uri worksheetFeed) { 14 | this.client = client; 15 | this.id = id; 16 | this.worksheetFeed = worksheetFeed; 17 | } 18 | 19 | public ITable CreateTable(string name) where T : new() { 20 | var fields = new Serializer().GetFields(); 21 | 22 | var request = new XDocument( 23 | new XElement(Utils.AtomNs + "entry", 24 | new XElement(Utils.AtomNs + "title", name), 25 | new XElement(Utils.SpreadsheetsNs + "rowCount", 1), 26 | new XElement(Utils.SpreadsheetsNs + "colCount", fields.Count()) 27 | ) 28 | ); 29 | 30 | var http = client.RequestFactory.CreateRequest(); 31 | var response = http.UploadString(worksheetFeed.AbsoluteUri, request.ToString()); 32 | var xmlResponse = XDocument.Parse(response); 33 | 34 | // i.e. https://spreadsheets.google.com/feeds/list/key/worksheetId/private/full 35 | var listFeedUri = DatabaseClient.ExtractEntryContent(new[] { xmlResponse.Root }); 36 | 37 | // i.e. https://spreadsheets.google.com/feeds/worksheets/key/private/full/worksheetId/version 38 | var editUri = xmlResponse.Root 39 | .Elements(Utils.AtomNs + "link") 40 | .Where(e => e.Attribute("rel").Value == "edit") 41 | .Select(e => new Uri(e.Attribute("href").Value)) 42 | .FirstOrDefault(); 43 | 44 | WriteTableHeaders(listFeedUri, fields); 45 | 46 | return new Table(client, listFeedUri: listFeedUri, worksheetUri: editUri); 47 | 48 | } 49 | 50 | private void WriteTableHeaders(Uri listFeedUri, IEnumerable fields) { 51 | var key = listFeedUri.Segments[3]; 52 | key = key.Substring(0, key.Length - 1); 53 | var worksheetId = listFeedUri.Segments[4]; 54 | worksheetId = worksheetId.Substring(0, worksheetId.Length - 1); 55 | 56 | var entries = 57 | from field in fields.Select((p, i) => new { p.Name, Index = i }) 58 | let columnName = ((char)('A' + field.Index)).ToString() // TODO see what happens beyond Z 59 | let column = (field.Index + 1).ToString() 60 | let row = "1" 61 | let cell = columnName + row 62 | let id = string.Format("https://spreadsheets.google.com/feeds/cells/{0}/{1}/private/full/R1C{2}", key, worksheetId, column) 63 | let edit = id + "/1" 64 | select new XElement(Utils.AtomNs + "entry", 65 | new XElement(Utils.BatchNs + "id", cell), 66 | new XElement(Utils.BatchNs + "operation", new XAttribute("type", "update")), 67 | new XElement(Utils.AtomNs + "id", id), 68 | new XElement(Utils.AtomNs + "edit", 69 | new XAttribute("rel", "edit"), 70 | new XAttribute("type", "application/atom+xml"), 71 | new XAttribute("href", edit)), 72 | new XElement(Utils.SpreadsheetsNs + "cell", 73 | new XAttribute("row", 1), 74 | new XAttribute("col", column), 75 | new XAttribute("inputValue", field.Name.ToLowerInvariant()))); 76 | 77 | var feed = new XDocument(new XElement(Utils.AtomNs + "feed", entries)); 78 | 79 | var batchCellUri = string.Format("https://spreadsheets.google.com/feeds/cells/{0}/{1}/private/full/batch", key, worksheetId); 80 | 81 | var http = client.RequestFactory.CreateRequest(); 82 | http.Headers.Add("If-Match", "*"); 83 | http.UploadString(batchCellUri, method: "POST", data: feed.ToString()); 84 | 85 | } 86 | 87 | public ITable GetTable(string name) where T: new() { 88 | 89 | var uri = worksheetFeed + "?title-exact=true&title=" + Utils.UrlEncode(name); 90 | 91 | var http = client.RequestFactory.CreateRequest(); 92 | var rawResponse = http.DownloadString(uri); 93 | var xmlResponse = XDocument.Parse(rawResponse); 94 | var feedUri = DatabaseClient.ExtractEntryContent(xmlResponse); 95 | 96 | if (feedUri == null) 97 | return null; 98 | 99 | var editUri = xmlResponse.Root 100 | .Elements(Utils.AtomNs + "entry") 101 | .SelectMany(e => e.Elements(Utils.AtomNs + "link")) 102 | .Where(e => e.Attribute("rel").Value == "edit") 103 | .Select(e => new Uri(e.Attribute("href").Value)) 104 | .FirstOrDefault(); 105 | 106 | return new Table(client, listFeedUri: feedUri, worksheetUri: editUri); 107 | 108 | } 109 | 110 | public void Delete() { 111 | var http = client.RequestFactory.CreateRequest(); 112 | http.UploadString(new Uri("https://www.googleapis.com/drive/v2/files/" + id), method: "DELETE", data: ""); 113 | } 114 | } 115 | } -------------------------------------------------------------------------------- /GDataDB/Impl/GDataDBRequestFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.Specialized; 4 | using System.Net; 5 | using System.Security.Cryptography; 6 | using System.Security.Cryptography.X509Certificates; 7 | using System.Text; 8 | using Newtonsoft.Json; 9 | 10 | namespace GDataDB.Impl { 11 | public class GDataDBRequestFactory { 12 | private readonly string clientEmail; 13 | private readonly RSACryptoServiceProvider privateKey; 14 | 15 | private Lazy oauthToken; 16 | 17 | public GDataDBRequestFactory(string clientEmail, byte[] privateKey) { 18 | this.clientEmail = clientEmail; 19 | this.privateKey = GetPrivateKey(privateKey); 20 | oauthToken = GetOAuth2Token(); 21 | } 22 | 23 | private Lazy GetOAuth2Token() { 24 | return new Lazy(() => { 25 | var tokenResponse = RequestNewToken(); 26 | var response = JsonConvert.DeserializeObject>(tokenResponse); 27 | var accessToken = (string)response["access_token"]; 28 | var expiresSeconds = (long)response["expires_in"]; 29 | var expiration = DateTime.Now.AddSeconds(expiresSeconds); 30 | return new OAuth2Token(accessToken, expiration); 31 | }); 32 | } 33 | 34 | public string GetToken() { 35 | if (oauthToken.Value.Expiration < DateTime.Now) 36 | oauthToken = GetOAuth2Token(); 37 | return oauthToken.Value.AuthToken; 38 | } 39 | 40 | private static RSACryptoServiceProvider GetPrivateKey(byte[] p12) { 41 | var certificate = new X509Certificate2(p12, "notasecret", X509KeyStorageFlags.Exportable); 42 | var rsa = (RSACryptoServiceProvider)certificate.PrivateKey; 43 | byte[] privateKeyBlob = rsa.ExportCspBlob(true); 44 | var privateKey = new RSACryptoServiceProvider(); 45 | privateKey.ImportCspBlob(privateKeyBlob); 46 | return privateKey; 47 | } 48 | 49 | private string RequestNewToken() { 50 | var http = new WebClient(); 51 | var response = http.UploadValues("https://accounts.google.com/o/oauth2/token", new NameValueCollection { 52 | {"grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer"}, 53 | {"assertion", GetJWT()}, 54 | }); 55 | return Encoding.UTF8.GetString(response); 56 | } 57 | 58 | private static string UrlBase64Encode(string value) { 59 | var bytesValue = Encoding.UTF8.GetBytes(value); 60 | return UrlBase64Encode(bytesValue); 61 | } 62 | 63 | private static string UrlBase64Encode(byte[] bytes) { 64 | return Convert.ToBase64String(bytes) 65 | .Replace("=", String.Empty) 66 | .Replace('+', '-') 67 | .Replace('/', '_'); 68 | } 69 | 70 | private static readonly string serializedHeader = 71 | JsonConvert.SerializeObject(new { 72 | typ = "JWT", 73 | alg = "RS256", 74 | }); 75 | 76 | private static readonly DateTime zeroDate = 77 | new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); 78 | 79 | private static readonly string scope = string.Join(" ", new[] { 80 | "https://www.googleapis.com/auth/drive", 81 | "https://spreadsheets.google.com/feeds", 82 | }); 83 | 84 | public static string GetJWT(string clientEmail, RSACryptoServiceProvider privateKey, DateTime now) { 85 | var payload = new { 86 | scope = scope, 87 | iss = clientEmail, 88 | aud = "https://accounts.google.com/o/oauth2/token", 89 | exp = (int)(now - zeroDate + TimeSpan.FromHours(1)).TotalSeconds, 90 | iat = (int)(now - zeroDate).TotalSeconds, 91 | //sub = "mauricioscheffer@gmail.com", 92 | }; 93 | 94 | string serializedPayload = JsonConvert.SerializeObject(payload); 95 | 96 | using (var hashAlg = new SHA256Managed()) { 97 | hashAlg.Initialize(); 98 | var headerAndPayload = UrlBase64Encode(serializedHeader) + "." + UrlBase64Encode(serializedPayload); 99 | var headerPayloadBytes = Encoding.ASCII.GetBytes(headerAndPayload); 100 | var signature = UrlBase64Encode(privateKey.SignData(headerPayloadBytes, hashAlg)); 101 | return headerAndPayload + "." + signature; 102 | } 103 | 104 | } 105 | 106 | public string GetJWT() { 107 | return GetJWT(clientEmail, privateKey, DateTime.UtcNow); 108 | } 109 | 110 | public WebClient CreateRequest() { 111 | var http = new WebClient(); 112 | http.Encoding = Encoding.UTF8; 113 | http.Headers.Add("Authorization", "Bearer " + GetToken()); 114 | http.Headers.Add("Content-Type", "application/atom+xml; charset=UTF-8"); 115 | http.Headers.Add("GData-Version", "3.0"); 116 | return http; 117 | } 118 | } 119 | } -------------------------------------------------------------------------------- /GDataDB/Impl/Lazy.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Lazy.cs 3 | // 4 | // Authors: 5 | // Zoltan Varga (vargaz@gmail.com) 6 | // Marek Safar (marek.safar@gmail.com) 7 | // Rodrigo Kumpera (kumpera@gmail.com) 8 | // 9 | // Copyright (C) 2009-2010 Novell 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining 12 | // a copy of this software and associated documentation files (the 13 | // "Software"), to deal in the Software without restriction, including 14 | // without limitation the rights to use, copy, modify, merge, publish, 15 | // distribute, sublicense, and/or sell copies of the Software, and to 16 | // permit persons to whom the Software is furnished to do so, subject to 17 | // the following conditions: 18 | // 19 | // The above copyright notice and this permission notice shall be 20 | // included in all copies or substantial portions of the Software. 21 | // 22 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 23 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 24 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 25 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 26 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 27 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 28 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 29 | // 30 | 31 | using System; 32 | using System.Runtime.Serialization; 33 | using System.Runtime.InteropServices; 34 | using System.Security.Permissions; 35 | using System.Threading; 36 | using System.Diagnostics; 37 | 38 | namespace System.Threading { 39 | internal enum LazyThreadSafetyMode { 40 | None, 41 | PublicationOnly, 42 | ExecutionAndPublication 43 | } 44 | } 45 | 46 | namespace System { 47 | [SerializableAttribute] 48 | [ComVisibleAttribute(false)] 49 | [HostProtectionAttribute(SecurityAction.LinkDemand, Synchronization = true, ExternalThreading = true)] 50 | [DebuggerDisplay("ThreadSafetyMode={Mode}, IsValueCreated={IsValueCreated}, IsValueFaulted={IsValueFaulted}, Value={ValueForDebugDisplay}")] 51 | internal class Lazy { 52 | T value; 53 | Func factory; 54 | object monitor; 55 | Exception exception; 56 | LazyThreadSafetyMode mode; 57 | bool inited; 58 | 59 | public Lazy() 60 | : this(LazyThreadSafetyMode.ExecutionAndPublication) { 61 | } 62 | 63 | public Lazy(Func valueFactory) 64 | : this(valueFactory, LazyThreadSafetyMode.ExecutionAndPublication) { 65 | } 66 | 67 | public Lazy(bool isThreadSafe) 68 | : this(() => Activator.CreateInstance(), isThreadSafe ? LazyThreadSafetyMode.ExecutionAndPublication : LazyThreadSafetyMode.None) { 69 | } 70 | 71 | public Lazy(Func valueFactory, bool isThreadSafe) 72 | : this(valueFactory, isThreadSafe ? LazyThreadSafetyMode.ExecutionAndPublication : LazyThreadSafetyMode.None) { 73 | } 74 | 75 | public Lazy(LazyThreadSafetyMode mode) 76 | : this(() => Activator.CreateInstance(), mode) { 77 | } 78 | 79 | 80 | 81 | public Lazy(Func valueFactory, LazyThreadSafetyMode mode) { 82 | if (valueFactory == null) 83 | throw new ArgumentNullException("valueFactory"); 84 | this.factory = valueFactory; 85 | if (mode != LazyThreadSafetyMode.None) 86 | monitor = new object(); 87 | this.mode = mode; 88 | } 89 | 90 | // Don't trigger expensive initialization 91 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 92 | public T Value { 93 | get { 94 | if (inited) 95 | return value; 96 | if (exception != null) 97 | throw exception; 98 | 99 | return InitValue(); 100 | } 101 | } 102 | 103 | T InitValue() { 104 | Func init_factory; 105 | T v; 106 | 107 | switch (mode) { 108 | case LazyThreadSafetyMode.None: 109 | init_factory = factory; 110 | if (init_factory == null) 111 | throw exception = new InvalidOperationException("The initialization function tries to access Value on this instance"); 112 | try { 113 | factory = null; 114 | v = init_factory(); 115 | value = v; 116 | Thread.MemoryBarrier(); 117 | inited = true; 118 | } catch (Exception ex) { 119 | exception = ex; 120 | throw; 121 | } 122 | break; 123 | 124 | case LazyThreadSafetyMode.PublicationOnly: 125 | init_factory = factory; 126 | 127 | //exceptions are ignored 128 | if (init_factory != null) 129 | v = init_factory(); 130 | else 131 | v = default(T); 132 | 133 | lock (monitor) { 134 | if (inited) 135 | return value; 136 | value = v; 137 | Thread.MemoryBarrier(); 138 | inited = true; 139 | factory = null; 140 | } 141 | break; 142 | 143 | case LazyThreadSafetyMode.ExecutionAndPublication: 144 | lock (monitor) { 145 | if (inited) 146 | return value; 147 | 148 | if (factory == null) 149 | throw exception = new InvalidOperationException("The initialization function tries to access Value on this instance"); 150 | 151 | init_factory = factory; 152 | try { 153 | factory = null; 154 | v = init_factory(); 155 | value = v; 156 | Thread.MemoryBarrier(); 157 | inited = true; 158 | } catch (Exception ex) { 159 | exception = ex; 160 | throw; 161 | } 162 | } 163 | break; 164 | 165 | default: 166 | throw new InvalidOperationException("Invalid LazyThreadSafetyMode " + mode); 167 | } 168 | 169 | return value; 170 | } 171 | 172 | public bool IsValueCreated { 173 | get { 174 | return inited; 175 | } 176 | } 177 | 178 | public override string ToString() { 179 | if (inited) 180 | return value.ToString(); 181 | else 182 | return "Value is not created"; 183 | } 184 | } 185 | } -------------------------------------------------------------------------------- /GDataDB/Impl/OAuth2Token.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace GDataDB.Impl { 4 | public class OAuth2Token { 5 | public readonly string AuthToken; 6 | public readonly DateTime Expiration; 7 | 8 | public OAuth2Token(string authToken, DateTime expiration) { 9 | AuthToken = authToken; 10 | Expiration = expiration; 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /GDataDB/Impl/Row.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace GDataDB.Impl { 4 | public class Row : IRow where T: new() { 5 | public T Element { get; set; } 6 | //public readonly DateTimeOffset Updated; 7 | public readonly string Etag; 8 | public readonly Uri Id; 9 | private readonly Uri Edit; 10 | private readonly DatabaseClient client; 11 | private readonly Serializer serializer = new Serializer(); 12 | 13 | public Row(string etag, Uri id, Uri edit, DatabaseClient client) { 14 | Etag = etag; 15 | Id = id; 16 | Edit = edit; 17 | this.client = client; 18 | } 19 | 20 | public void Update() { 21 | // https://developers.google.com/google-apps/spreadsheets/#updating_a_list_row_1 22 | var xml = serializer.Serialize(this); 23 | var http = client.RequestFactory.CreateRequest(); 24 | http.UploadString(Edit, method: "PUT", data: xml.ToString()); 25 | } 26 | 27 | public void Delete() { 28 | // https://developers.google.com/google-apps/spreadsheets/#deleting_a_list_row_1 29 | // DELETE https://spreadsheets.google.com/feeds/list/key/worksheetId/private/full/rowId/rowVersion 30 | var http = client.RequestFactory.CreateRequest(); 31 | http.Headers.Add("If-Match", "*"); 32 | http.UploadString(Edit, method: "DELETE", data: ""); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /GDataDB/Impl/Serializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Linq; 5 | using System.Reflection; 6 | using System.Xml.Linq; 7 | 8 | namespace GDataDB.Impl { 9 | /// 10 | /// (de)serializes an object into a spreadsheet row 11 | /// Uses only the object properties. 12 | /// Property names are used as column names in the spreadsheet 13 | /// 14 | /// 15 | public class Serializer where T: new() { 16 | public static readonly XNamespace GsxNs = "http://schemas.google.com/spreadsheets/2006/extended"; 17 | public static readonly XNamespace GdNs = "http://schemas.google.com/g/2005"; 18 | 19 | public XElement SerializeNewRow(T e) { 20 | return new XElement(Utils.AtomNs + "entry", 21 | new XAttribute(XNamespace.Xmlns + "gsx", GsxNs), 22 | //new XElement(DatabaseClient.AtomNs + "id", "123"), 23 | SerializeFields(e).ToArray()); 24 | } 25 | 26 | public IEnumerable GetFields() { 27 | return typeof(T).GetProperties().Where(p => p.CanRead); 28 | } 29 | 30 | public IEnumerable SerializeFields(T e) { 31 | return GetFields() 32 | .Select(p => new XElement(GsxNs + p.Name.ToLowerInvariant(), ToNullOrString(p.GetValue(e, null)))); 33 | } 34 | 35 | public XElement Serialize(Row row) { 36 | var e = new XElement(Utils.AtomNs + "entry", 37 | new XAttribute(GdNs + "etag", row.Etag)); 38 | e.Add(new XElement(Utils.AtomNs + "id", row.Id.AbsoluteUri)); 39 | e.Add(SerializeFields(row.Element)); 40 | return e; 41 | } 42 | 43 | public string ToNullOrString(object o) { 44 | if (o == null) 45 | return null; 46 | return o.ToString(); 47 | } 48 | 49 | public object ConvertFrom(object value, Type t) { 50 | if (t.IsGenericType && t.GetGenericTypeDefinition().Equals(typeof (Nullable<>))) { 51 | var nc = new NullableConverter(t); 52 | return nc.ConvertFrom(value); 53 | } 54 | return Convert.ChangeType(value, t); 55 | } 56 | 57 | public T DeserializeElement(XElement entry) { 58 | var setters = 59 | entry.Elements() 60 | .Where(e => e.Name.Namespace == GsxNs) 61 | .Select(e => new { 62 | property = new[] { typeof(T).GetProperty(e.Name.LocalName, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public) } 63 | .FirstOrDefault(x => x != null && x.CanWrite), 64 | rawValue = e.Value, 65 | }) 66 | .Where(e => e.property != null) 67 | .Select(e => new { 68 | e.property, 69 | value = ConvertFrom(e.rawValue, e.property.PropertyType), 70 | }); 71 | var r = new T(); 72 | foreach (var setter in setters) 73 | setter.property.SetValue(r, setter.value, null); 74 | return r; 75 | } 76 | 77 | public IRow DeserializeRow(XElement entry, DatabaseClient client) { 78 | var etag = entry.Attribute(GdNs + "etag").Value; 79 | var id = new Uri(entry.Element(Utils.AtomNs + "id").Value); 80 | var edit = entry 81 | .Elements(Utils.AtomNs + "link") 82 | .Where(e => e.Attribute("rel").Value == "edit") 83 | .Select(e => new Uri(e.Attribute("href").Value)) 84 | .FirstOrDefault(); 85 | 86 | var value = DeserializeElement(entry); 87 | return new Row(etag, id: id, edit: edit, client: client) { 88 | Element = value, 89 | }; 90 | } 91 | } 92 | } -------------------------------------------------------------------------------- /GDataDB/Impl/Table.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Xml.Linq; 6 | 7 | namespace GDataDB.Impl { 8 | public class Table : ITable where T: new() { 9 | private readonly DatabaseClient client; 10 | private readonly Uri listFeedUri; 11 | private readonly Uri worksheetUri; 12 | private readonly Serializer serializer = new Serializer(); 13 | 14 | public Table(DatabaseClient client, Uri listFeedUri, Uri worksheetUri) { 15 | if (listFeedUri == null) 16 | throw new ArgumentNullException("listFeedUri"); 17 | if (client == null) 18 | throw new ArgumentNullException("client"); 19 | if (worksheetUri == null) 20 | throw new ArgumentNullException("worksheetUri"); 21 | this.client = client; 22 | this.listFeedUri = listFeedUri; 23 | this.worksheetUri = worksheetUri; 24 | } 25 | 26 | public void Delete() { 27 | var http = client.RequestFactory.CreateRequest(); 28 | http.UploadString(worksheetUri, method: "DELETE", data: ""); 29 | } 30 | 31 | public void Clear() { 32 | // https://developers.google.com/google-apps/spreadsheets/#deleting_a_list_row_1 33 | var rows = Find(new Query { 34 | Order = new Order { 35 | Descending = true, 36 | } 37 | }); 38 | foreach (var row in rows) 39 | row.Delete(); 40 | } 41 | 42 | public void Rename(string newName) { 43 | var http = client.RequestFactory.CreateRequest(); 44 | var response = http.DownloadString(worksheetUri); 45 | var xmlResponse = XDocument.Parse(response); 46 | var title = xmlResponse.Root.Element(Utils.AtomNs + "title"); 47 | if (title == null) 48 | throw new Exception("Title was null in worksheet feed entry"); 49 | title.Value = newName; 50 | http = client.RequestFactory.CreateRequest(); 51 | http.UploadString(worksheetUri, method: "PUT", data: xmlResponse.Root.ToString()); 52 | } 53 | 54 | public IRow Add(T e) { 55 | // https://developers.google.com/google-apps/spreadsheets/#adding_a_list_row_1 56 | var xml = serializer.SerializeNewRow(e); 57 | var http = client.RequestFactory.CreateRequest(); 58 | var response = http.UploadString(listFeedUri, xml.ToString()); 59 | var xmlResponse = XDocument.Parse(response); 60 | var row = serializer.DeserializeRow(xmlResponse.Root, client); 61 | return row; 62 | } 63 | 64 | public IRow Get(int rowNumber) { 65 | var q = new Query { 66 | Count = 1, 67 | Start = rowNumber, 68 | }; 69 | var results = Find(q); 70 | if (results.Count == 0) 71 | return null; 72 | return results[0]; 73 | } 74 | 75 | public IList> FindAll() { 76 | return Find(new Query()); 77 | } 78 | 79 | public IList> FindAll(int start, int count) { 80 | return Find(new Query { 81 | Start = start, 82 | Count = count, 83 | }); 84 | } 85 | 86 | public IList> Find(string query) { 87 | return Find(new Query {FreeQuery = query}); 88 | } 89 | 90 | public IList> FindStructured(string query) { 91 | return Find(new Query {StructuredQuery = query}); 92 | } 93 | 94 | public IList> FindStructured(string query, int start, int count) { 95 | return Find(new Query { 96 | StructuredQuery = query, 97 | Start = start, 98 | Count = count, 99 | }); 100 | } 101 | 102 | 103 | public static string SerializeQuery(Query q) { 104 | var b = new StringBuilder(); 105 | 106 | if (q.FreeQuery != null) 107 | b.Append("q=" + Utils.UrlEncode(q.FreeQuery) + "&"); 108 | if (q.StructuredQuery != null) 109 | b.Append("sq=" + Utils.UrlEncode(q.StructuredQuery) + "&"); 110 | if (q.Start > 0) 111 | b.Append("start-index=" + q.Start + "&"); 112 | if (q.Count > 0) 113 | b.Append("max-results=" + q.Count + "&"); 114 | if (q.Order != null) { 115 | if (q.Order.ColumnName != null) 116 | b.Append("orderby=column:" + Utils.UrlEncode(q.Order.ColumnName) + "&"); 117 | if (q.Order.Descending) 118 | b.Append("reverse=true&"); 119 | } 120 | 121 | return b.ToString(); 122 | } 123 | 124 | public IList> Find(Query q) { 125 | var http = client.RequestFactory.CreateRequest(); 126 | var uri = listFeedUri + "?" + SerializeQuery(q); 127 | var rawResponse = http.DownloadString(uri); 128 | var xmlResponse = XDocument.Parse(rawResponse); 129 | return xmlResponse.Root.Elements(Utils.AtomNs + "entry") 130 | .Select(e => serializer.DeserializeRow(e, client)) 131 | .ToList(); 132 | } 133 | } 134 | } -------------------------------------------------------------------------------- /GDataDB/Impl/Utils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Xml.Linq; 3 | 4 | namespace GDataDB.Impl { 5 | public static class Utils { 6 | public static string UrlEncode(string s) { 7 | return Uri.EscapeDataString(s).Replace("%20", "+"); 8 | } 9 | 10 | public static readonly XNamespace SpreadsheetsNs = "http://schemas.google.com/spreadsheets/2006"; 11 | public static readonly XNamespace BatchNs = "http://schemas.google.com/gdata/batch"; 12 | public static readonly XNamespace AtomNs = "http://www.w3.org/2005/Atom"; 13 | 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /GDataDB/Linq/ITableExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using GDataDB.Linq.Impl; 3 | 4 | namespace GDataDB.Linq { 5 | public static class ITableExtensions { 6 | public static IQueryable AsQueryable(this ITable t) { 7 | return new Query(new GDataDBQueryProvider(t)); 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /GDataDB/Linq/Impl/Evaluator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq.Expressions; 4 | 5 | namespace GDataDB.Linq.Impl { 6 | /// 7 | /// From http://blogs.msdn.com/mattwar/archive/2007/08/01/linq-building-an-iqueryable-provider-part-iii.aspx 8 | /// 9 | public static class Evaluator { 10 | /// 11 | /// Performs evaluation & replacement of independent sub-trees 12 | /// 13 | /// The root of the expression tree. 14 | /// A function that decides whether a given expression node can be part of the local function. 15 | /// A new tree with sub-trees evaluated and replaced. 16 | public static Expression PartialEval(Expression expression, Func fnCanBeEvaluated) { 17 | return new SubtreeEvaluator(new Nominator(fnCanBeEvaluated).Nominate(expression)).Eval(expression); 18 | } 19 | 20 | 21 | /// 22 | /// Performs evaluation & replacement of independent sub-trees 23 | /// 24 | /// The root of the expression tree. 25 | /// A new tree with sub-trees evaluated and replaced. 26 | public static Expression PartialEval(Expression expression) { 27 | return PartialEval(expression, CanBeEvaluatedLocally); 28 | } 29 | 30 | 31 | private static bool CanBeEvaluatedLocally(Expression expression) { 32 | return expression.NodeType != ExpressionType.Parameter; 33 | } 34 | 35 | 36 | /// 37 | /// Evaluates & replaces sub-trees when first candidate is reached (top-down) 38 | /// 39 | private class SubtreeEvaluator : ExpressionVisitor { 40 | private readonly HashSet candidates; 41 | 42 | 43 | internal SubtreeEvaluator(HashSet candidates) { 44 | this.candidates = candidates; 45 | } 46 | 47 | 48 | internal Expression Eval(Expression exp) { 49 | return Visit(exp); 50 | } 51 | 52 | 53 | protected override Expression Visit(Expression exp) { 54 | if (exp == null) { 55 | return null; 56 | } 57 | 58 | if (candidates.Contains(exp)) { 59 | return Evaluate(exp); 60 | } 61 | 62 | return base.Visit(exp); 63 | } 64 | 65 | 66 | private Expression Evaluate(Expression e) { 67 | if (e.NodeType == ExpressionType.Constant) { 68 | return e; 69 | } 70 | 71 | LambdaExpression lambda = Expression.Lambda(e); 72 | 73 | Delegate fn = lambda.Compile(); 74 | 75 | return Expression.Constant(fn.DynamicInvoke(null), e.Type); 76 | } 77 | } 78 | 79 | 80 | /// 81 | /// Performs bottom-up analysis to determine which nodes can possibly 82 | /// be part of an evaluated sub-tree. 83 | /// 84 | private class Nominator : ExpressionVisitor { 85 | private readonly Func fnCanBeEvaluated; 86 | 87 | private HashSet candidates; 88 | 89 | private bool cannotBeEvaluated; 90 | 91 | 92 | internal Nominator(Func fnCanBeEvaluated) { 93 | this.fnCanBeEvaluated = fnCanBeEvaluated; 94 | } 95 | 96 | 97 | internal HashSet Nominate(Expression expression) { 98 | candidates = new HashSet(); 99 | 100 | Visit(expression); 101 | 102 | return candidates; 103 | } 104 | 105 | 106 | protected override Expression Visit(Expression expression) { 107 | if (expression != null) { 108 | bool saveCannotBeEvaluated = cannotBeEvaluated; 109 | 110 | cannotBeEvaluated = false; 111 | 112 | base.Visit(expression); 113 | 114 | if (!cannotBeEvaluated) { 115 | if (fnCanBeEvaluated(expression)) { 116 | candidates.Add(expression); 117 | } else { 118 | cannotBeEvaluated = true; 119 | } 120 | } 121 | 122 | cannotBeEvaluated |= saveCannotBeEvaluated; 123 | } 124 | 125 | return expression; 126 | } 127 | } 128 | } 129 | } -------------------------------------------------------------------------------- /GDataDB/Linq/Impl/ExpressionVisitor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.ObjectModel; 4 | using System.Linq.Expressions; 5 | 6 | namespace GDataDB.Linq.Impl { 7 | /// 8 | /// From http://blogs.msdn.com/mattwar/archive/2007/07/31/linq-building-an-iqueryable-provider-part-ii.aspx 9 | /// 10 | public abstract class ExpressionVisitor { 11 | protected virtual Expression Visit(Expression exp) { 12 | if (exp == null) 13 | return exp; 14 | switch (exp.NodeType) { 15 | case ExpressionType.Negate: 16 | case ExpressionType.NegateChecked: 17 | case ExpressionType.Not: 18 | case ExpressionType.Convert: 19 | case ExpressionType.ConvertChecked: 20 | case ExpressionType.ArrayLength: 21 | case ExpressionType.Quote: 22 | case ExpressionType.TypeAs: 23 | return VisitUnary((UnaryExpression) exp); 24 | case ExpressionType.Add: 25 | case ExpressionType.AddChecked: 26 | case ExpressionType.Subtract: 27 | case ExpressionType.SubtractChecked: 28 | case ExpressionType.Multiply: 29 | case ExpressionType.MultiplyChecked: 30 | case ExpressionType.Divide: 31 | case ExpressionType.Modulo: 32 | case ExpressionType.And: 33 | case ExpressionType.AndAlso: 34 | case ExpressionType.Or: 35 | case ExpressionType.OrElse: 36 | case ExpressionType.LessThan: 37 | case ExpressionType.LessThanOrEqual: 38 | case ExpressionType.GreaterThan: 39 | case ExpressionType.GreaterThanOrEqual: 40 | case ExpressionType.Equal: 41 | case ExpressionType.NotEqual: 42 | case ExpressionType.Coalesce: 43 | case ExpressionType.ArrayIndex: 44 | case ExpressionType.RightShift: 45 | case ExpressionType.LeftShift: 46 | case ExpressionType.ExclusiveOr: 47 | return VisitBinary((BinaryExpression) exp); 48 | case ExpressionType.TypeIs: 49 | return VisitTypeIs((TypeBinaryExpression) exp); 50 | case ExpressionType.Conditional: 51 | return VisitConditional((ConditionalExpression) exp); 52 | case ExpressionType.Constant: 53 | return VisitConstant((ConstantExpression) exp); 54 | case ExpressionType.Parameter: 55 | return VisitParameter((ParameterExpression) exp); 56 | case ExpressionType.MemberAccess: 57 | return VisitMemberAccess((MemberExpression) exp); 58 | case ExpressionType.Call: 59 | return VisitMethodCall((MethodCallExpression) exp); 60 | case ExpressionType.Lambda: 61 | return VisitLambda((LambdaExpression) exp); 62 | case ExpressionType.New: 63 | return VisitNew((NewExpression) exp); 64 | case ExpressionType.NewArrayInit: 65 | case ExpressionType.NewArrayBounds: 66 | return VisitNewArray((NewArrayExpression) exp); 67 | case ExpressionType.Invoke: 68 | return VisitInvocation((InvocationExpression) exp); 69 | case ExpressionType.MemberInit: 70 | return VisitMemberInit((MemberInitExpression) exp); 71 | case ExpressionType.ListInit: 72 | return VisitListInit((ListInitExpression) exp); 73 | default: 74 | throw new Exception(string.Format("Unhandled expression type: '{0}'", exp.NodeType)); 75 | } 76 | } 77 | 78 | protected virtual MemberBinding VisitBinding(MemberBinding binding) { 79 | switch (binding.BindingType) { 80 | case MemberBindingType.Assignment: 81 | return VisitMemberAssignment((MemberAssignment) binding); 82 | case MemberBindingType.MemberBinding: 83 | return VisitMemberMemberBinding((MemberMemberBinding) binding); 84 | case MemberBindingType.ListBinding: 85 | return VisitMemberListBinding((MemberListBinding) binding); 86 | default: 87 | throw new Exception(string.Format("Unhandled binding type '{0}'", binding.BindingType)); 88 | } 89 | } 90 | 91 | protected virtual ElementInit VisitElementInitializer(ElementInit initializer) { 92 | ReadOnlyCollection arguments = VisitExpressionList(initializer.Arguments); 93 | if (arguments != initializer.Arguments) { 94 | return Expression.ElementInit(initializer.AddMethod, arguments); 95 | } 96 | return initializer; 97 | } 98 | 99 | protected virtual Expression VisitUnary(UnaryExpression u) { 100 | Expression operand = Visit(u.Operand); 101 | if (operand != u.Operand) { 102 | return Expression.MakeUnary(u.NodeType, operand, u.Type, u.Method); 103 | } 104 | return u; 105 | } 106 | 107 | protected virtual Expression VisitBinary(BinaryExpression b) { 108 | Expression left = Visit(b.Left); 109 | Expression right = Visit(b.Right); 110 | Expression conversion = Visit(b.Conversion); 111 | if (left != b.Left || right != b.Right || conversion != b.Conversion) { 112 | if (b.NodeType == ExpressionType.Coalesce && b.Conversion != null) 113 | return Expression.Coalesce(left, right, conversion as LambdaExpression); 114 | return Expression.MakeBinary(b.NodeType, left, right, b.IsLiftedToNull, b.Method); 115 | } 116 | return b; 117 | } 118 | 119 | protected virtual Expression VisitTypeIs(TypeBinaryExpression b) { 120 | Expression expr = Visit(b.Expression); 121 | if (expr != b.Expression) { 122 | return Expression.TypeIs(expr, b.TypeOperand); 123 | } 124 | return b; 125 | } 126 | 127 | protected virtual Expression VisitConstant(ConstantExpression c) { 128 | return c; 129 | } 130 | 131 | protected virtual Expression VisitConditional(ConditionalExpression c) { 132 | Expression test = Visit(c.Test); 133 | Expression ifTrue = Visit(c.IfTrue); 134 | Expression ifFalse = Visit(c.IfFalse); 135 | if (test != c.Test || ifTrue != c.IfTrue || ifFalse != c.IfFalse) { 136 | return Expression.Condition(test, ifTrue, ifFalse); 137 | } 138 | return c; 139 | } 140 | 141 | protected virtual Expression VisitParameter(ParameterExpression p) { 142 | return p; 143 | } 144 | 145 | protected virtual Expression VisitMemberAccess(MemberExpression m) { 146 | Expression exp = Visit(m.Expression); 147 | if (exp != m.Expression) { 148 | return Expression.MakeMemberAccess(exp, m.Member); 149 | } 150 | return m; 151 | } 152 | 153 | protected virtual Expression VisitMethodCall(MethodCallExpression m) { 154 | Expression obj = Visit(m.Object); 155 | IEnumerable args = VisitExpressionList(m.Arguments); 156 | if (obj != m.Object || args != m.Arguments) { 157 | return Expression.Call(obj, m.Method, args); 158 | } 159 | return m; 160 | } 161 | 162 | protected virtual ReadOnlyCollection VisitExpressionList(ReadOnlyCollection original) { 163 | List list = null; 164 | for (int i = 0, n = original.Count; i < n; i++) { 165 | Expression p = Visit(original[i]); 166 | if (list != null) { 167 | list.Add(p); 168 | } else if (p != original[i]) { 169 | list = new List(n); 170 | for (int j = 0; j < i; j++) { 171 | list.Add(original[j]); 172 | } 173 | list.Add(p); 174 | } 175 | } 176 | if (list != null) { 177 | return list.AsReadOnly(); 178 | } 179 | return original; 180 | } 181 | 182 | protected virtual MemberAssignment VisitMemberAssignment(MemberAssignment assignment) { 183 | Expression e = Visit(assignment.Expression); 184 | if (e != assignment.Expression) { 185 | return Expression.Bind(assignment.Member, e); 186 | } 187 | return assignment; 188 | } 189 | 190 | protected virtual MemberMemberBinding VisitMemberMemberBinding(MemberMemberBinding binding) { 191 | IEnumerable bindings = VisitBindingList(binding.Bindings); 192 | if (bindings != binding.Bindings) { 193 | return Expression.MemberBind(binding.Member, bindings); 194 | } 195 | return binding; 196 | } 197 | 198 | protected virtual MemberListBinding VisitMemberListBinding(MemberListBinding binding) { 199 | IEnumerable initializers = VisitElementInitializerList(binding.Initializers); 200 | if (initializers != binding.Initializers) { 201 | return Expression.ListBind(binding.Member, initializers); 202 | } 203 | return binding; 204 | } 205 | 206 | protected virtual IEnumerable VisitBindingList(ReadOnlyCollection original) { 207 | List list = null; 208 | for (int i = 0, n = original.Count; i < n; i++) { 209 | MemberBinding b = VisitBinding(original[i]); 210 | if (list != null) { 211 | list.Add(b); 212 | } else if (b != original[i]) { 213 | list = new List(n); 214 | for (int j = 0; j < i; j++) { 215 | list.Add(original[j]); 216 | } 217 | list.Add(b); 218 | } 219 | } 220 | if (list != null) 221 | return list; 222 | return original; 223 | } 224 | 225 | protected virtual IEnumerable VisitElementInitializerList(ReadOnlyCollection original) { 226 | List list = null; 227 | for (int i = 0, n = original.Count; i < n; i++) { 228 | ElementInit init = VisitElementInitializer(original[i]); 229 | if (list != null) { 230 | list.Add(init); 231 | } else if (init != original[i]) { 232 | list = new List(n); 233 | for (int j = 0; j < i; j++) { 234 | list.Add(original[j]); 235 | } 236 | list.Add(init); 237 | } 238 | } 239 | if (list != null) 240 | return list; 241 | return original; 242 | } 243 | 244 | protected virtual Expression VisitLambda(LambdaExpression lambda) { 245 | Expression body = Visit(lambda.Body); 246 | if (body != lambda.Body) { 247 | return Expression.Lambda(lambda.Type, body, lambda.Parameters); 248 | } 249 | return lambda; 250 | } 251 | 252 | protected virtual NewExpression VisitNew(NewExpression nex) { 253 | IEnumerable args = VisitExpressionList(nex.Arguments); 254 | if (args != nex.Arguments) { 255 | if (nex.Members != null) 256 | return Expression.New(nex.Constructor, args, nex.Members); 257 | return Expression.New(nex.Constructor, args); 258 | } 259 | return nex; 260 | } 261 | 262 | protected virtual Expression VisitMemberInit(MemberInitExpression init) { 263 | NewExpression n = VisitNew(init.NewExpression); 264 | IEnumerable bindings = VisitBindingList(init.Bindings); 265 | if (n != init.NewExpression || bindings != init.Bindings) { 266 | return Expression.MemberInit(n, bindings); 267 | } 268 | return init; 269 | } 270 | 271 | protected virtual Expression VisitListInit(ListInitExpression init) { 272 | NewExpression n = VisitNew(init.NewExpression); 273 | IEnumerable initializers = VisitElementInitializerList(init.Initializers); 274 | if (n != init.NewExpression || initializers != init.Initializers) { 275 | return Expression.ListInit(n, initializers); 276 | } 277 | return init; 278 | } 279 | 280 | protected virtual Expression VisitNewArray(NewArrayExpression na) { 281 | IEnumerable exprs = VisitExpressionList(na.Expressions); 282 | if (exprs != na.Expressions) { 283 | if (na.NodeType == ExpressionType.NewArrayInit) { 284 | return Expression.NewArrayInit(na.Type.GetElementType(), exprs); 285 | } 286 | return Expression.NewArrayBounds(na.Type.GetElementType(), exprs); 287 | } 288 | return na; 289 | } 290 | 291 | protected virtual Expression VisitInvocation(InvocationExpression iv) { 292 | IEnumerable args = VisitExpressionList(iv.Arguments); 293 | Expression expr = Visit(iv.Expression); 294 | if (args != iv.Arguments || expr != iv.Expression) { 295 | return Expression.Invoke(expr, args); 296 | } 297 | return iv; 298 | } 299 | } 300 | } -------------------------------------------------------------------------------- /GDataDB/Linq/Impl/GDataDBQueryProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Linq.Expressions; 4 | 5 | namespace GDataDB.Linq.Impl { 6 | public class GDataDBQueryProvider : QueryProvider { 7 | private readonly ITable table; 8 | 9 | public GDataDBQueryProvider(ITable table) { 10 | this.table = table; 11 | } 12 | 13 | public override Query GetQuery(Expression expression) { 14 | expression = Evaluator.PartialEval(expression); 15 | return new QueryTranslator().Translate(expression); 16 | } 17 | 18 | public override object Execute(Expression expression) { 19 | return table.Find(GetQuery(expression)).Select(r => r.Element); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /GDataDB/Linq/Impl/OrderTranslator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | namespace GDataDB.Linq.Impl { 5 | public class OrderTranslator : ExpressionVisitor { 6 | private string columnName; 7 | private bool descending; 8 | 9 | public Order Translate(Expression e) { 10 | Visit(e); 11 | if (columnName == null) 12 | return null; 13 | return new Order {ColumnName = columnName, Descending = descending}; 14 | } 15 | 16 | protected override Expression VisitMethodCall(MethodCallExpression m) { 17 | if (m.Arguments.Count > 2) 18 | throw new NotSupportedException("OrderBy with comparer is not supported"); 19 | if (m.Method.Name == "OrderBy") { 20 | Visit(m.Arguments[1]); 21 | } else if (m.Method.Name == "OrderByDescending") { 22 | descending = true; 23 | Visit(m.Arguments[1]); 24 | } 25 | return m; 26 | } 27 | 28 | protected override Expression VisitMemberAccess(MemberExpression m) { 29 | columnName = m.Member.Name.ToLowerInvariant(); 30 | return m; 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /GDataDB/Linq/Impl/Query.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Linq.Expressions; 6 | 7 | namespace GDataDB.Linq.Impl { 8 | /// 9 | /// From http://blogs.msdn.com/mattwar/archive/2007/07/30/linq-building-an-iqueryable-provider-part-i.aspx 10 | /// 11 | /// 12 | public class Query : IOrderedQueryable { 13 | private readonly QueryProvider provider; 14 | private readonly Expression expression; 15 | 16 | public Query(QueryProvider provider) { 17 | if (provider == null) { 18 | throw new ArgumentNullException("provider"); 19 | } 20 | this.provider = provider; 21 | expression = Expression.Constant(this); 22 | } 23 | 24 | public Query(QueryProvider provider, Expression expression) { 25 | if (provider == null) { 26 | throw new ArgumentNullException("provider"); 27 | } 28 | if (expression == null) { 29 | throw new ArgumentNullException("expression"); 30 | } 31 | if (!typeof (IQueryable).IsAssignableFrom(expression.Type)) { 32 | throw new ArgumentOutOfRangeException("expression"); 33 | } 34 | this.provider = provider; 35 | this.expression = expression; 36 | } 37 | 38 | Expression IQueryable.Expression { 39 | get { return expression; } 40 | } 41 | 42 | Type IQueryable.ElementType { 43 | get { return typeof (T); } 44 | } 45 | 46 | IQueryProvider IQueryable.Provider { 47 | get { return provider; } 48 | } 49 | 50 | public IEnumerator GetEnumerator() { 51 | return ((IEnumerable) provider.Execute(expression)).GetEnumerator(); 52 | } 53 | 54 | public override string ToString() { 55 | return ToQuery().StructuredQuery; 56 | } 57 | 58 | public Query ToQuery() { 59 | return provider.GetQuery(expression); 60 | } 61 | 62 | IEnumerator IEnumerable.GetEnumerator() { 63 | return GetEnumerator(); 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /GDataDB/Linq/Impl/QueryProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Linq.Expressions; 4 | using System.Reflection; 5 | 6 | namespace GDataDB.Linq.Impl { 7 | /// 8 | /// From http://blogs.msdn.com/mattwar/archive/2007/07/30/linq-building-an-iqueryable-provider-part-i.aspx 9 | /// 10 | public abstract class QueryProvider : IQueryProvider { 11 | IQueryable IQueryProvider.CreateQuery(Expression expression) { 12 | return new Query(this, expression); 13 | } 14 | 15 | IQueryable IQueryProvider.CreateQuery(Expression expression) { 16 | Type elementType = TypeSystem.GetElementType(expression.Type); 17 | try { 18 | return (IQueryable) Activator.CreateInstance(typeof (Query<>).MakeGenericType(elementType), new object[] {this, expression}); 19 | } catch (TargetInvocationException tie) { 20 | throw tie.InnerException; 21 | } 22 | } 23 | 24 | public abstract Query GetQuery(Expression expression); 25 | public abstract object Execute(Expression expression); 26 | 27 | public TResult Execute(Expression expression) { 28 | return (TResult) Execute(expression); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /GDataDB/Linq/Impl/QueryTranslator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | namespace GDataDB.Linq.Impl { 5 | public class QueryTranslator : ExpressionVisitor { 6 | private readonly Query q = new Query(); 7 | 8 | public Query Translate(Expression e) { 9 | Visit(e); 10 | return q; 11 | } 12 | 13 | protected override Expression VisitMethodCall(MethodCallExpression m) { 14 | Visit(m.Arguments[0]); 15 | switch (m.Method.Name) { 16 | case "Where": 17 | q.StructuredQuery = new WhereTranslator().Translate(m); 18 | break; 19 | case "OrderBy": 20 | case "OrderByDescending": 21 | q.Order = new OrderTranslator().Translate(m); 22 | break; 23 | case "Take": 24 | q.Count = (int)((ConstantExpression)m.Arguments[1]).Value; 25 | break; 26 | case "Skip": 27 | q.Start = (int)((ConstantExpression)m.Arguments[1]).Value+1; 28 | break; 29 | default: 30 | throw new NotSupportedException(string.Format("Method {0} not supported", m.Method.Name)); 31 | } 32 | return m; 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /GDataDB/Linq/Impl/TypeSystem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace GDataDB.Linq.Impl { 5 | /// 6 | /// From http://blogs.msdn.com/mattwar/archive/2007/07/30/linq-building-an-iqueryable-provider-part-i.aspx 7 | /// 8 | internal static class TypeSystem { 9 | internal static Type GetElementType(Type seqType) { 10 | Type ienum = FindIEnumerable(seqType); 11 | if (ienum == null) return seqType; 12 | return ienum.GetGenericArguments()[0]; 13 | } 14 | 15 | private static Type FindIEnumerable(Type seqType) { 16 | if (seqType == null || seqType == typeof (string)) 17 | return null; 18 | if (seqType.IsArray) 19 | return typeof (IEnumerable<>).MakeGenericType(seqType.GetElementType()); 20 | if (seqType.IsGenericType) { 21 | foreach (var arg in seqType.GetGenericArguments()) { 22 | Type ienum = typeof (IEnumerable<>).MakeGenericType(arg); 23 | if (ienum.IsAssignableFrom(seqType)) { 24 | return ienum; 25 | } 26 | } 27 | } 28 | Type[] ifaces = seqType.GetInterfaces(); 29 | if (ifaces != null && ifaces.Length > 0) { 30 | foreach (var iface in ifaces) { 31 | Type ienum = FindIEnumerable(iface); 32 | if (ienum != null) return ienum; 33 | } 34 | } 35 | if (seqType.BaseType != null && seqType.BaseType != typeof (object)) { 36 | return FindIEnumerable(seqType.BaseType); 37 | } 38 | return null; 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /GDataDB/Linq/Impl/WhereTranslator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | using System.Text; 4 | 5 | namespace GDataDB.Linq.Impl { 6 | public class WhereTranslator : ExpressionVisitor { 7 | private readonly StringBuilder sb = new StringBuilder(); 8 | 9 | public string Translate(Expression e) { 10 | Visit(e); 11 | return sb.ToString(); 12 | } 13 | 14 | protected override Expression VisitMethodCall(MethodCallExpression m) { 15 | if (m.Method.Name == "Where") { 16 | Visit(m.Arguments[1]); 17 | } 18 | return m; 19 | } 20 | 21 | protected override Expression VisitBinary(BinaryExpression b) { 22 | sb.Append("("); 23 | switch (b.NodeType) { 24 | case ExpressionType.And: 25 | case ExpressionType.AndAlso: 26 | Visit(b.Left); 27 | sb.Append("&&"); 28 | Visit(b.Right); 29 | break; 30 | case ExpressionType.Or: 31 | case ExpressionType.OrElse: 32 | Visit(b.Left); 33 | sb.Append("||"); 34 | Visit(b.Right); 35 | break; 36 | case ExpressionType.Equal: 37 | Visit(b.Left); 38 | sb.Append("="); 39 | Visit(b.Right); 40 | break; 41 | case ExpressionType.NotEqual: 42 | Visit(b.Left); 43 | sb.Append("!="); 44 | Visit(b.Right); 45 | break; 46 | case ExpressionType.LessThan: 47 | Visit(b.Left); 48 | sb.Append("<"); 49 | Visit(b.Right); 50 | break; 51 | case ExpressionType.GreaterThan: 52 | Visit(b.Left); 53 | sb.Append(">"); 54 | Visit(b.Right); 55 | break; 56 | case ExpressionType.LessThanOrEqual: 57 | Visit(Expression.Or(Expression.LessThan(b.Left, b.Right), Expression.Equal(b.Left, b.Right))); 58 | break; 59 | case ExpressionType.GreaterThanOrEqual: 60 | Visit(Expression.Or(Expression.GreaterThan(b.Left, b.Right), Expression.Equal(b.Left, b.Right))); 61 | break; 62 | default: 63 | throw new NotSupportedException(string.Format("The binary operator '{0}' is not supported", b.NodeType)); 64 | } 65 | sb.Append(")"); 66 | return b; 67 | } 68 | 69 | protected override Expression VisitMemberAccess(MemberExpression m) { 70 | if (m.Expression != null && m.Expression.NodeType == ExpressionType.Parameter) { 71 | sb.Append(m.Member.Name.ToLowerInvariant()); 72 | return m; 73 | } 74 | throw new NotSupportedException(string.Format("The member '{0}' is not supported", m.Member.Name)); 75 | } 76 | 77 | protected override Expression VisitConstant(ConstantExpression c) { 78 | if (c.Value is string) 79 | sb.AppendFormat("\"{0}\"", c.Value); 80 | else 81 | sb.Append(c.Value.ToString()); 82 | return c; 83 | } 84 | } 85 | } -------------------------------------------------------------------------------- /GDataDB/Order.cs: -------------------------------------------------------------------------------- 1 | namespace GDataDB { 2 | /// 3 | /// Sort order 4 | /// 5 | public class Order { 6 | public bool Descending { get; set; } 7 | public string ColumnName { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /GDataDB/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("GDataDB")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("GDataDB")] 13 | [assembly: AssemblyCopyright("Copyright © Mauricio Scheffer 2008-2015")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("e5359f4b-8845-4d9f-8cb1-97eb0764e1ac")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("0.6.1.0")] 36 | [assembly: AssemblyFileVersion("0.6.1.0")] 37 | -------------------------------------------------------------------------------- /GDataDB/Query.cs: -------------------------------------------------------------------------------- 1 | namespace GDataDB { 2 | /// 3 | /// Query parameters 4 | /// 5 | public class Query { 6 | /// 7 | /// Start index, for paging 8 | /// 9 | public int Start { get; set; } 10 | 11 | /// 12 | /// Record count to fetch, for paging 13 | /// 14 | public int Count { get; set; } 15 | 16 | /// 17 | /// Free text query 18 | /// 19 | public string FreeQuery { get; set; } 20 | 21 | /// 22 | /// Structured query 23 | /// 24 | public string StructuredQuery { get; set; } 25 | 26 | /// 27 | /// Sort order 28 | /// 29 | public Order Order { get; set; } 30 | } 31 | } -------------------------------------------------------------------------------- /GDataDB/app.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /GDataDB/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GDataDB 2 | 3 | GDataDB is a database-like interface to Google Spreadsheets for .Net.
4 | It models spreadsheets as databases, worksheets as database tables, and rows as records. 5 | 6 | [NuGet package here.](https://www.nuget.org/packages/GDataDB/) 7 | 8 | A few articles documenting it: 9 | 10 | * http://bugsquash.blogspot.co.uk/search/label/gdatadb 11 | * http://ryanfarley.com/blog/archive/2010/09/24/using-google-docs-as-an-online-database.aspx 12 | 13 | Google has changed the authentication scheme since those articles were written. Now it requires OAuth2.
14 | To set this up visit http://console.developers.google.com , create a new project, enable the Drive API, create a new client ID of type "service account" and download the P12 key. Use the service account email address and the P12 key [as shown in this example](https://github.com/mausch/GDataDB/blob/master/GDataDB.Tests/IntegrationTests.cs#L27-L29) .
15 | If you want to access any documents from your personal gmail account, share them (edit permissions) with the service account email address (the one that looks like "`987191231-asdfkasjdjfk@developer.gserviceaccount.com`") and make sure that they're in the root folder of Drive. 16 | 17 | Original idea for GDataDB borrowed from https://github.com/google/gdata-python-client/blob/master/src/gdata/spreadsheet/text_db.py 18 | 19 | [![Please donate](http://www.pledgie.com/campaigns/11248.png)](http://www.pledgie.com/campaigns/11248) 20 | -------------------------------------------------------------------------------- /Sample/Entity.cs: -------------------------------------------------------------------------------- 1 | namespace Sample { 2 | public class Entity { 3 | public string Conteudo { get; set; } 4 | public int Amount { get; set; } 5 | } 6 | } -------------------------------------------------------------------------------- /Sample/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Linq; 4 | using GDataDB; 5 | using GDataDB.Linq; 6 | 7 | namespace Sample { 8 | internal class Program { 9 | private static void Main(string[] args) { 10 | Console.WriteLine("Connecting"); 11 | var client = new DatabaseClient("you@gmail.com", File.ReadAllBytes("key.p12")); 12 | const string dbName = "testing"; 13 | Console.WriteLine("Opening or creating database"); 14 | var db = client.GetDatabase(dbName) ?? client.CreateDatabase(dbName); 15 | const string tableName = "testtable"; 16 | Console.WriteLine("Opening or creating table"); 17 | var t = db.GetTable(tableName) ?? db.CreateTable(tableName); 18 | var all = t.FindAll(); 19 | Console.WriteLine("{0} elements", all.Count); 20 | var r = all.Count > 0 ? all[0] : t.Add(new Entity {Conteudo = "some content", Amount = 5}); 21 | Console.WriteLine("conteudo: {0}", r.Element.Conteudo); 22 | r.Element.Conteudo = "nothing at all"; 23 | Console.WriteLine("updating row"); 24 | r.Update(); 25 | Console.WriteLine("Now there are {0} elements", t.FindAll().Count); 26 | { 27 | Console.WriteLine("executing a few queries"); 28 | foreach (var q in new[] { "amount=5", "amount<6", "amount>0", "amount!=6", "amount<>6", "conteudo=\"nothing at all\"" }) { 29 | Console.Write("querying '{0}': ", q); 30 | var rows = t.FindStructured(q); 31 | Console.WriteLine("{0} elements found", rows.Count); 32 | } 33 | } 34 | { 35 | Console.WriteLine("Linq queries"); 36 | var rows = from e in t.AsQueryable() 37 | where e.Conteudo == r.Element.Conteudo 38 | orderby e.Amount 39 | select e; 40 | Console.WriteLine("{0} elements found", rows.ToList().Count()); 41 | } 42 | Console.WriteLine("deleting row"); 43 | r.Delete(); 44 | Console.WriteLine("deleting table"); 45 | t.Delete(); 46 | Console.WriteLine("deleting database"); 47 | db.Delete(); 48 | Console.WriteLine("Press any key..."); 49 | Console.ReadKey(); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Sample/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Sample")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Sample")] 13 | [assembly: AssemblyCopyright("Copyright © 2008")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("93a2bf9e-48af-405a-a7ef-c3efd8481b39")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /Sample/Sample.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | 9.0.21022 7 | 2.0 8 | {D3FDBE0F-E64E-4742-9F7E-0B77ACFE6380} 9 | Exe 10 | Properties 11 | Sample 12 | Sample 13 | v3.5 14 | 512 15 | 16 | 17 | 3.5 18 | 19 | 20 | 21 | true 22 | full 23 | false 24 | bin\Debug\ 25 | DEBUG;TRACE 26 | prompt 27 | 4 28 | 29 | 30 | pdbonly 31 | true 32 | bin\Release\ 33 | TRACE 34 | prompt 35 | 4 36 | 37 | 38 | 39 | 40 | 3.5 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | {9B543220-CB1D-414D-9787-2DDC08F0EBD1} 51 | GDataDB 52 | 53 | 54 | 55 | 56 | 57 | 58 | 65 | -------------------------------------------------------------------------------- /Sample/app.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ilmerge.exclude: -------------------------------------------------------------------------------- 1 | GDataDB.Linq.* 2 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | GDataDB 2 | 3 | Copyright 2008-2012 Mauricio Scheffer 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | 18 | 19 | Apache License 20 | Version 2.0, January 2004 21 | http://www.apache.org/licenses/ 22 | 23 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 24 | 25 | 1. Definitions. 26 | 27 | "License" shall mean the terms and conditions for use, reproduction, 28 | and distribution as defined by Sections 1 through 9 of this document. 29 | 30 | "Licensor" shall mean the copyright owner or entity authorized by 31 | the copyright owner that is granting the License. 32 | 33 | "Legal Entity" shall mean the union of the acting entity and all 34 | other entities that control, are controlled by, or are under common 35 | control with that entity. For the purposes of this definition, 36 | "control" means (i) the power, direct or indirect, to cause the 37 | direction or management of such entity, whether by contract or 38 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 39 | outstanding shares, or (iii) beneficial ownership of such entity. 40 | 41 | "You" (or "Your") shall mean an individual or Legal Entity 42 | exercising permissions granted by this License. 43 | 44 | "Source" form shall mean the preferred form for making modifications, 45 | including but not limited to software source code, documentation 46 | source, and configuration files. 47 | 48 | "Object" form shall mean any form resulting from mechanical 49 | transformation or translation of a Source form, including but 50 | not limited to compiled object code, generated documentation, 51 | and conversions to other media types. 52 | 53 | "Work" shall mean the work of authorship, whether in Source or 54 | Object form, made available under the License, as indicated by a 55 | copyright notice that is included in or attached to the work 56 | (an example is provided in the Appendix below). 57 | 58 | "Derivative Works" shall mean any work, whether in Source or Object 59 | form, that is based on (or derived from) the Work and for which the 60 | editorial revisions, annotations, elaborations, or other modifications 61 | represent, as a whole, an original work of authorship. For the purposes 62 | of this License, Derivative Works shall not include works that remain 63 | separable from, or merely link (or bind by name) to the interfaces of, 64 | the Work and Derivative Works thereof. 65 | 66 | "Contribution" shall mean any work of authorship, including 67 | the original version of the Work and any modifications or additions 68 | to that Work or Derivative Works thereof, that is intentionally 69 | submitted to Licensor for inclusion in the Work by the copyright owner 70 | or by an individual or Legal Entity authorized to submit on behalf of 71 | the copyright owner. For the purposes of this definition, "submitted" 72 | means any form of electronic, verbal, or written communication sent 73 | to the Licensor or its representatives, including but not limited to 74 | communication on electronic mailing lists, source code control systems, 75 | and issue tracking systems that are managed by, or on behalf of, the 76 | Licensor for the purpose of discussing and improving the Work, but 77 | excluding communication that is conspicuously marked or otherwise 78 | designated in writing by the copyright owner as "Not a Contribution." 79 | 80 | "Contributor" shall mean Licensor and any individual or Legal Entity 81 | on behalf of whom a Contribution has been received by Licensor and 82 | subsequently incorporated within the Work. 83 | 84 | 2. Grant of Copyright License. Subject to the terms and conditions of 85 | this License, each Contributor hereby grants to You a perpetual, 86 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 87 | copyright license to reproduce, prepare Derivative Works of, 88 | publicly display, publicly perform, sublicense, and distribute the 89 | Work and such Derivative Works in Source or Object form. 90 | 91 | 3. Grant of Patent License. Subject to the terms and conditions of 92 | this License, each Contributor hereby grants to You a perpetual, 93 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 94 | (except as stated in this section) patent license to make, have made, 95 | use, offer to sell, sell, import, and otherwise transfer the Work, 96 | where such license applies only to those patent claims licensable 97 | by such Contributor that are necessarily infringed by their 98 | Contribution(s) alone or by combination of their Contribution(s) 99 | with the Work to which such Contribution(s) was submitted. If You 100 | institute patent litigation against any entity (including a 101 | cross-claim or counterclaim in a lawsuit) alleging that the Work 102 | or a Contribution incorporated within the Work constitutes direct 103 | or contributory patent infringement, then any patent licenses 104 | granted to You under this License for that Work shall terminate 105 | as of the date such litigation is filed. 106 | 107 | 4. Redistribution. You may reproduce and distribute copies of the 108 | Work or Derivative Works thereof in any medium, with or without 109 | modifications, and in Source or Object form, provided that You 110 | meet the following conditions: 111 | 112 | (a) You must give any other recipients of the Work or 113 | Derivative Works a copy of this License; and 114 | 115 | (b) You must cause any modified files to carry prominent notices 116 | stating that You changed the files; and 117 | 118 | (c) You must retain, in the Source form of any Derivative Works 119 | that You distribute, all copyright, patent, trademark, and 120 | attribution notices from the Source form of the Work, 121 | excluding those notices that do not pertain to any part of 122 | the Derivative Works; and 123 | 124 | (d) If the Work includes a "NOTICE" text file as part of its 125 | distribution, then any Derivative Works that You distribute must 126 | include a readable copy of the attribution notices contained 127 | within such NOTICE file, excluding those notices that do not 128 | pertain to any part of the Derivative Works, in at least one 129 | of the following places: within a NOTICE text file distributed 130 | as part of the Derivative Works; within the Source form or 131 | documentation, if provided along with the Derivative Works; or, 132 | within a display generated by the Derivative Works, if and 133 | wherever such third-party notices normally appear. The contents 134 | of the NOTICE file are for informational purposes only and 135 | do not modify the License. You may add Your own attribution 136 | notices within Derivative Works that You distribute, alongside 137 | or as an addendum to the NOTICE text from the Work, provided 138 | that such additional attribution notices cannot be construed 139 | as modifying the License. 140 | 141 | You may add Your own copyright statement to Your modifications and 142 | may provide additional or different license terms and conditions 143 | for use, reproduction, or distribution of Your modifications, or 144 | for any such Derivative Works as a whole, provided Your use, 145 | reproduction, and distribution of the Work otherwise complies with 146 | the conditions stated in this License. 147 | 148 | 5. Submission of Contributions. Unless You explicitly state otherwise, 149 | any Contribution intentionally submitted for inclusion in the Work 150 | by You to the Licensor shall be under the terms and conditions of 151 | this License, without any additional terms or conditions. 152 | Notwithstanding the above, nothing herein shall supersede or modify 153 | the terms of any separate license agreement you may have executed 154 | with Licensor regarding such Contributions. 155 | 156 | 6. Trademarks. This License does not grant permission to use the trade 157 | names, trademarks, service marks, or product names of the Licensor, 158 | except as required for reasonable and customary use in describing the 159 | origin of the Work and reproducing the content of the NOTICE file. 160 | 161 | 7. Disclaimer of Warranty. Unless required by applicable law or 162 | agreed to in writing, Licensor provides the Work (and each 163 | Contributor provides its Contributions) on an "AS IS" BASIS, 164 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 165 | implied, including, without limitation, any warranties or conditions 166 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 167 | PARTICULAR PURPOSE. You are solely responsible for determining the 168 | appropriateness of using or redistributing the Work and assume any 169 | risks associated with Your exercise of permissions under this License. 170 | 171 | 8. Limitation of Liability. In no event and under no legal theory, 172 | whether in tort (including negligence), contract, or otherwise, 173 | unless required by applicable law (such as deliberate and grossly 174 | negligent acts) or agreed to in writing, shall any Contributor be 175 | liable to You for damages, including any direct, indirect, special, 176 | incidental, or consequential damages of any character arising as a 177 | result of this License or out of the use or inability to use the 178 | Work (including but not limited to damages for loss of goodwill, 179 | work stoppage, computer failure or malfunction, or any and all 180 | other commercial damages or losses), even if such Contributor 181 | has been advised of the possibility of such damages. 182 | 183 | 9. Accepting Warranty or Additional Liability. While redistributing 184 | the Work or Derivative Works thereof, You may choose to offer, 185 | and charge a fee for, acceptance of support, warranty, indemnity, 186 | or other liability obligations and/or rights consistent with this 187 | License. However, in accepting such obligations, You may act only 188 | on Your own behalf and on Your sole responsibility, not on behalf 189 | of any other Contributor, and only if You agree to indemnify, 190 | defend, and hold each Contributor harmless for any liability 191 | incurred by, or claims asserted against, such Contributor by reason 192 | of your accepting any such warranty or additional liability. 193 | 194 | END OF TERMS AND CONDITIONS -------------------------------------------------------------------------------- /packages/repositories.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /release.bat: -------------------------------------------------------------------------------- 1 | msbuild gdatadb.sln /m /t:rebuild /p:Configuration=Release 2 | .nuget\nuget pack GDataDB.nuspec --------------------------------------------------------------------------------