├── .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}}}", member, propertyType, 68 | propertyContent); 69 | projection += propertyProjection; 70 | } 71 | 72 | return string.Format("<{0} type=\"{1}\">{2}", 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}}}", member, propertyType, 86 | propertyContent); 87 | projection += propertyProjection; 88 | } 89 | return string.Format("<{0} type=\"dynamic\">{1}", 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) 70 | { 71 | parameterValue = DeserializeList2(parameterValue, expectedPropertyType); 72 | } 73 | values[i] = parameterValue; 74 | i++; 75 | //values[parameter.Name] = parameterValue; 76 | } 77 | 78 | object result = Activator.CreateInstance(type, values); 79 | 80 | return result; 81 | } 82 | else 83 | { 84 | //deserialize with default ctor 85 | object result = Activator.CreateInstance(type, null); 86 | foreach (XElement xProperty in xml.Elements().Where(e => !e.Name.LocalName.StartsWith("__"))) 87 | { 88 | PropertyInfo property = type.GetProperty(xProperty.Name.LocalName); 89 | if (property.CanWrite == false) 90 | continue; 91 | var expectedPropertyType = property.PropertyType; 92 | 93 | object propertyValue = Deserialize(xProperty, expectedPropertyType); 94 | if (propertyValue is List) 95 | { 96 | DeserializeList(value, property, propertyValue, result); 97 | } 98 | else 99 | { 100 | property.SetValue(result, propertyValue, null); 101 | } 102 | } 103 | return result; 104 | } 105 | } 106 | 107 | private static void DeserializeList(string value, PropertyInfo property, object propertyValue, object result) 108 | { 109 | var list = (List) propertyValue; 110 | Type collectionType = property.PropertyType; 111 | if (collectionType.IsInterface && collectionType.IsGenericType) 112 | { 113 | Type elementType = collectionType.GetGenericArguments().First(); 114 | Type concreteType = typeof (List<>).MakeGenericType(elementType); 115 | var concreteList = (IList) Activator.CreateInstance(concreteType); 116 | foreach (object item in list) 117 | concreteList.Add(item); 118 | property.SetValue(result, concreteList, null); 119 | } 120 | else if (typeof (Array).IsAssignableFrom(collectionType)) 121 | { 122 | int length = list.Count; 123 | Array arr = null; 124 | for (int i = 0; i < length; i++) 125 | { 126 | arr.SetValue(list[i], i); 127 | } 128 | property.SetValue(result, arr, null); 129 | } 130 | else if (collectionType == typeof (object)) 131 | { 132 | property.SetValue(result, value, null); 133 | } 134 | } 135 | 136 | private static object DeserializeList2(object propertyValue, Type collectionType) 137 | { 138 | var list = (List)propertyValue; 139 | 140 | if (collectionType.IsInterface && collectionType.IsGenericType) 141 | { 142 | Type elementType = collectionType.GetGenericArguments().First(); 143 | Type concreteType = typeof(List<>).MakeGenericType(elementType); 144 | var concreteList = (IList)Activator.CreateInstance(concreteType); 145 | foreach (object item in list) 146 | concreteList.Add(item); 147 | 148 | return concreteList; 149 | } 150 | else if (typeof(Array).IsAssignableFrom(collectionType)) 151 | { 152 | int length = list.Count; 153 | Array arr = null; 154 | for (int i = 0; i < length; i++) 155 | { 156 | arr.SetValue(list[i], i); 157 | } 158 | return arr; 159 | } 160 | else if (collectionType == typeof(object)) 161 | { 162 | throw new Exception(); 163 | } 164 | 165 | throw new NotSupportedException(""); 166 | } 167 | 168 | private static object DeserializeDateTime(string propertyString) 169 | { 170 | return XmlConvert.ToDateTime(propertyString, XmlDateTimeSerializationMode.Local); 171 | } 172 | 173 | private static object DeserializeDecimal(string propertyString) 174 | { 175 | return XmlConvert.ToDecimal(propertyString); 176 | } 177 | 178 | private static object DeserializeDouble(string propertyString) 179 | { 180 | return XmlConvert.ToDouble(propertyString); 181 | } 182 | 183 | private static object DeserializeInt(string propertyString) 184 | { 185 | return XmlConvert.ToInt32(propertyString); 186 | } 187 | 188 | private static object DeserializeBool(string propertyString) 189 | { 190 | return XmlConvert.ToBoolean(propertyString); 191 | } 192 | 193 | private static object DeserializeGuid(string propertyString) 194 | { 195 | return XmlConvert.ToGuid(propertyString); 196 | } 197 | 198 | private static object DeserializeString(string propertyString) 199 | { 200 | return propertyString; 201 | } 202 | } 203 | } -------------------------------------------------------------------------------- /Source/LinqToSqlXml/Serialization/DocumentSerializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Linq; 4 | using System.Reflection; 5 | using System.Xml; 6 | using System.Xml.Linq; 7 | 8 | namespace LinqToSqlXml 9 | { 10 | public static class DocumentSerializer 11 | { 12 | public static XElement Serialize(object item) 13 | { 14 | var root = new XElement("document"); 15 | Serialize(item, root); 16 | return root; 17 | } 18 | 19 | 20 | private static void Serialize(object value, XElement ownerTag) 21 | { 22 | if (value == null) 23 | { 24 | ownerTag.Add(new XAttribute("type", "null")); 25 | return; 26 | } 27 | if (value is string) 28 | { 29 | ownerTag.Add(new XAttribute("type", "string")); 30 | ownerTag.Add(SerializeString((string) value)); 31 | return; 32 | } 33 | if (value is Guid || value is Guid?) 34 | { 35 | ownerTag.Add(new XAttribute("type", "guid")); 36 | ownerTag.Add(SerializeGuid((Guid) value)); 37 | return; 38 | } 39 | if (value is bool || value is bool?) 40 | { 41 | ownerTag.Add(new XAttribute("type", "bool")); 42 | ownerTag.Add(SerializeBool((bool) value)); 43 | return; 44 | } 45 | if (value is decimal || value is decimal?) 46 | { 47 | ownerTag.Add(new XAttribute("type", "decimal")); 48 | ownerTag.Add(SerializeDecimal((decimal) value)); 49 | return; 50 | } 51 | if (value is double || value is double?) 52 | { 53 | ownerTag.Add(new XAttribute("type", "double")); 54 | ownerTag.Add(SerializeDouble((double) value)); 55 | return; 56 | } 57 | if (value is int || value is int?) 58 | { 59 | ownerTag.Add(new XAttribute("type", "int")); 60 | ownerTag.Add(SerializeInt((int) value)); 61 | return; 62 | } 63 | if (value is DateTime || value is DateTime?) 64 | { 65 | ownerTag.Add(new XAttribute("type", "datetime")); 66 | ownerTag.Add(SerializeDateTime((DateTime) value)); 67 | return; 68 | } 69 | if (value is IEnumerable) 70 | { 71 | ownerTag.Add(new XAttribute("type", "collection")); 72 | foreach (object childValue in (IList) value) 73 | { 74 | var collectionElement = new XElement("element"); 75 | Serialize(childValue, collectionElement); 76 | ownerTag.Add(collectionElement); 77 | } 78 | return; 79 | } 80 | if (value is Enum) 81 | { 82 | // ReSharper disable PossibleInvalidCastException 83 | var intValue = (int) value; 84 | // ReSharper restore PossibleInvalidCastException 85 | ownerTag.Add(new XAttribute("type", "int")); 86 | ownerTag.Add(SerializeInt(intValue)); 87 | return; 88 | } 89 | Type itemType = value.GetType(); 90 | ownerTag.Add(new XAttribute("type", itemType.SerializedName())); 91 | var metaTags = new XElement("__meta"); 92 | 93 | Type currentType = itemType; 94 | while (currentType != null && currentType != typeof (object)) 95 | { 96 | if (currentType != itemType) 97 | { 98 | var typeTag = new XElement("type", currentType.SerializedName()); 99 | metaTags.Add(typeTag); 100 | } 101 | 102 | Type[] interfaces = currentType.GetInterfaces(); 103 | foreach (Type interfaceType in interfaces) 104 | { 105 | var interfaceTag = new XElement("type", interfaceType.SerializedName()); 106 | metaTags.Add(interfaceTag); 107 | } 108 | currentType = currentType.BaseType; 109 | } 110 | 111 | //only add metadata if metadata exists 112 | if (metaTags.Elements().Count() > 0) 113 | ownerTag.Add(metaTags); 114 | 115 | PropertyInfo[] properties = itemType.GetProperties(); 116 | foreach (PropertyInfo property in properties) 117 | { 118 | var propertyTag = new XElement(property.Name); 119 | object propertyValue = property.GetValue(value, null); 120 | 121 | Serialize(propertyValue, propertyTag); 122 | ownerTag.Add(propertyTag); 123 | } 124 | } 125 | 126 | public static string GetSerializedTypeName(Type type) 127 | { 128 | if (type == typeof (bool)) 129 | return "bool"; 130 | 131 | if (type == typeof (int)) 132 | return "int"; 133 | 134 | if (type == typeof (double)) 135 | return "double"; 136 | 137 | if (type == typeof (decimal)) 138 | return "decimal"; 139 | 140 | if (type == typeof (DateTime)) 141 | return "datetime"; 142 | 143 | if (type == typeof (string)) 144 | return "string"; 145 | 146 | if (type == typeof (Guid)) 147 | return "guid"; 148 | 149 | if (typeof (IEnumerable).IsAssignableFrom(type)) 150 | return "collection"; 151 | 152 | return type.SerializedName(); 153 | } 154 | 155 | public static string SerializeDateTime(DateTime value) 156 | { 157 | return XmlConvert.ToString(value, XmlDateTimeSerializationMode.Local); 158 | } 159 | 160 | public static string SerializeInt(int value) 161 | { 162 | return XmlConvert.ToString(value); 163 | } 164 | 165 | public static string SerializeDouble(double value) 166 | { 167 | return XmlConvert.ToString(value); 168 | } 169 | 170 | public static string SerializeDecimal(decimal value) 171 | { 172 | return XmlConvert.ToString(value); 173 | } 174 | 175 | public static string SerializeBool(bool value) 176 | { 177 | return XmlConvert.ToString(value); 178 | } 179 | 180 | public static string SerializeGuid(Guid value) 181 | { 182 | return XmlConvert.ToString(value); 183 | } 184 | 185 | public static string SerializeString(string value) 186 | { 187 | return value; 188 | } 189 | } 190 | 191 | public static class TypeExtensions 192 | { 193 | public static string SerializedName(this Type self) 194 | { 195 | return string.Format("{0}, {1}", self.FullName, 196 | self.Assembly.FullName.Substring(0, self.Assembly.FullName.IndexOf(","))); 197 | } 198 | } 199 | } -------------------------------------------------------------------------------- /Source/LinqToSqlXml/app.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2011 Roger Alsing, 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. --------------------------------------------------------------------------------