├── .gitignore
├── README.md
├── Source
├── LinqToSqlXml.BlogSample
│ ├── BlogSample.csproj
│ ├── Program.cs
│ └── Properties
│ │ └── AssemblyInfo.cs
├── LinqToSqlXml.ProjectionSample
│ ├── Program.cs
│ ├── ProjectionSample.csproj
│ ├── Properties
│ │ └── AssemblyInfo.cs
│ └── app.config
├── LinqToSqlXml.sln
└── LinqToSqlXml
│ ├── Collections
│ └── DocumentCollection.cs
│ ├── DocumentContext.cs
│ ├── DocumentDB.dbml
│ ├── DocumentDB.dbml.layout
│ ├── DocumentDB.designer.cs
│ ├── DocumentId.cs
│ ├── Linq
│ ├── DocumentQuery.cs
│ └── SqlServer
│ │ ├── OrderByBuilder.cs
│ │ ├── PredicateBuilder.cs
│ │ ├── QueryBuilder.cs
│ │ ├── QueryProvider.cs
│ │ ├── SelectorBuilder.cs
│ │ └── XQueryMapping.cs
│ ├── LinqToSqlXml.csproj
│ ├── Properties
│ ├── AssemblyInfo.cs
│ ├── Settings.Designer.cs
│ └── Settings.settings
│ ├── Serialization
│ ├── DocumentDeserializer.cs
│ └── DocumentSerializer.cs
│ └── app.config
└── license.txt
/.gitignore:
--------------------------------------------------------------------------------
1 | *.suo
2 | *.user
3 | bin
4 | obj
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Linq to SqlXml
2 | ==============
3 | A document database emulator on top of Microsoft SQL Server XML columns.
--------------------------------------------------------------------------------
/Source/LinqToSqlXml.BlogSample/BlogSample.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | x86
6 | 8.0.30703
7 | 2.0
8 | {9413E425-9DC9-451E-85AD-478F7B631C65}
9 | Exe
10 | Properties
11 | BlogSample
12 | BlogSample
13 | v4.0
14 | Client
15 | 512
16 |
17 |
18 | x86
19 | true
20 | full
21 | false
22 | bin\Debug\
23 | DEBUG;TRACE
24 | prompt
25 | 4
26 |
27 |
28 | x86
29 | pdbonly
30 | true
31 | bin\Release\
32 | TRACE
33 | prompt
34 | 4
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | {0B728524-A481-4450-B501-64699655A5DB}
52 | LinqToSqlXml
53 |
54 |
55 |
56 |
63 |
--------------------------------------------------------------------------------
/Source/LinqToSqlXml.BlogSample/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using LinqToSqlXml;
5 |
6 | namespace BlogSample
7 | {
8 | internal class Program
9 | {
10 | private static void Main(string[] args)
11 | {
12 | var ctx = new DocumentContext("main");
13 |
14 | //var blogpost = new BlogPost()
15 | // {
16 | // Body = "gdfg",
17 | // Topic = "fsdfs world",
18 | // };
19 |
20 | //blogpost.ReplyTo("Hej hej", "gdfgdf");
21 | //blogpost.AddTag("NoSql");
22 | //Console.WriteLine(blogpost.Id);
23 | //ctx.GetCollection().Add(blogpost);
24 |
25 | //ctx.SaveChanges();
26 | //Console.Read();
27 |
28 | IQueryable query = from blogpost in ctx.GetCollection().AsQueryable()
29 | where
30 | blogpost.Comments.Any(c => c.UserName == "roger") &&
31 | blogpost.CommentCount == 1
32 | select blogpost;
33 |
34 | List result = query.ToList();
35 |
36 | foreach (BlogPost blogpost in result)
37 | {
38 | Console.WriteLine(blogpost.Topic);
39 | }
40 | }
41 | }
42 |
43 |
44 | public class BlogPost
45 | {
46 | public BlogPost()
47 | {
48 | Id = Guid.NewGuid();
49 | Comments = new List();
50 | }
51 |
52 | [DocumentId]
53 | public Guid Id { get; set; }
54 |
55 | public string Topic { get; set; }
56 | public string Body { get; set; }
57 | public ICollection Comments { get; set; }
58 |
59 | public int CommentCount
60 | {
61 | get { return Comments.Count; }
62 | }
63 |
64 | public void ReplyTo(string body, string userName)
65 | {
66 | Comments.Add(new Comment {Body = body, UserName = userName});
67 | }
68 |
69 | public void AddTag(string tag)
70 | {
71 | }
72 | }
73 |
74 |
75 | public class Comment
76 | {
77 | public string Body { get; set; }
78 | public string UserName { get; set; }
79 | }
80 | }
--------------------------------------------------------------------------------
/Source/LinqToSqlXml.BlogSample/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 |
4 | // General Information about an assembly is controlled through the following
5 | // set of attributes. Change these attribute values to modify the information
6 | // associated with an assembly.
7 |
8 | [assembly: AssemblyTitle("BlogSample")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("Microsoft")]
12 | [assembly: AssemblyProduct("BlogSample")]
13 | [assembly: AssemblyCopyright("Copyright © Microsoft 2011")]
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 |
21 | [assembly: ComVisible(false)]
22 |
23 | // The following GUID is for the ID of the typelib if this project is exposed to COM
24 |
25 | [assembly: Guid("134c27a4-ca9c-4051-9a9a-5738a20d423f")]
26 |
27 | // Version information for an assembly consists of the following four values:
28 | //
29 | // Major Version
30 | // Minor Version
31 | // Build Number
32 | // Revision
33 | //
34 | // You can specify all the values or you can default the Build and Revision Numbers
35 | // by using the '*' as shown below:
36 | // [assembly: AssemblyVersion("1.0.*")]
37 |
38 | [assembly: AssemblyVersion("1.0.0.0")]
39 | [assembly: AssemblyFileVersion("1.0.0.0")]
--------------------------------------------------------------------------------
/Source/LinqToSqlXml.ProjectionSample/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using LinqToSqlXml;
5 | using System.Diagnostics;
6 |
7 | namespace ProjectionSample
8 | {
9 | public class Projection
10 | {
11 | public decimal OrderTotal { get; set; }
12 | public Guid CustomerId { get; set; }
13 | public IEnumerable OrderDetails { get; set; }
14 | }
15 |
16 | public class ProjectionD
17 | {
18 | public decimal LineTotal { get; set; }
19 | }
20 |
21 | internal class Program
22 | {
23 | private static void Main(string[] args)
24 | {
25 | var ctx = new DocumentContext("main");
26 | ctx.EnsureDatabaseExists();
27 |
28 |
29 | var query = (from order in ctx.GetCollection().AsQueryable().OfType()
30 | where order.OrderTotal > 0
31 | where order.ShippingDate != null
32 | where order.ShippingAddress.Line1 != "aa"
33 | where order.ShippingAddress is Address
34 | //select order
35 | select new
36 | {
37 | OrderTotal = order.OrderTotal,
38 | CustomerId = order.CustomerId,
39 | OrderDetails =
40 | order.OrderDetails.Select(
41 | d =>
42 | new
43 | {
44 | LineTotal = d.ItemPrice * d.Quantity
45 | }),
46 | }
47 | ).Take(100);
48 |
49 | Stopwatch sw = new Stopwatch();
50 | sw.Start();
51 | var result = query.ToList();
52 | sw.Stop();
53 |
54 |
55 | foreach (var order in result)
56 | {
57 | Console.WriteLine("{0} {1}", order.OrderTotal, order.OrderDetails.Count());
58 | }
59 |
60 | Console.WriteLine(sw.Elapsed);
61 | Console.ReadLine();
62 | return;
63 |
64 | for (int i = 0; i < 1000; i++)
65 | {
66 | Console.WriteLine(i);
67 | var someCompany = new Customer
68 | {
69 | Address = new Address
70 | {
71 | City = "Stora mellösa",
72 | Line1 = "Linfrövägen " + i,
73 | State = "T",
74 | ZipCode = "71572"
75 | },
76 | Name = "Precio" + i,
77 |
78 | };
79 |
80 | ctx.GetCollection().Add(someCompany);
81 |
82 | var someOrder = new Order
83 | {
84 | CustomerId = Guid.NewGuid(),
85 | OrderDate = DateTime.Now,
86 | ShippingDate = DateTime.Now,
87 | OrderDetails = new List
88 | {
89 | new OrderDetail
90 | {
91 | ItemPrice = 123,
92 | ProductNo = "banan",
93 | Quantity = 432
94 | },
95 | new OrderDetail
96 | {
97 | ItemPrice = 123,
98 | ProductNo = "äpple",
99 | Quantity = 432
100 | },
101 | new OrderDetail
102 | {
103 | ItemPrice = 123,
104 | ProductNo = "gurka",
105 | Quantity = 432
106 | },
107 | },
108 | ShippingAddress = new Address
109 | {
110 | City = "gdfgdf",
111 | Line1 = "dfgdgdfgd",
112 | ZipCode = "gdfgdfgd"
113 | },
114 | Status = OrderStatus.Shipped,
115 | };
116 |
117 | ctx.GetCollection().Add(someOrder);
118 | //var result = DocumentSerializer.Serialize(specialOrder);
119 | //Console.WriteLine(result.ToString());
120 | //ctx.GetCollection().Add(specialOrder);
121 | //ctx.SaveChanges();
122 | //var des = DocumentDeserializer.Deserialize(result);
123 |
124 |
125 |
126 |
127 |
128 |
129 | // var address = new Address()
130 | // {
131 | // City = "Örebro",
132 | // Line1 = "blabla",
133 | // ZipCode = "" + i ,
134 | // };
135 |
136 | // ctx.GetCollection().Add(address);
137 | //}
138 |
139 | }
140 | ctx.SaveChanges();
141 | Console.ReadLine();
142 | }
143 | }
144 |
145 | public interface IAmDocument
146 | {
147 | }
148 |
149 | public class SpecialOrder : Order, IAmDocument
150 | {
151 | }
152 |
153 | public class Customer
154 | {
155 | [DocumentId]
156 | public Guid Id { get; private set; }
157 |
158 | public Customer()
159 | {
160 | this.Id = Guid.NewGuid();
161 | }
162 |
163 | public string Name { get; set; }
164 | public Address Address { get; set; }
165 |
166 | public Order NewOrder()
167 | {
168 | return new Order
169 | {
170 | CustomerId = this.Id,
171 | };
172 | }
173 | }
174 |
175 | public class Order
176 | {
177 | [DocumentId]
178 | public Guid Id { get; private set; }
179 |
180 | public Order()
181 | {
182 | this.Id = Guid.NewGuid();
183 | }
184 |
185 | public DateTime OrderDate { get; set; }
186 | public DateTime? ShippingDate { get; set; }
187 | public Guid CustomerId { get; set; }
188 | public Address ShippingAddress { get; set; }
189 | public ICollection OrderDetails { get; set; }
190 |
191 | public decimal OrderTotal
192 | {
193 | get { return OrderDetails.Sum(d => d.Quantity * d.ItemPrice); }
194 | }
195 |
196 | public OrderStatus Status { get; set; }
197 | }
198 |
199 | public enum OrderStatus
200 | {
201 | PreOrder,
202 | Confirmed,
203 | Shipped,
204 | Cancelled,
205 | }
206 |
207 | public class OrderDetail
208 | {
209 | public decimal Quantity { get; set; }
210 | public decimal ItemPrice { get; set; }
211 | public string ProductNo { get; set; }
212 | }
213 |
214 | public class Address
215 | {
216 | public string Line1 { get; set; }
217 | public string Line2 { get; set; }
218 | public string Line3 { get; set; }
219 | public string State { get; set; }
220 | public string City { get; set; }
221 | public string ZipCode { get; set; }
222 | }
223 | }
--------------------------------------------------------------------------------
/Source/LinqToSqlXml.ProjectionSample/ProjectionSample.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | x86
6 | 8.0.30703
7 | 2.0
8 | {AC69F2D6-680A-42EF-BC13-1278F9330A2D}
9 | Exe
10 | Properties
11 | ProjectionSample
12 | ProjectionSample
13 | v4.0
14 | Client
15 | 512
16 |
17 |
18 | x86
19 | true
20 | full
21 | false
22 | bin\Debug\
23 | DEBUG;TRACE
24 | prompt
25 | 4
26 |
27 |
28 | x86
29 | pdbonly
30 | true
31 | bin\Release\
32 | TRACE
33 | prompt
34 | 4
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 | {0B728524-A481-4450-B501-64699655A5DB}
55 | LinqToSqlXml
56 |
57 |
58 |
59 |
66 |
--------------------------------------------------------------------------------
/Source/LinqToSqlXml.ProjectionSample/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 |
4 | // General Information about an assembly is controlled through the following
5 | // set of attributes. Change these attribute values to modify the information
6 | // associated with an assembly.
7 |
8 | [assembly: AssemblyTitle("ProjectionSample")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("Microsoft")]
12 | [assembly: AssemblyProduct("ProjectionSample")]
13 | [assembly: AssemblyCopyright("Copyright © Microsoft 2011")]
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 |
21 | [assembly: ComVisible(false)]
22 |
23 | // The following GUID is for the ID of the typelib if this project is exposed to COM
24 |
25 | [assembly: Guid("1ac1eaba-a7a1-42de-a256-c664f1a8b644")]
26 |
27 | // Version information for an assembly consists of the following four values:
28 | //
29 | // Major Version
30 | // Minor Version
31 | // Build Number
32 | // Revision
33 | //
34 | // You can specify all the values or you can default the Build and Revision Numbers
35 | // by using the '*' as shown below:
36 | // [assembly: AssemblyVersion("1.0.*")]
37 |
38 | [assembly: AssemblyVersion("1.0.0.0")]
39 | [assembly: AssemblyFileVersion("1.0.0.0")]
--------------------------------------------------------------------------------
/Source/LinqToSqlXml.ProjectionSample/app.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Source/LinqToSqlXml.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 11.00
3 | # Visual Studio 2010
4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProjectionSample", "LinqToSqlXml.ProjectionSample\ProjectionSample.csproj", "{AC69F2D6-680A-42EF-BC13-1278F9330A2D}"
5 | EndProject
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlogSample", "LinqToSqlXml.BlogSample\BlogSample.csproj", "{9413E425-9DC9-451E-85AD-478F7B631C65}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{3F1396FB-640A-42EE-B6DD-105911029196}"
9 | EndProject
10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LinqToSqlXml", "LinqToSqlXml\LinqToSqlXml.csproj", "{0B728524-A481-4450-B501-64699655A5DB}"
11 | EndProject
12 | Global
13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
14 | Debug|Any CPU = Debug|Any CPU
15 | Debug|Mixed Platforms = Debug|Mixed Platforms
16 | Debug|x86 = Debug|x86
17 | Release|Any CPU = Release|Any CPU
18 | Release|Mixed Platforms = Release|Mixed Platforms
19 | Release|x86 = Release|x86
20 | EndGlobalSection
21 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
22 | {AC69F2D6-680A-42EF-BC13-1278F9330A2D}.Debug|Any CPU.ActiveCfg = Debug|x86
23 | {AC69F2D6-680A-42EF-BC13-1278F9330A2D}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
24 | {AC69F2D6-680A-42EF-BC13-1278F9330A2D}.Debug|Mixed Platforms.Build.0 = Debug|x86
25 | {AC69F2D6-680A-42EF-BC13-1278F9330A2D}.Debug|x86.ActiveCfg = Debug|x86
26 | {AC69F2D6-680A-42EF-BC13-1278F9330A2D}.Debug|x86.Build.0 = Debug|x86
27 | {AC69F2D6-680A-42EF-BC13-1278F9330A2D}.Release|Any CPU.ActiveCfg = Release|x86
28 | {AC69F2D6-680A-42EF-BC13-1278F9330A2D}.Release|Mixed Platforms.ActiveCfg = Release|x86
29 | {AC69F2D6-680A-42EF-BC13-1278F9330A2D}.Release|Mixed Platforms.Build.0 = Release|x86
30 | {AC69F2D6-680A-42EF-BC13-1278F9330A2D}.Release|x86.ActiveCfg = Release|x86
31 | {AC69F2D6-680A-42EF-BC13-1278F9330A2D}.Release|x86.Build.0 = Release|x86
32 | {9413E425-9DC9-451E-85AD-478F7B631C65}.Debug|Any CPU.ActiveCfg = Debug|x86
33 | {9413E425-9DC9-451E-85AD-478F7B631C65}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
34 | {9413E425-9DC9-451E-85AD-478F7B631C65}.Debug|Mixed Platforms.Build.0 = Debug|x86
35 | {9413E425-9DC9-451E-85AD-478F7B631C65}.Debug|x86.ActiveCfg = Debug|x86
36 | {9413E425-9DC9-451E-85AD-478F7B631C65}.Debug|x86.Build.0 = Debug|x86
37 | {9413E425-9DC9-451E-85AD-478F7B631C65}.Release|Any CPU.ActiveCfg = Release|x86
38 | {9413E425-9DC9-451E-85AD-478F7B631C65}.Release|Mixed Platforms.ActiveCfg = Release|x86
39 | {9413E425-9DC9-451E-85AD-478F7B631C65}.Release|Mixed Platforms.Build.0 = Release|x86
40 | {9413E425-9DC9-451E-85AD-478F7B631C65}.Release|x86.ActiveCfg = Release|x86
41 | {9413E425-9DC9-451E-85AD-478F7B631C65}.Release|x86.Build.0 = Release|x86
42 | {0B728524-A481-4450-B501-64699655A5DB}.Debug|Any CPU.ActiveCfg = Debug|x86
43 | {0B728524-A481-4450-B501-64699655A5DB}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
44 | {0B728524-A481-4450-B501-64699655A5DB}.Debug|Mixed Platforms.Build.0 = Debug|x86
45 | {0B728524-A481-4450-B501-64699655A5DB}.Debug|x86.ActiveCfg = Debug|x86
46 | {0B728524-A481-4450-B501-64699655A5DB}.Debug|x86.Build.0 = Debug|x86
47 | {0B728524-A481-4450-B501-64699655A5DB}.Release|Any CPU.ActiveCfg = Release|x86
48 | {0B728524-A481-4450-B501-64699655A5DB}.Release|Mixed Platforms.ActiveCfg = Release|x86
49 | {0B728524-A481-4450-B501-64699655A5DB}.Release|Mixed Platforms.Build.0 = Release|x86
50 | {0B728524-A481-4450-B501-64699655A5DB}.Release|x86.ActiveCfg = Release|x86
51 | {0B728524-A481-4450-B501-64699655A5DB}.Release|x86.Build.0 = Release|x86
52 | EndGlobalSection
53 | GlobalSection(SolutionProperties) = preSolution
54 | HideSolutionNode = FALSE
55 | EndGlobalSection
56 | GlobalSection(NestedProjects) = preSolution
57 | {9413E425-9DC9-451E-85AD-478F7B631C65} = {3F1396FB-640A-42EE-B6DD-105911029196}
58 | {AC69F2D6-680A-42EF-BC13-1278F9330A2D} = {3F1396FB-640A-42EE-B6DD-105911029196}
59 | EndGlobalSection
60 | EndGlobal
61 |
--------------------------------------------------------------------------------
/Source/LinqToSqlXml/Collections/DocumentCollection.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Reflection;
5 |
6 | namespace LinqToSqlXml
7 | {
8 | public class DocumentCollection
9 | {
10 | protected string collectionName = "";
11 | protected DocumentContext owner;
12 |
13 |
14 | public string CollectionName
15 | {
16 | get { return collectionName; }
17 | }
18 |
19 | internal IEnumerable ExecuteQuery(string query)
20 | {
21 | return owner.DB.ExecuteQuery(query);
22 | }
23 | }
24 |
25 | public class DocumentCollection : DocumentCollection
26 | {
27 | public DocumentCollection(DocumentContext owner)
28 | {
29 | this.owner = owner;
30 | collectionName = typeof (T).Name;
31 | }
32 |
33 | internal DocumentContext Owner
34 | {
35 | get { return owner; }
36 | }
37 |
38 | public void Add(T item)
39 | {
40 | Guid documentId;
41 |
42 | PropertyInfo idproperty = item.GetType().GetDocumentIdProperty();
43 | if (idproperty != null)
44 | {
45 | var propertyvalue = (Guid) idproperty.GetValue(item, null);
46 | if (propertyvalue == Guid.Empty)
47 | {
48 | documentId = Guid.NewGuid();
49 | idproperty.SetValue(item, documentId, null);
50 | }
51 | else
52 | documentId = propertyvalue;
53 | }
54 | else
55 | {
56 | documentId = Guid.NewGuid();
57 | }
58 |
59 |
60 | var doc = new Document();
61 | doc.Id = documentId;
62 | doc.DocumentData = DocumentSerializer.Serialize(item);
63 | doc.CollectionName = collectionName;
64 | doc.DbName = owner.DbInstance;
65 |
66 | owner.DB.Documents.InsertOnSubmit(doc);
67 | }
68 |
69 | public IQueryable AsQueryable()
70 | {
71 | var queryProvider = new SqlServerQueryProvider(this);
72 | return new DocumentQuery(queryProvider);
73 | }
74 | }
75 | }
--------------------------------------------------------------------------------
/Source/LinqToSqlXml/DocumentContext.cs:
--------------------------------------------------------------------------------
1 | namespace LinqToSqlXml
2 | {
3 | public class DocumentContext
4 | {
5 | private readonly DocumentDataContext db = new DocumentDataContext();
6 | private readonly string dbInstance;
7 |
8 | public DocumentContext(string dbInstance)
9 | {
10 | this.dbInstance = dbInstance;
11 | }
12 |
13 | public string DbInstance
14 | {
15 | get { return dbInstance; }
16 | }
17 |
18 | internal DocumentDataContext DB
19 | {
20 | get { return db; }
21 | }
22 |
23 | public void EnsureDatabaseExists()
24 | {
25 | if (!db.DatabaseExists())
26 | db.CreateDatabase();
27 | }
28 |
29 | public DocumentCollection GetCollection() where T : class
30 | {
31 | return new DocumentCollection(this);
32 | }
33 |
34 | public void SaveChanges()
35 | {
36 | db.SubmitChanges();
37 | }
38 | }
39 | }
--------------------------------------------------------------------------------
/Source/LinqToSqlXml/DocumentDB.dbml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/Source/LinqToSqlXml/DocumentDB.dbml.layout:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/Source/LinqToSqlXml/DocumentDB.designer.cs:
--------------------------------------------------------------------------------
1 | #pragma warning disable 1591
2 | //------------------------------------------------------------------------------
3 | //
4 | // This code was generated by a tool.
5 | // Runtime Version:4.0.30319.1
6 | //
7 | // Changes to this file may cause incorrect behavior and will be lost if
8 | // the code is regenerated.
9 | //
10 | //------------------------------------------------------------------------------
11 |
12 | namespace LinqToSqlXml
13 | {
14 | using System.Data.Linq;
15 | using System.Data.Linq.Mapping;
16 | using System.Data;
17 | using System.Collections.Generic;
18 | using System.Reflection;
19 | using System.Linq;
20 | using System.Linq.Expressions;
21 | using System.ComponentModel;
22 | using System;
23 |
24 |
25 | [global::System.Data.Linq.Mapping.DatabaseAttribute(Name="DocumentDB")]
26 | internal partial class DocumentDataContext : System.Data.Linq.DataContext
27 | {
28 |
29 | private static System.Data.Linq.Mapping.MappingSource mappingSource = new AttributeMappingSource();
30 |
31 | #region Extensibility Method Definitions
32 | partial void OnCreated();
33 | partial void InsertDocument(Document instance);
34 | partial void UpdateDocument(Document instance);
35 | partial void DeleteDocument(Document instance);
36 | #endregion
37 |
38 | public DocumentDataContext() :
39 | base(global::LinqToSqlXml.Properties.Settings.Default.LinqToSqlXmlConnectionString, mappingSource)
40 | {
41 | OnCreated();
42 | }
43 |
44 | public DocumentDataContext(string connection) :
45 | base(connection, mappingSource)
46 | {
47 | OnCreated();
48 | }
49 |
50 | public DocumentDataContext(System.Data.IDbConnection connection) :
51 | base(connection, mappingSource)
52 | {
53 | OnCreated();
54 | }
55 |
56 | public DocumentDataContext(string connection, System.Data.Linq.Mapping.MappingSource mappingSource) :
57 | base(connection, mappingSource)
58 | {
59 | OnCreated();
60 | }
61 |
62 | public DocumentDataContext(System.Data.IDbConnection connection, System.Data.Linq.Mapping.MappingSource mappingSource) :
63 | base(connection, mappingSource)
64 | {
65 | OnCreated();
66 | }
67 |
68 | public System.Data.Linq.Table Documents
69 | {
70 | get
71 | {
72 | return this.GetTable();
73 | }
74 | }
75 | }
76 |
77 | [global::System.Data.Linq.Mapping.TableAttribute(Name="dbo.Documents")]
78 | public partial class Document : INotifyPropertyChanging, INotifyPropertyChanged
79 | {
80 |
81 | private static PropertyChangingEventArgs emptyChangingEventArgs = new PropertyChangingEventArgs(String.Empty);
82 |
83 | private System.Guid _Id;
84 |
85 | private string _DbName;
86 |
87 | private string _CollectionName;
88 |
89 | private System.Xml.Linq.XElement _DocumentData;
90 |
91 | #region Extensibility Method Definitions
92 | partial void OnLoaded();
93 | partial void OnValidate(System.Data.Linq.ChangeAction action);
94 | partial void OnCreated();
95 | partial void OnIdChanging(System.Guid value);
96 | partial void OnIdChanged();
97 | partial void OnDbNameChanging(string value);
98 | partial void OnDbNameChanged();
99 | partial void OnCollectionNameChanging(string value);
100 | partial void OnCollectionNameChanged();
101 | partial void OnDocumentDataChanging(System.Xml.Linq.XElement value);
102 | partial void OnDocumentDataChanged();
103 | #endregion
104 |
105 | public Document()
106 | {
107 | OnCreated();
108 | }
109 |
110 | [global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_Id", DbType="UniqueIdentifier NOT NULL", IsPrimaryKey=true)]
111 | public System.Guid Id
112 | {
113 | get
114 | {
115 | return this._Id;
116 | }
117 | set
118 | {
119 | if ((this._Id != value))
120 | {
121 | this.OnIdChanging(value);
122 | this.SendPropertyChanging();
123 | this._Id = value;
124 | this.SendPropertyChanged("Id");
125 | this.OnIdChanged();
126 | }
127 | }
128 | }
129 |
130 | [global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_DbName", DbType="NVarChar(50) NOT NULL", CanBeNull=false)]
131 | public string DbName
132 | {
133 | get
134 | {
135 | return this._DbName;
136 | }
137 | set
138 | {
139 | if ((this._DbName != value))
140 | {
141 | this.OnDbNameChanging(value);
142 | this.SendPropertyChanging();
143 | this._DbName = value;
144 | this.SendPropertyChanged("DbName");
145 | this.OnDbNameChanged();
146 | }
147 | }
148 | }
149 |
150 | [global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_CollectionName", DbType="NVarChar(50) NOT NULL", CanBeNull=false)]
151 | public string CollectionName
152 | {
153 | get
154 | {
155 | return this._CollectionName;
156 | }
157 | set
158 | {
159 | if ((this._CollectionName != value))
160 | {
161 | this.OnCollectionNameChanging(value);
162 | this.SendPropertyChanging();
163 | this._CollectionName = value;
164 | this.SendPropertyChanged("CollectionName");
165 | this.OnCollectionNameChanged();
166 | }
167 | }
168 | }
169 |
170 | [global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_DocumentData", DbType="Xml NOT NULL", CanBeNull=false, UpdateCheck=UpdateCheck.Never)]
171 | public System.Xml.Linq.XElement DocumentData
172 | {
173 | get
174 | {
175 | return this._DocumentData;
176 | }
177 | set
178 | {
179 | if ((this._DocumentData != value))
180 | {
181 | this.OnDocumentDataChanging(value);
182 | this.SendPropertyChanging();
183 | this._DocumentData = value;
184 | this.SendPropertyChanged("DocumentData");
185 | this.OnDocumentDataChanged();
186 | }
187 | }
188 | }
189 |
190 | public event PropertyChangingEventHandler PropertyChanging;
191 |
192 | public event PropertyChangedEventHandler PropertyChanged;
193 |
194 | protected virtual void SendPropertyChanging()
195 | {
196 | if ((this.PropertyChanging != null))
197 | {
198 | this.PropertyChanging(this, emptyChangingEventArgs);
199 | }
200 | }
201 |
202 | protected virtual void SendPropertyChanged(String propertyName)
203 | {
204 | if ((this.PropertyChanged != null))
205 | {
206 | this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
207 | }
208 | }
209 | }
210 | }
211 | #pragma warning restore 1591
212 |
--------------------------------------------------------------------------------
/Source/LinqToSqlXml/DocumentId.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reflection;
3 |
4 | namespace LinqToSqlXml
5 | {
6 | [AttributeUsage(AttributeTargets.Property)]
7 | public class DocumentIdAttribute : Attribute
8 | {
9 | }
10 |
11 |
12 | public static class DocumentIdExtensions
13 | {
14 | public static PropertyInfo GetDocumentIdProperty(this Type self)
15 | {
16 | PropertyInfo property = self.GetProperty("Id");
17 | if (property == null)
18 | return null;
19 | if (property.IsDefined(typeof (DocumentIdAttribute), true))
20 | return property;
21 |
22 | return null;
23 | }
24 | }
25 | }
--------------------------------------------------------------------------------
/Source/LinqToSqlXml/Linq/DocumentQuery.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 LinqToSqlXml
8 | {
9 | public class DocumentQuery : IQueryable, IOrderedQueryable
10 | {
11 | private readonly Expression expression;
12 | private readonly SqlServerQueryProvider queryProvider;
13 |
14 | public DocumentQuery(SqlServerQueryProvider queryProvider)
15 | {
16 | expression = Expression.Constant(this);
17 | this.queryProvider = queryProvider;
18 | }
19 |
20 | public DocumentQuery(SqlServerQueryProvider queryProvider, Expression expression)
21 | {
22 | this.expression = expression;
23 | this.queryProvider = queryProvider;
24 | }
25 |
26 | #region IQueryable Members
27 |
28 | public IEnumerator GetEnumerator()
29 | {
30 | IEnumerable result = queryProvider.ExecuteQuery(expression);
31 | IEnumerator enumerator = (result).GetEnumerator();
32 | return enumerator;
33 | }
34 |
35 | IEnumerator IEnumerable.GetEnumerator()
36 | {
37 | throw new NotImplementedException();
38 | }
39 |
40 | Type IQueryable.ElementType
41 | {
42 | get { return typeof (T); }
43 | }
44 |
45 | Expression IQueryable.Expression
46 | {
47 | get { return expression; }
48 | }
49 |
50 | public IQueryProvider Provider
51 | {
52 | get { return queryProvider; }
53 | }
54 |
55 | #endregion
56 | }
57 | }
--------------------------------------------------------------------------------
/Source/LinqToSqlXml/Linq/SqlServer/OrderByBuilder.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace LinqToSqlXml.Linq.SqlServer
7 | {
8 | public class OrderByBuilder
9 | {
10 | private readonly Stack paths = new Stack();
11 | private string CurrentPath
12 | {
13 | get { return paths.Peek(); }
14 | }
15 | private string GetFreeVariable()
16 | {
17 | int index = paths.Count;
18 | return "$" + ((char)(64 + index));
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Source/LinqToSqlXml/Linq/SqlServer/PredicateBuilder.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Linq.Expressions;
6 |
7 | namespace LinqToSqlXml.SqlServer
8 | {
9 | public class PredicateBuilder
10 | {
11 | private readonly Stack paths = new Stack();
12 | private string CurrentPath
13 | {
14 | get { return paths.Peek(); }
15 | }
16 | private string GetFreeVariable()
17 | {
18 | int index = paths.Count;
19 | return "$" + ((char)(64 + index));
20 | }
21 |
22 | public string TranslateToWhere(MethodCallExpression node)
23 | {
24 | paths.Push("");
25 |
26 | if (node.Arguments.Count != 2)
27 | throw new NotSupportedException("Unknown Where call");
28 |
29 | Expression predicate = node.Arguments[1];
30 | var x = predicate as UnaryExpression;
31 | Expression operand = x.Operand;
32 |
33 | var lambdaExpression = operand as LambdaExpression;
34 | var result = BuildPredicate(lambdaExpression.Body);
35 |
36 | paths.Pop();
37 |
38 | return result;
39 | }
40 |
41 | private string BuildPredicate(Expression expression)
42 | {
43 | switch (expression.NodeType)
44 | {
45 | case ExpressionType.Call:
46 | return BuildPredicateCall(expression);
47 | case ExpressionType.Convert:
48 | return BuildPredicateConvert(expression);
49 | case ExpressionType.Constant:
50 | return BuildPredicateConstant(expression);
51 | case ExpressionType.MemberAccess:
52 | return BuildPredicateMemberAccess(expression);
53 | case ExpressionType.TypeIs:
54 | return BuildPredicateTypeIs(expression);
55 | case ExpressionType.AndAlso:
56 | case ExpressionType.OrElse:
57 | case ExpressionType.NotEqual:
58 | case ExpressionType.LessThan:
59 | case ExpressionType.LessThanOrEqual:
60 | case ExpressionType.GreaterThan:
61 | case ExpressionType.GreaterThanOrEqual:
62 | case ExpressionType.Equal:
63 | case ExpressionType.Add:
64 | case ExpressionType.Subtract:
65 | case ExpressionType.Multiply:
66 | case ExpressionType.Divide:
67 |
68 | return BuildPredicateBinaryExpression(expression);
69 | default:
70 | throw new NotSupportedException("Unknown expression type");
71 | }
72 | }
73 |
74 | private string BuildPredicateCall(Expression expression)
75 | {
76 | var methodCallExpression = expression as MethodCallExpression;
77 |
78 | if (methodCallExpression.Method.DeclaringType == typeof(Enumerable) ||
79 | methodCallExpression.Method.DeclaringType == typeof(Queryable))
80 | {
81 | switch (methodCallExpression.Method.Name)
82 | {
83 | case "Any":
84 | return BuildAnyPredicate(methodCallExpression);
85 | case "Sum":
86 | case "Min":
87 | case "Max":
88 | case "Average":
89 | return BuildAggregatePredicate(methodCallExpression,
90 | XQueryMapping.Functions[methodCallExpression.Method.Name]);
91 | default:
92 | break;
93 | }
94 | }
95 |
96 | throw new NotSupportedException("Unknown method");
97 | }
98 |
99 | private string BuildAnyPredicate(MethodCallExpression methodCallExpression)
100 | {
101 | string rootPath = BuildPredicate(methodCallExpression.Arguments[0]);
102 | var lambda = methodCallExpression.Arguments[1] as LambdaExpression;
103 | Expression body = lambda.Body;
104 | string part = BuildPredicate(body);
105 | string propertyPath = BuildPredicate(methodCallExpression.Arguments[0]);
106 | string predicate = string.Format("{0}[{1}]", propertyPath, part);
107 | return predicate;
108 | }
109 |
110 | private string BuildAggregatePredicate(MethodCallExpression methodCallExpression, string functionName)
111 | {
112 | string propertyPath = BuildPredicate(methodCallExpression.Arguments[0]);
113 | var lambda = methodCallExpression.Arguments[1] as LambdaExpression;
114 | Expression body = lambda.Body;
115 | string variable = GetFreeVariable();
116 | paths.Push(variable + "/");
117 | string part = BuildPredicate(body);
118 | paths.Pop();
119 | string predicate = string.Format("{0}( for {1} in {2}/element return {3})", functionName, variable,
120 | propertyPath,
121 | part);
122 | return predicate;
123 | }
124 |
125 |
126 |
127 | private string BuildPredicateConvert(Expression expression)
128 | {
129 | var convertExpression = expression as UnaryExpression;
130 | return BuildPredicate(convertExpression.Operand);
131 | }
132 |
133 | private string BuildPredicateBinaryExpression(Expression expression)
134 | {
135 | var binaryExpression = expression as BinaryExpression;
136 | string op = XQueryMapping.Operators[expression.NodeType];
137 | string left = BuildPredicate(binaryExpression.Left);
138 |
139 |
140 | var rightAsUnary = binaryExpression.Right as UnaryExpression;
141 | ConstantExpression rightAsConstant = rightAsUnary != null
142 | ? rightAsUnary.Operand as ConstantExpression
143 | : null;
144 | if (rightAsConstant != null && rightAsConstant.Value == null)
145 | {
146 | return string.Format("{0}[@type{1}\"null\"]", left, op);
147 | }
148 | else
149 | {
150 | string right = BuildPredicate(binaryExpression.Right);
151 | return string.Format("({0} {1} {2})", left, op, right);
152 | }
153 | }
154 |
155 | private string BuildPredicateTypeIs(Expression expression)
156 | {
157 | var typeBinaryExpression = expression as TypeBinaryExpression;
158 | string left = BuildPredicate(typeBinaryExpression.Expression);
159 | string typeName = typeBinaryExpression.TypeOperand.SerializedName();
160 |
161 | //check if type attrib equals typename OR if typename exists in metadata type array
162 | string query = string.Format("{0}[(@type=\"{1}\" or __meta[type[. = \"{1}\"]])]",left, typeName);
163 | return query;
164 | }
165 |
166 | private string BuildPredicateMemberAccess(Expression expression)
167 | {
168 | var memberExpression = expression as MemberExpression;
169 | string memberName = memberExpression.Member.Name;
170 |
171 | if (memberExpression.Member.DeclaringType == typeof(DateTime))
172 | {
173 | if (memberName == "Now")
174 | return XQueryMapping.BuildLiteral(DateTime.Now);
175 | }
176 |
177 | return string.Format("({0})[1]",BuildPredicateMemberAccessReq(expression));
178 | }
179 |
180 | private string BuildPredicateMemberAccessReq(Expression expression)
181 | {
182 | var memberExpression = expression as MemberExpression;
183 | string memberName = memberExpression.Member.Name;
184 |
185 | string current = string.Format("{0}", memberName);
186 | string prev = "";
187 | if (memberExpression.Expression is MemberExpression)
188 | prev = BuildPredicateMemberAccessReq(memberExpression.Expression) +"/" ;
189 | else
190 | prev = CurrentPath;
191 |
192 | return prev + current;
193 | }
194 |
195 | private string BuildPredicateConstant(Expression expression)
196 | {
197 | var constantExpression = expression as ConstantExpression;
198 | object value = constantExpression.Value;
199 | return XQueryMapping.BuildLiteral(value);
200 | }
201 |
202 | public string TranslateToOfType(MethodCallExpression node)
203 | {
204 | Type ofType = node.Method.GetGenericArguments()[0] as Type;
205 |
206 | string typeName = ofType.SerializedName();
207 |
208 | //check if type attrib equals typename OR if typename exists in metadata type array
209 | string query = string.Format("(@type=\"{0}\" or __meta[type[. = \"{0}\"]])", typeName);
210 | return query;
211 | }
212 | }
213 | }
214 |
--------------------------------------------------------------------------------
/Source/LinqToSqlXml/Linq/SqlServer/QueryBuilder.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Linq.Expressions;
6 | using System.Xml.Linq;
7 |
8 | namespace LinqToSqlXml.SqlServer
9 | {
10 | public partial class QueryBuilder : ExpressionVisitor
11 | {
12 | private SelectorBuilder selectorBuilder = new SelectorBuilder();
13 | private PredicateBuilder predicateBuilder = new PredicateBuilder();
14 | public string limit = "";
15 | public string orderby = "";
16 | private string wherepredicate = "";
17 | public string documentDataSelector = "DocumentData"; //just the column by default
18 |
19 | public QueryBuilder()
20 | {
21 | }
22 |
23 | public string Where
24 | {
25 | get
26 | {
27 | if (wherepredicate != "")
28 | return string.Format(" and (documentdata.exist('/document[{0}]')) = 1", wherepredicate);
29 | else
30 | return "";
31 | }
32 | }
33 |
34 | protected override Expression VisitMethodCall(MethodCallExpression node)
35 | {
36 | if (node.Method.DeclaringType == typeof(Queryable))
37 | {
38 | switch (node.Method.Name)
39 | {
40 | case "OrderBy":
41 | TranslateToOrderBy(node);
42 | break;
43 | case "Where":
44 | if (wherepredicate != "")
45 | wherepredicate += " and " + Environment.NewLine;
46 | wherepredicate += predicateBuilder.TranslateToWhere(node);
47 | break;
48 | case "OfType":
49 | if (wherepredicate != "")
50 | wherepredicate += " and " + Environment.NewLine;
51 | wherepredicate += predicateBuilder.TranslateToOfType(node);
52 | break;
53 | case "Take":
54 | TranslateToTake(node);
55 | break;
56 | case "Select":
57 | documentDataSelector = selectorBuilder.TranslateToProjection(node);
58 | break;
59 | default:
60 | throw new NotSupportedException(string.Format("Method {0} is not yet supported",
61 | node.Method.Name));
62 | }
63 | }
64 | return base.VisitMethodCall(node);
65 | }
66 |
67 | private void TranslateToTake(MethodCallExpression node)
68 | {
69 | limit = string.Format("top {0}", (int)(node.Arguments[1] as ConstantExpression).Value);
70 | }
71 |
72 | //move to predicate builder and fix it
73 |
74 |
75 | private void TranslateToOrderBy(MethodCallExpression node)
76 | {
77 | if (node.Arguments.Count != 2)
78 | return;
79 |
80 | Expression path = node.Arguments[1];
81 | var x = path as UnaryExpression;
82 | Expression operand = x.Operand;
83 |
84 | if (orderby != "")
85 | orderby += " , ";
86 | else
87 | orderby = "order by ";
88 |
89 | var lambdaExpression = operand as LambdaExpression;
90 | orderby += BuildOrderByStart(lambdaExpression.Body);
91 | }
92 |
93 | private string BuildOrderByStart(Expression expression)
94 | {
95 | string path = BuildOrderBy(expression);
96 | return string.Format("documentdata.value('(({0})[1])','nvarchar(MAX)')", path);
97 | }
98 |
99 | private string BuildOrderBy(Expression expression)
100 | {
101 | var memberExpression = expression as MemberExpression;
102 | if (memberExpression != null)
103 | {
104 | string memberName = memberExpression.Member.Name;
105 | string current = string.Format("/{0}", memberName);
106 | string prev = "";
107 | // if (memberExpression.Expression is MemberExpression)
108 | // prev = BuildPredicate(memberExpression.Expression);
109 |
110 | return prev + current;
111 | }
112 | throw new NotSupportedException("Unknown order by clause");
113 | }
114 | }
115 | }
--------------------------------------------------------------------------------
/Source/LinqToSqlXml/Linq/SqlServer/QueryProvider.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Linq.Expressions;
5 | using System.Xml.Linq;
6 | using LinqToSqlXml.SqlServer;
7 |
8 | namespace LinqToSqlXml
9 | {
10 | public class SqlServerQueryProvider : IQueryProvider
11 | {
12 | private readonly DocumentCollection documentCollection;
13 |
14 | public SqlServerQueryProvider(DocumentCollection documentCollection)
15 | {
16 | this.documentCollection = documentCollection;
17 | }
18 |
19 | #region IQueryProvider Members
20 |
21 | public IQueryable CreateQuery(Expression expression)
22 | {
23 | return new DocumentQuery(this, expression);
24 | }
25 |
26 | public IQueryable CreateQuery(Expression expression)
27 | {
28 | throw new NotImplementedException();
29 | }
30 |
31 | public TResult Execute(Expression expression)
32 | {
33 | return default(TResult);
34 | }
35 |
36 | public object Execute(Expression expression)
37 | {
38 | throw new NotImplementedException();
39 | }
40 |
41 | #endregion
42 |
43 | private static IEnumerable DocumentEnumerator(IEnumerable documents)
44 | {
45 | return documents
46 | .Select(document => document.DocumentData)
47 | .Select(xml => (TResult) DocumentDeserializer.Deserialize(xml,typeof(TResult)))
48 | .Where(result => result != null);
49 | }
50 |
51 | public IEnumerable ExecuteQuery(Expression expression)
52 | {
53 | var queryBuilder = new QueryBuilder();
54 | queryBuilder.Visit(expression);
55 |
56 | var sql = string.Format(@"
57 | select {0} Id,{1}
58 | from Documents
59 | where CollectionName = '{2}'
60 | {3}
61 | {4}",
62 | queryBuilder.limit,
63 | queryBuilder.documentDataSelector,
64 | documentCollection.CollectionName,
65 | queryBuilder.Where,
66 | queryBuilder.orderby);
67 |
68 | IEnumerable result = documentCollection.ExecuteQuery(sql);
69 | return DocumentEnumerator(result);
70 | }
71 | }
72 | }
--------------------------------------------------------------------------------
/Source/LinqToSqlXml/Linq/SqlServer/SelectorBuilder.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Linq.Expressions;
6 | using System.Collections;
7 | using System.Xml.Linq;
8 |
9 | namespace LinqToSqlXml.SqlServer
10 | {
11 | public class SelectorBuilder
12 | {
13 | private readonly Stack paths = new Stack();
14 | private string CurrentPath
15 | {
16 | get { return paths.Peek(); }
17 | }
18 | private string GetFreeVariable()
19 | {
20 | int index = paths.Count;
21 | return "$" + ((char)(64 + index));
22 | }
23 |
24 | public string TranslateToProjection(MethodCallExpression node)
25 | {
26 | paths.Push("document/");
27 | var unary = node.Arguments[1] as UnaryExpression;
28 | var lambda = unary.Operand as LambdaExpression;
29 | var selector1 = lambda.Body as NewExpression;
30 | var selector2 = lambda.Body as MemberInitExpression;
31 |
32 | var selector = "";
33 | var columns = new string[2];
34 | if (selector1 != null)
35 | {
36 | string[] members = selector1.Members.Select(m => m.Name).ToArray();
37 | selector = BuildSelectors1(selector1, members, "document");
38 | }
39 | if (selector2 != null)
40 | {
41 | string[] members = selector2.Bindings.Select(m => m.Member.Name).ToArray();
42 | selector = BuildSelectors2(selector2, members, "document");
43 | }
44 |
45 | var x = XElement.Parse(selector);
46 | selector = x.ToString();
47 |
48 | var result = "DocumentData.query('" + selector + "') as DocumentData";
49 |
50 | paths.Pop();
51 | return result;
52 | }
53 |
54 | private string BuildSelectors2(MemberInitExpression selector, string[] members, string owner)
55 | {
56 |
57 | string projection = "";
58 | for (int i = 0; i < members.Length; i++)
59 | {
60 | string member = members[i];
61 |
62 | var a = selector.Bindings[i] as MemberAssignment;
63 | Expression expression = a.Expression;
64 | string propertyContent = BuildSelector(expression);
65 | string propertyType = DocumentSerializer.GetSerializedTypeName(expression.Type);
66 |
67 | string propertyProjection = string.Format("<{0} type=\"{1}\">{{{2}}}{0}>", member, propertyType,
68 | propertyContent);
69 | projection += propertyProjection;
70 | }
71 |
72 | return string.Format("<{0} type=\"{1}\">{2}{0}>", owner, selector.NewExpression.Type.SerializedName(), projection);
73 | }
74 |
75 | private string BuildSelectors1(NewExpression selector, string[] members, string owner)
76 | {
77 | string projection = "";
78 | for (int i = 0; i < members.Length; i++)
79 | {
80 | string member = members[i];
81 | Expression expression = selector.Arguments[i];
82 | string propertyContent = BuildSelector(expression);
83 | string propertyType = DocumentSerializer.GetSerializedTypeName(expression.Type);
84 |
85 | string propertyProjection = string.Format("<{0} type=\"{1}\">{{{2}}}{0}>", member, propertyType,
86 | propertyContent);
87 | projection += propertyProjection;
88 | }
89 | return string.Format("<{0} type=\"dynamic\">{1}{0}>", owner, projection);
90 | }
91 |
92 | private string BuildSelector(Expression expression)
93 | {
94 | switch (expression.NodeType)
95 | {
96 | case ExpressionType.Call:
97 | return BuildSelectorCall(expression);
98 | case ExpressionType.Convert:
99 | return BuildSelectorConvert(expression);
100 | case ExpressionType.Constant:
101 | return BuildSelectorConstant(expression);
102 | case ExpressionType.MemberAccess:
103 | return BuildSelectorMemberAccess(expression);
104 | case ExpressionType.TypeIs:
105 | return BuildSelectorTypeIs(expression);
106 | case ExpressionType.AndAlso:
107 | case ExpressionType.OrElse:
108 | case ExpressionType.NotEqual:
109 | case ExpressionType.LessThan:
110 | case ExpressionType.LessThanOrEqual:
111 | case ExpressionType.GreaterThan:
112 | case ExpressionType.GreaterThanOrEqual:
113 | case ExpressionType.Equal:
114 | case ExpressionType.Add:
115 | case ExpressionType.Subtract:
116 | case ExpressionType.Multiply:
117 | case ExpressionType.Divide:
118 | return BuildSelectorBinaryExpression(expression);
119 | default:
120 | throw new NotSupportedException("Unknown expression type");
121 | }
122 | }
123 |
124 | private string BuildSelectorBinaryExpression(Expression expression)
125 | {
126 | var binaryExpression = expression as BinaryExpression;
127 | string op = XQueryMapping.Operators[expression.NodeType];
128 | string left = BuildSelector(binaryExpression.Left);
129 |
130 | var rightAsUnary = binaryExpression.Right as UnaryExpression;
131 | ConstantExpression rightAsConstant = rightAsUnary != null
132 | ? rightAsUnary.Operand as ConstantExpression
133 | : null;
134 | if (rightAsConstant != null && rightAsConstant.Value == null)
135 | {
136 | return string.Format("{0}[@type{1}\"null\"]", left, op);
137 | }
138 |
139 | string right = BuildSelector(binaryExpression.Right);
140 | return string.Format("({0} {1} {2})", left, op, right);
141 | }
142 |
143 | private string BuildSelectorTypeIs(Expression expression)
144 | {
145 | throw new NotImplementedException();
146 | }
147 |
148 | private string BuildSelectorMemberAccess(Expression expression)
149 | {
150 | var memberExpression = expression as MemberExpression;
151 | string memberName = memberExpression.Member.Name;
152 |
153 | if (memberExpression.Member.DeclaringType == typeof(DateTime))
154 | {
155 | if (memberName == "Now")
156 | return string.Format("xs:dateTime(\"{0}\")", DocumentSerializer.SerializeDateTime(DateTime.Now));
157 | }
158 |
159 | string result = BuildSelectorMemberAccessRec(expression);
160 | result = string.Format("({0})[1]", result);
161 |
162 | if (expression.Type == typeof(string))
163 | return string.Format("xs:string({0})", result);
164 |
165 | if (expression.Type == typeof(Guid))
166 | return string.Format("xs:string({0})", result);
167 |
168 | if (expression.Type == typeof(int))
169 | return string.Format("xs:int({0})", result);
170 |
171 | if (expression.Type == typeof(decimal))
172 | return string.Format("xs:decimal({0})", result);
173 |
174 | if (expression.Type == typeof(double))
175 | return string.Format("xs:double({0})", result);
176 |
177 | if (typeof(IEnumerable).IsAssignableFrom(expression.Type))
178 | return string.Format("{0}/element", result);
179 |
180 | return result;
181 | }
182 |
183 | private string BuildSelectorMemberAccessRec(Expression expression)
184 | {
185 | var memberExpression = expression as MemberExpression;
186 | string memberName = memberExpression.Member.Name;
187 |
188 | string current = string.Format("{0}", memberName);
189 | string prev = "";
190 | if (memberExpression.Expression is MemberExpression)
191 | prev = BuildSelectorMemberAccessRec(memberExpression.Expression) + "/";
192 | else
193 | {
194 | return CurrentPath + current;
195 | }
196 |
197 | return prev + current;
198 | }
199 |
200 | private string BuildSelectorConstant(Expression expression)
201 | {
202 | throw new NotImplementedException();
203 | }
204 |
205 | private string BuildSelectorConvert(Expression expression)
206 | {
207 | throw new NotImplementedException();
208 | }
209 |
210 | private string BuildSelectorCall(Expression expression)
211 | {
212 | var methodCallExpression = expression as MethodCallExpression;
213 |
214 | if (methodCallExpression.Method.DeclaringType == typeof(Enumerable) ||
215 | methodCallExpression.Method.DeclaringType == typeof(Queryable))
216 | {
217 | switch (methodCallExpression.Method.Name)
218 | {
219 | case "Select":
220 | return BuildSelectorProjection(methodCallExpression);
221 | case "Any":
222 | return BuildSelectorAny(methodCallExpression);
223 | case "Sum":
224 | case "Min":
225 | case "Max":
226 | case "Average":
227 | return BuildSelectorAggregate(methodCallExpression,
228 | XQueryMapping.Functions[methodCallExpression.Method.Name]);
229 | default:
230 | break;
231 | }
232 | }
233 |
234 | throw new NotSupportedException("Unknown method");
235 | }
236 |
237 | private string BuildSelectorProjection(MethodCallExpression methodCallExpression)
238 | {
239 | string propertyPath = BuildSelector(methodCallExpression.Arguments[0]);
240 | var variable = GetFreeVariable();
241 | paths.Push(variable + "/");
242 | var lambda = methodCallExpression.Arguments[1] as LambdaExpression;
243 |
244 | var selector1 = lambda.Body as NewExpression;
245 | var selector2 = lambda.Body as MemberInitExpression;
246 | string result = "";
247 | if (selector1 != null)
248 | {
249 | string[] members = selector1.Members.Select(m => m.Name).ToArray();
250 | result = BuildSelectors1(selector1, members, "element");
251 | }
252 |
253 | if (selector2 != null)
254 | {
255 | string[] members = selector2.Bindings.Select(m => m.Member.Name).ToArray();
256 | result = BuildSelectors2(selector2, members, "element");
257 | }
258 | paths.Pop();
259 | return string.Format("for {0} in {1} return {2}", variable, propertyPath, result);
260 | }
261 |
262 | private string BuildSelectorAny(MethodCallExpression methodCallExpression)
263 | {
264 | throw new NotImplementedException();
265 | }
266 |
267 | private string BuildSelectorAggregate(MethodCallExpression methodCallExpression, string functionName)
268 | {
269 | string propertyPath = BuildSelector(methodCallExpression.Arguments[0]);
270 | var lambda = methodCallExpression.Arguments[1] as LambdaExpression;
271 | Expression body = lambda.Body;
272 | string freeVariable = GetFreeVariable();
273 | paths.Push(freeVariable + "/");
274 | string part = BuildSelector(body);
275 | paths.Pop();
276 | string predicate = string.Format("{0}( for {1} in {2} return xs:decimal({3}))", functionName,
277 | freeVariable,
278 | propertyPath,
279 | part);
280 | return predicate;
281 | }
282 |
283 | }
284 | }
285 |
--------------------------------------------------------------------------------
/Source/LinqToSqlXml/Linq/SqlServer/XQueryMapping.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Linq.Expressions;
6 |
7 | namespace LinqToSqlXml.SqlServer
8 | {
9 | public static class XQueryMapping
10 | {
11 | public static readonly Dictionary Operators = new Dictionary
12 | {
13 | {ExpressionType.AndAlso, "and"},
14 | {ExpressionType.OrElse, "or"},
15 | {ExpressionType.NotEqual, "!="},
16 | {ExpressionType.LessThan, "<"},
17 | {ExpressionType.LessThanOrEqual, "<="},
18 | {ExpressionType.GreaterThan, ">"},
19 | {ExpressionType.GreaterThanOrEqual, ">="},
20 | {ExpressionType.Equal, "="},
21 | {ExpressionType.Add, "+"},
22 | {ExpressionType.Subtract, "-"},
23 | {ExpressionType.Divide, "/"},
24 | {ExpressionType.Multiply, "*"},
25 | };
26 |
27 | public static readonly Dictionary Functions = new Dictionary
28 | {
29 | {"Sum", "fn:sum"},
30 | {"Max", "fn:max"},
31 | {"Min", "fn:min"},
32 | {"Average", "fn:avg"},
33 | };
34 |
35 | public static readonly string xsTrue = "fn:true()";
36 | public static readonly string xsFalse = "fn:false()";
37 |
38 | public static string BuildLiteral(object value)
39 | {
40 | if (value is string)
41 | return "\"" + DocumentSerializer.SerializeString((string)value) + "\"";
42 | if (value is int)
43 | return string.Format("xs:int({0})", DocumentSerializer.SerializeDecimal((int)value));
44 | if (value is decimal)
45 | return string.Format("xs:decimal({0})", DocumentSerializer.SerializeDecimal((decimal)value));
46 | if (value is DateTime)
47 | return string.Format("xs:dateTime({0})", DocumentSerializer.SerializeDateTime((DateTime)value));
48 | if (value is bool)
49 | if ((bool)value)
50 | return XQueryMapping.xsTrue;
51 | else
52 | return XQueryMapping.xsFalse;
53 |
54 | return value.ToString();
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/Source/LinqToSqlXml/LinqToSqlXml.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | x86
6 | 8.0.30703
7 | 2.0
8 | {0B728524-A481-4450-B501-64699655A5DB}
9 | Library
10 | Properties
11 | LinqToSqlXml
12 | LinqToSqlXml
13 | v4.0
14 | Client
15 | 512
16 |
17 |
18 | x86
19 | true
20 | full
21 | false
22 | bin\Debug\
23 | DEBUG;TRACE
24 | prompt
25 | 4
26 |
27 |
28 | x86
29 | pdbonly
30 | true
31 | bin\Release\
32 | TRACE
33 | prompt
34 | 4
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 | True
55 | True
56 | DocumentDB.dbml
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 | True
68 | True
69 | Settings.settings
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 | MSLinqToSQLGenerator
81 | DocumentDB.designer.cs
82 | Designer
83 |
84 |
85 | SettingsSingleFileGenerator
86 | Settings.Designer.cs
87 |
88 |
89 |
90 |
91 | DocumentDB.dbml
92 |
93 |
94 |
95 |
102 |
--------------------------------------------------------------------------------
/Source/LinqToSqlXml/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 |
4 | // General Information about an assembly is controlled through the following
5 | // set of attributes. Change these attribute values to modify the information
6 | // associated with an assembly.
7 |
8 | [assembly: AssemblyTitle("LinqToSqlXml")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("Microsoft")]
12 | [assembly: AssemblyProduct("LinqToSqlXml")]
13 | [assembly: AssemblyCopyright("Copyright © Microsoft 2011")]
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 |
21 | [assembly: ComVisible(false)]
22 |
23 | // The following GUID is for the ID of the typelib if this project is exposed to COM
24 |
25 | [assembly: Guid("fe32cd68-34fc-4bb1-9e46-c192853df4ae")]
26 |
27 | // Version information for an assembly consists of the following four values:
28 | //
29 | // Major Version
30 | // Minor Version
31 | // Build Number
32 | // Revision
33 | //
34 | // You can specify all the values or you can default the Build and Revision Numbers
35 | // by using the '*' as shown below:
36 | // [assembly: AssemblyVersion("1.0.*")]
37 |
38 | [assembly: AssemblyVersion("1.0.0.0")]
39 | [assembly: AssemblyFileVersion("1.0.0.0")]
--------------------------------------------------------------------------------
/Source/LinqToSqlXml/Properties/Settings.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.1
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace LinqToSqlXml.Properties {
12 |
13 |
14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "10.0.0.0")]
16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
17 |
18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
19 |
20 | public static Settings Default {
21 | get {
22 | return defaultInstance;
23 | }
24 | }
25 |
26 | [global::System.Configuration.ApplicationScopedSettingAttribute()]
27 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
28 | [global::System.Configuration.SpecialSettingAttribute(global::System.Configuration.SpecialSetting.ConnectionString)]
29 | [global::System.Configuration.DefaultSettingValueAttribute("Data Source=localhost;Initial Catalog=DocumentDB;Integrated Security=True")]
30 | public string LinqToSqlXmlConnectionString {
31 | get {
32 | return ((string)(this["LinqToSqlXmlConnectionString"]));
33 | }
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Source/LinqToSqlXml/Properties/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | <?xml version="1.0" encoding="utf-16"?>
7 | <SerializableConnectionString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
8 | <ConnectionString>Data Source=localhost;Initial Catalog=DocumentDB;Integrated Security=True</ConnectionString>
9 | <ProviderName>System.Data.SqlClient</ProviderName>
10 | </SerializableConnectionString>
11 | Data Source=localhost;Initial Catalog=DocumentDB;Integrated Security=True
12 |
13 |
14 |
--------------------------------------------------------------------------------
/Source/LinqToSqlXml/Serialization/DocumentDeserializer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Reflection;
6 | using System.Xml;
7 | using System.Xml.Linq;
8 |
9 | namespace LinqToSqlXml
10 | {
11 | public static class DocumentDeserializer
12 | {
13 | public static object Deserialize(XElement xml,Type expectedType)
14 | {
15 | //todo fix
16 |
17 | // ReSharper disable PossibleNullReferenceException
18 | string typeName = xml.Attribute("type").Value;
19 | // ReSharper restore PossibleNullReferenceException
20 |
21 | string value = xml.Value;
22 | if (typeName == "string")
23 | return DeserializeString(value);
24 | if (typeName == "guid")
25 | return DeserializeGuid(value);
26 | if (typeName == "bool")
27 | return DeserializeBool(value);
28 | if (typeName == "int")
29 | return DeserializeInt(value);
30 | if (typeName == "double")
31 | return DeserializeDouble(value);
32 | if (typeName == "decimal")
33 | return DeserializeDecimal(value);
34 | if (typeName == "datetime")
35 | return DeserializeDateTime(value);
36 | if (typeName == "null")
37 | return null;
38 | if (typeName == "collection")
39 | {
40 | var elementExpectedType = typeof(object);
41 | if (expectedType.IsGenericType)
42 | elementExpectedType = expectedType.GetGenericArguments()[0];
43 |
44 | return xml.Elements().Select(e => Deserialize(e, elementExpectedType)).ToList();
45 | }
46 |
47 |
48 | Type type = Type.GetType(typeName);
49 | if (typeName == "dynamic")
50 | type = expectedType;
51 |
52 | if (type == null)
53 | return null;
54 |
55 | if (type.GetConstructor(new Type[] { }) == null)
56 | {
57 | //deserialize with ctor args
58 |
59 | var ctor = type.GetConstructors().First();
60 | var ctorArgs = ctor.GetParameters();
61 | var values = new object[ctorArgs.Length];
62 | int i=0;
63 | foreach (XElement xProperty in xml.Elements().Where(e => !e.Name.LocalName.StartsWith("__")))
64 | {
65 | ParameterInfo parameter = ctorArgs.Where(p => p.Name == xProperty.Name).First();
66 | var expectedPropertyType = parameter.ParameterType;
67 |
68 | object parameterValue = Deserialize(xProperty, expectedPropertyType);
69 | if (parameterValue is List