├── .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 | [](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
--------------------------------------------------------------------------------