Updates.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. using System;
  2. using System.Linq;
  3. using PetaPoco.Core;
  4. using PetaPoco.Tests.Integration.Databases;
  5. using PetaPoco.Tests.Integration.Databases.MSSQL;
  6. using PetaPoco.Tests.Integration.Documentation.Pocos;
  7. using Shouldly;
  8. using Xunit;
  9. namespace PetaPoco.Tests.Integration.Documentation
  10. {
  11. [Collection("MssqlTests")]
  12. public class Updates : BaseDatabase
  13. {
  14. public Updates()
  15. : base(new MssqlDBTestProvider())
  16. {
  17. PocoData.FlushCaches();
  18. }
  19. [Fact]
  20. public void Update()
  21. {
  22. // Create and insert the person
  23. var person = new Person { Id = Guid.NewGuid(), Name = "PetaPoco", Dob = new DateTime(2011, 1, 1), Age = (DateTime.Now.Year - 2011), Height = 242 };
  24. var id = DB.Insert(person);
  25. // Update a few properties of the person
  26. person.Age = 70;
  27. person.Name = "The PetaPoco";
  28. // Tell PetaPoco to update the DB
  29. DB.Update(person);
  30. // Get a clone/copy from the DB
  31. var clone = DB.Single<Person>(id);
  32. // See, the person has been updated
  33. clone.Id.ShouldBe(person.Id);
  34. clone.Dob.ShouldBe(person.Dob);
  35. clone.Height.ShouldBe(person.Height);
  36. clone.Age.ShouldBe(person.Age);
  37. clone.Name.ShouldBe(person.Name);
  38. }
  39. [Fact]
  40. public void UpdatePartial()
  41. {
  42. // Create and insert the person
  43. var person = new Person { Id = Guid.NewGuid(), Name = "PetaPoco", Dob = new DateTime(2011, 1, 1), Age = (DateTime.Now.Year - 2011), Height = 242 };
  44. var id = DB.Insert(person);
  45. // Update a few properties of the person
  46. person.Age = 70;
  47. person.Name = "The PetaPoco";
  48. // Get the poco data
  49. var pocoData = PocoData.ForType(person.GetType(), DB.DefaultMapper);
  50. // Tell PetaPoco to update only ther person's name
  51. // The update statement produced is `UPDATE [People] SET [FullName] = @0 WHERE [Id] = @1`
  52. DB.Update(person, new[] { pocoData.GetColumnName(nameof(Person.Name)) });
  53. // Get a clone/copy from the DB
  54. var clone = DB.Single<Person>(id);
  55. // See, the person has been updated, but only the name
  56. clone.Id.ShouldBe(person.Id);
  57. clone.Dob.ShouldBe(person.Dob);
  58. clone.Height.ShouldBe(person.Height);
  59. clone.Age.ShouldNotBe(70);
  60. clone.Name.ShouldBe("The PetaPoco");
  61. }
  62. [Fact]
  63. public void UpdateToDifferentTable()
  64. {
  65. // Create the and insert the person
  66. var person = new Person { Id = Guid.NewGuid(), Name = "PetaPoco", Dob = new DateTime(2011, 1, 1), Age = (DateTime.Now.Year - 2011), Height = 242 };
  67. var id = DB.Insert("SpecificPeople", "Id", person);
  68. // Update a few properties of the person
  69. person.Age = 70;
  70. person.Name = "The PetaPoco";
  71. // Tell PetaPoco to update the DB table SpecificPeople
  72. // The update statement produced is `UPDATE [SpecificPeople] SET [FullName] = @0, [Age] = @1, [Height] = @2, [Dob] = @3 WHERE [Id] = @4`
  73. DB.Update("SpecificPeople", "Id", person);
  74. // We need to get the clone/copy from the correct table
  75. // Note: we can't use auto select builder here because PetaPoco would create columns such as People.Id
  76. var clone = DB.Query<Person>("SELECT * FROM [SpecificPeople] sp WHERE sp.[Id] = @0", id).Single();
  77. // See, the person has been updated
  78. clone.Id.ShouldBe(person.Id);
  79. clone.Dob.ShouldBe(person.Dob);
  80. clone.Height.ShouldBe(person.Height);
  81. clone.Age.ShouldBe(person.Age);
  82. clone.Name.ShouldBe(person.Name);
  83. }
  84. [Fact]
  85. public void UpdateConventionalPoco()
  86. {
  87. // Clear out any notes and reset the ID sequence counter
  88. DB.Execute("TRUNCATE TABLE [Note]");
  89. // Insert some notes using all APIs
  90. var note1 = new Note { Text = "PetaPoco's note", CreatedOn = new DateTime(1948, 1, 11, 4, 2, 4, DateTimeKind.Utc) };
  91. var note2 = new Note { Text = "PetaPoco's note", CreatedOn = new DateTime(1948, 1, 11, 4, 2, 4, DateTimeKind.Utc) };
  92. var note3 = new Note { Text = "PetaPoco's note", CreatedOn = new DateTime(1948, 1, 11, 4, 2, 4, DateTimeKind.Utc) };
  93. var note4 = new Note { Text = "PetaPoco's note", CreatedOn = new DateTime(1948, 1, 11, 4, 2, 4, DateTimeKind.Utc) };
  94. var note5 = new Note { Text = "PetaPoco's note", CreatedOn = new DateTime(1948, 1, 11, 4, 2, 4, DateTimeKind.Utc) };
  95. // Each of the API usuages here are effectively the same, as PetaPoco is providing the correct unknown values.
  96. // This is because the poco has been mapped by convention and therefore PetaPoco understands how to do this.
  97. DB.Insert(note1);
  98. DB.Insert(note2);
  99. DB.Insert(note3);
  100. DB.Insert(note4);
  101. DB.Insert(note5);
  102. //Update the notes
  103. note1.Text += " some more text";
  104. note2.Text += " some more text";
  105. note3.Text += " some more text";
  106. note4.Text += " some more text";
  107. note5.Text += " some more text";
  108. // Get the poco data
  109. var pocoData = PocoData.ForType(typeof(Note), DB.DefaultMapper);
  110. // Update all notes using all APIs
  111. DB.Update(note1);
  112. DB.Update(note2, note2.Id);
  113. DB.Update(note3, note3.Id, pocoData.UpdateColumns);
  114. var sql1 = $"SET {DB.Provider.EscapeSqlIdentifier(pocoData.GetColumnName(nameof(Note.Text)))} = @1 " +
  115. $"WHERE {DB.Provider.EscapeSqlIdentifier(pocoData.TableInfo.PrimaryKey)} = @0";
  116. DB.Update<Note>(sql1, note4.Id, note4.Text);
  117. var sql2 = new Sql(
  118. $"SET {DB.Provider.EscapeSqlIdentifier(pocoData.GetColumnName(nameof(Note.Text)))} = @1 " +
  119. $"WHERE {DB.Provider.EscapeSqlIdentifier(pocoData.TableInfo.PrimaryKey)} = @0", note5.Id, note5.Text);
  120. DB.Update<Note>(sql2);
  121. // Just to be sure
  122. DB.ExecuteScalar<int>("SELECT COUNT(*) FROM [Note] WHERE CAST(Text AS NVARCHAR(MAX)) = @0", "PetaPoco's note some more text").ShouldBe(5);
  123. }
  124. [Fact]
  125. public void UpdateUnconventionalPoco()
  126. {
  127. // Create the UnconventionalPocos table
  128. DB.Execute(@"IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES t WHERE t.TABLE_SCHEMA = 'dbo' AND t.TABLE_NAME = 'TBL_UnconventionalPocos')
  129. DROP TABLE dbo.[TBL_UnconventionalPocos]
  130. CREATE TABLE dbo.[TBL_UnconventionalPocos] (
  131. [PrimaryKey] INT IDENTITY(1,1) PRIMARY KEY,
  132. [Text] NTEXT NOT NULL
  133. )");
  134. // This POCO is unconventional because, when using the default conventional mapper, PetaPoco won't understand how this poco maps to the database.
  135. // To understand the power of unconventional mapping, a developer could configure it to work in this situation.
  136. var poco = new UnconventionalPoco { Text = "PetaPoco" };
  137. // Insert the poco
  138. var id = DB.Insert("TBL_UnconventionalPocos", "PrimaryKey", true, poco);
  139. // Update the poco
  140. poco.Text += " some more text";
  141. DB.Update("TBL_UnconventionalPocos", "PrimaryKey", poco);
  142. // Get a clone/copy from the DB
  143. var clone = DB.Query<UnconventionalPoco>("SELECT * FROM [TBL_UnconventionalPocos] WHERE [PrimaryKey] = @0", id).Single();
  144. // Just to be sure
  145. poco.PrimaryKey.ShouldBe(clone.PrimaryKey);
  146. poco.Text.ShouldBe(clone.Text);
  147. }
  148. [Fact]
  149. public void UpdateConventionalUnconventionalPoco()
  150. {
  151. // Create the UnconventionalPocos table
  152. DB.Execute(@"IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES t WHERE t.TABLE_SCHEMA = 'dbo' AND t.TABLE_NAME = 'TBL_UnconventionalPocos')
  153. DROP TABLE dbo.[TBL_UnconventionalPocos]
  154. CREATE TABLE dbo.[TBL_UnconventionalPocos] (
  155. [PrimaryKey] INT IDENTITY(1,1) PRIMARY KEY,
  156. [Text] NTEXT NOT NULL
  157. )");
  158. // Reconfigure the convention mapper
  159. // Note: I can't think of a valid reason, other than for a purpose such as this, where you would configure the convention mapper in this way.
  160. ((ConventionMapper) DB.DefaultMapper).MapPrimaryKey = (ti, t) =>
  161. {
  162. var prop = t.GetProperties().FirstOrDefault(p => p.Name == "PrimaryKey");
  163. if (prop == null)
  164. return false;
  165. ti.PrimaryKey = prop.Name;
  166. ti.AutoIncrement = ((ConventionMapper) DB.DefaultMapper).IsPrimaryKeyAutoIncrement(prop.PropertyType);
  167. return true;
  168. };
  169. ((ConventionMapper) DB.DefaultMapper).InflectTableName = (i, tn) => "TBL_" + tn + "s";
  170. // Create the POCO
  171. var poco = new UnconventionalPoco { Text = "PetaPoco" };
  172. // Tell PetaPoco to insert it
  173. var id = DB.Insert(poco);
  174. // Get a clone/copy from the DB
  175. var clone = DB.SingleOrDefault<UnconventionalPoco>(id);
  176. // See, they're are the same
  177. clone.ShouldBe(poco);
  178. // Update the original poco
  179. poco.Text += " some more text";
  180. // Update the poco
  181. DB.Update(poco);
  182. // Get the clone from teh database again
  183. clone = DB.SingleOrDefault<UnconventionalPoco>(id);
  184. // Confirm the text was updated
  185. clone.Text.ShouldBe("PetaPoco some more text");
  186. }
  187. [Fact]
  188. public void UpdateAnonymousPocoWithConventionalNaming()
  189. {
  190. // Create the table for our unknown but conventional POCO
  191. DB.Execute(@"IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES t WHERE t.TABLE_SCHEMA = 'dbo' AND t.TABLE_NAME = 'XFiles')
  192. DROP TABLE dbo.[XFiles]
  193. CREATE TABLE dbo.[XFiles] (
  194. [Id] INT IDENTITY(1,1) PRIMARY KEY,
  195. [FileName] VARCHAR(255) NOT NULL
  196. )");
  197. // Anonymous type are friend of PetaPoco
  198. var xfile = new { FileName = "Agent Mulder.sec" };
  199. // Tell PetaPoco to insert it
  200. var id = DB.Insert("XFiles", "Id", true, xfile);
  201. // Update the poco
  202. xfile = new { FileName = "Agent Mulder.sec" };
  203. // Update the database
  204. DB.Update("XFiles", "Id", xfile);
  205. // Get a clone/copy from the DB
  206. // Note: Check out the name parameters - cool eh?
  207. var clone = DB.Query<dynamic>("SELECT * FROM [XFiles] WHERE [Id] = @Id", new { Id = id }).Single();
  208. // See, they're are the same
  209. id.ShouldBe((int) clone.Id);
  210. xfile.FileName.ShouldBe((string) clone.FileName);
  211. }
  212. [Fact]
  213. public void InsertDynamicUnknownPocoWithConventionalNaming()
  214. {
  215. // Create the table for our unknown but conventional POCO
  216. DB.Execute(@"IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES t WHERE t.TABLE_SCHEMA = 'dbo' AND t.TABLE_NAME = 'XFiles')
  217. DROP TABLE dbo.[XFiles]
  218. CREATE TABLE dbo.[XFiles] (
  219. [Id] INT IDENTITY(1,1) PRIMARY KEY,
  220. [FileName] VARCHAR(255) NOT NULL
  221. )");
  222. // Dynamics type are friend of PetaPoco
  223. dynamic xfile = new System.Dynamic.ExpandoObject();
  224. xfile.FileName = "Agent Mulder.sec";
  225. // Tell PetaPoco to insert it
  226. var id = DB.Insert("XFiles", "Id", true, (object) xfile);
  227. // Update the poco
  228. xfile.FileName = "Agent Mulder.sec";
  229. // Update the database
  230. DB.Update("XFiles", "Id", (object) xfile);
  231. // Get a clone/copy from the DB
  232. // Note: Check out the name parameters - cool eh?
  233. var clone = DB.Query<dynamic>("SELECT * FROM [XFiles] WHERE [Id] = @Id", new { Id = id }).Single();
  234. // See, they're are the same
  235. id.ShouldBe((int) clone.Id);
  236. ((string) xfile.FileName).ShouldBe((string) clone.FileName);
  237. }
  238. }
  239. }