/PetaPoco.Tests/MultiPocoTests.cs

http://github.com/toptensoftware/PetaPoco · C# · 314 lines · 230 code · 49 blank · 35 comment · 8 complexity · ef6b20780b17bdb03a88c3364f4a60e6 MD5 · raw file

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using PetaTest;
  6. using PetaPoco;
  7. namespace PetaPoco.Tests
  8. {
  9. public class MultiPocoTests
  10. {
  11. [TableName("posts")]
  12. [PrimaryKey("id")]
  13. public class post
  14. {
  15. public long id { get; set; }
  16. public string title { get; set; }
  17. public long author { get; set; }
  18. [ResultColumn] public author author_obj { get; set; }
  19. }
  20. [TableName("authors")]
  21. [PrimaryKey("id")]
  22. public class author
  23. {
  24. public long id { get; set; }
  25. public string name { get; set; }
  26. [ResultColumn]
  27. public List<post> posts { get; set; }
  28. }
  29. public MultiPocoTests()
  30. {
  31. _connectionStringName = "mysql";
  32. }
  33. string _connectionStringName;
  34. Database db;
  35. [TestFixtureSetUp]
  36. public void CreateDB()
  37. {
  38. db = new Database(_connectionStringName);
  39. db.Execute(@"
  40. DROP TABLE IF EXISTS posts;
  41. DROP TABLE IF EXISTS authors;
  42. CREATE TABLE posts (
  43. id bigint AUTO_INCREMENT NOT NULL,
  44. title varchar(127) NOT NULL,
  45. author bigint NOT NULL,
  46. PRIMARY KEY (id)
  47. ) ENGINE=INNODB;
  48. CREATE TABLE authors (
  49. id bigint AUTO_INCREMENT NOT NULL,
  50. name varchar(127) NOT NULL,
  51. PRIMARY KEY (id)
  52. ) ENGINE=INNODB;
  53. ");
  54. var a1 = new author();
  55. a1.name = "Bill";
  56. db.Insert(a1);
  57. var a2 = new author();
  58. a2.name = "Ted";
  59. db.Insert(a2);
  60. var p = new post();
  61. p.title = "post1";
  62. p.author = a1.id;
  63. db.Insert(p);
  64. p = new post();
  65. p.title = "post2";
  66. p.author = a1.id;
  67. db.Insert(p);
  68. p = new post();
  69. p.title = "post3";
  70. p.author = a2.id;
  71. db.Insert(p);
  72. }
  73. [TestFixtureTearDown]
  74. public void DeleteDB()
  75. {
  76. db.Execute(@"
  77. DROP TABLE IF EXISTS posts;
  78. DROP TABLE IF EXISTS authors;
  79. ");
  80. }
  81. [Test]
  82. public void Basic()
  83. {
  84. var posts = db.Fetch<post, author>("SELECT * FROM posts LEFT JOIN authors ON posts.author = authors.id ORDER BY posts.id");
  85. Assert.AreEqual(posts.Count, 3);
  86. Assert.AreEqual(posts[0].id, 1);
  87. Assert.AreEqual(posts[0].title, "post1");
  88. Assert.AreEqual(posts[0].author, 1);
  89. Assert.AreEqual(posts[0].author_obj.name, "Bill");
  90. Assert.AreEqual(posts[1].id, 2);
  91. Assert.AreEqual(posts[1].title, "post2");
  92. Assert.AreEqual(posts[1].author, 1);
  93. Assert.AreEqual(posts[1].author_obj.name, "Bill");
  94. Assert.AreEqual(posts[2].id, 3);
  95. Assert.AreEqual(posts[2].title, "post3");
  96. Assert.AreEqual(posts[2].author, 2);
  97. Assert.AreEqual(posts[2].author_obj.name, "Ted");
  98. }
  99. [Test]
  100. public void CustomRelator()
  101. {
  102. var posts = db.Fetch<post, author, post>(
  103. (p,a)=>
  104. {
  105. p.author_obj = a;
  106. return p;
  107. },
  108. "SELECT * FROM posts LEFT JOIN authors ON posts.author = authors.id ORDER BY posts.id");
  109. Assert.AreEqual(posts.Count, 3);
  110. Assert.AreNotSame(posts[0].author_obj, posts[1].author_obj);
  111. Assert.AreEqual(posts[0].id, 1);
  112. Assert.AreEqual(posts[0].title, "post1");
  113. Assert.AreEqual(posts[0].author, 1);
  114. Assert.AreEqual(posts[0].author_obj.name, "Bill");
  115. Assert.AreEqual(posts[1].id, 2);
  116. Assert.AreEqual(posts[1].title, "post2");
  117. Assert.AreEqual(posts[1].author, 1);
  118. Assert.AreEqual(posts[1].author_obj.name, "Bill");
  119. Assert.AreEqual(posts[2].id, 3);
  120. Assert.AreEqual(posts[2].title, "post3");
  121. Assert.AreEqual(posts[2].author, 2);
  122. Assert.AreEqual(posts[2].author_obj.name, "Ted");
  123. }
  124. // Relator callback to do many to one relationship mapping
  125. class PostAuthorRelator
  126. {
  127. // A dictionary of known authors
  128. Dictionary<long, author> authors = new Dictionary<long, author>();
  129. public post MapIt(post p, author a)
  130. {
  131. // Get existing author object, or if not found store this one
  132. author aExisting;
  133. if (authors.TryGetValue(a.id, out aExisting))
  134. a = aExisting;
  135. else
  136. authors.Add(a.id, a);
  137. // Wire up objects
  138. p.author_obj = a;
  139. return p;
  140. }
  141. }
  142. [Test]
  143. public void ManyToOne()
  144. {
  145. // This test uses a custom relator callback to connect posts to existing author instances
  146. // Note that for each row, an author object is still created - it's just that the duplicates
  147. // are discarded
  148. var posts = db.Fetch<post, author, post>(new PostAuthorRelator().MapIt,
  149. "SELECT * FROM posts LEFT JOIN authors ON posts.author = authors.id ORDER BY posts.id"
  150. );
  151. Assert.AreEqual(posts.Count, 3);
  152. Assert.AreSame(posts[0].author_obj, posts[1].author_obj);
  153. Assert.AreEqual(posts[0].id, 1);
  154. Assert.AreEqual(posts[0].title, "post1");
  155. Assert.AreEqual(posts[0].author, 1);
  156. Assert.AreEqual(posts[0].author_obj.name, "Bill");
  157. Assert.AreEqual(posts[1].id, 2);
  158. Assert.AreEqual(posts[1].title, "post2");
  159. Assert.AreEqual(posts[1].author, 1);
  160. Assert.AreEqual(posts[1].author_obj.name, "Bill");
  161. Assert.AreEqual(posts[2].id, 3);
  162. Assert.AreEqual(posts[2].title, "post3");
  163. Assert.AreEqual(posts[2].author, 2);
  164. Assert.AreEqual(posts[2].author_obj.name, "Ted");
  165. }
  166. class AuthorPostRelator
  167. {
  168. /*
  169. * In order to support OneToMany relationship mapping, we need to be able to
  170. * delay returning an LHS object until we've processed its many RHS objects
  171. *
  172. * To support this, PetaPoco allows a relator callback to return null - indicating
  173. * that the object isn't yet fully populated.
  174. *
  175. * In order to flush the final object, PetaPoco will call the relator function
  176. * one final time with all parameters set to null. It only does this if the callback
  177. * returned null at least once during the processing of the result set (this saves
  178. * simple lamba mapping functions from having to deal with nulls).
  179. *
  180. */
  181. public author current;
  182. public author MapIt(author a, post p)
  183. {
  184. // Terminating call. Since we can return null from this function
  185. // we need to be ready for PetaPoco to callback later with null
  186. // parameters
  187. if (a == null)
  188. return current;
  189. // Is this the same author as the current one we're processing
  190. if (current != null && current.id == a.id)
  191. {
  192. // Yes, just add this post to the current author's collection of posts
  193. current.posts.Add(p);
  194. // Return null to indicate we're not done with this author yet
  195. return null;
  196. }
  197. // This is a different author to the current one, or this is the
  198. // first time through and we don't have an author yet
  199. // Save the current author
  200. var prev = current;
  201. // Setup the new current author
  202. current = a;
  203. current.posts = new List<post>();
  204. current.posts.Add(p);
  205. // Return the now populated previous author (or null if first time through)
  206. return prev;
  207. }
  208. }
  209. [Test]
  210. public void OneToMany()
  211. {
  212. // Example of OneToMany mappings
  213. var authors = db.Fetch<author, post, author>(new AuthorPostRelator().MapIt,
  214. "SELECT * FROM authors LEFT JOIN posts ON posts.author = authors.id ORDER BY posts.id"
  215. );
  216. Assert.AreEqual(authors.Count, 2);
  217. Assert.AreEqual(authors[0].name, "Bill");
  218. Assert.AreEqual(authors[0].posts.Count, 2);
  219. Assert.AreEqual(authors[0].posts[0].title, "post1");
  220. Assert.AreEqual(authors[0].posts[1].title, "post2");
  221. Assert.AreEqual(authors[1].name, "Ted");
  222. Assert.AreEqual(authors[1].posts.Count, 1);
  223. Assert.AreEqual(authors[1].posts[0].title, "post3");
  224. }
  225. [Test]
  226. public void ManyToOne_Lambda()
  227. {
  228. // same as ManyToOne test case above, but uses a lambda method as the callback
  229. var authors = new Dictionary<long, author>();
  230. var posts = db.Fetch<post, author, post>(
  231. (p, a) =>
  232. {
  233. // Get existing author object
  234. author aExisting;
  235. if (authors.TryGetValue(a.id, out aExisting))
  236. a = aExisting;
  237. else
  238. authors.Add(a.id, a);
  239. // Wire up objects
  240. p.author_obj = a;
  241. return p;
  242. },
  243. "SELECT * FROM posts LEFT JOIN authors ON posts.author = authors.id ORDER BY posts.id"
  244. );
  245. Assert.AreEqual(posts.Count, 3);
  246. Assert.AreSame(posts[0].author_obj, posts[1].author_obj);
  247. Assert.AreEqual(posts[0].id, 1);
  248. Assert.AreEqual(posts[0].title, "post1");
  249. Assert.AreEqual(posts[0].author, 1);
  250. Assert.AreEqual(posts[0].author_obj.name, "Bill");
  251. Assert.AreEqual(posts[1].id, 2);
  252. Assert.AreEqual(posts[1].title, "post2");
  253. Assert.AreEqual(posts[1].author, 1);
  254. Assert.AreEqual(posts[1].author_obj.name, "Bill");
  255. Assert.AreEqual(posts[2].id, 3);
  256. Assert.AreEqual(posts[2].title, "post3");
  257. Assert.AreEqual(posts[2].author, 2);
  258. Assert.AreEqual(posts[2].author_obj.name, "Ted");
  259. }
  260. }
  261. }