22using Microsoft . Extensions . Logging ;
33using NHUnitExample . Entities ;
44using System ;
5+ using System . Collections ;
56using System . Collections . Generic ;
67using System . Linq ;
78using System . Threading ;
89using System . Threading . Tasks ;
10+ using NHibernate . Util ;
911using Color = NHUnitExample . Entities . Color ;
1012
1113namespace NHUnitExample . Controllers
@@ -23,8 +25,14 @@ public TestController(ILogger<TestController> logger, IDbContext dbContext)
2325 _dbContext = dbContext ;
2426 }
2527
28+ /// <summary>
29+ /// You need to setup the database before testing any other endpoint.
30+ /// At every project start the DB schema is recreated: Check Startup.cs -> BuildSchema(Configuration config)
31+ /// </summary>
32+ /// <param name="cancellationToken"></param>
33+ /// <returns></returns>
2634 [ HttpGet ( "/initializeDatabase" ) ]
27- public async Task < IActionResult > InitializeDatabase ( CancellationToken token )
35+ public async Task < IActionResult > InitializeDatabase ( CancellationToken cancellationToken )
2836 {
2937 if ( _dbContext . Countries . All ( ) . Any ( ) )
3038 return Ok ( "Already initialized" ) ;
@@ -39,7 +47,7 @@ public async Task<IActionResult> InitializeDatabase(CancellationToken token)
3947 new Country ( ) { Name = "India" } ,
4048 new Country ( ) { Name = "Romania" } ,
4149 } ;
42- await _dbContext . Countries . InsertManyAsync ( countries , token ) ;
50+ await _dbContext . Countries . InsertManyAsync ( countries , cancellationToken ) ;
4351
4452 //colors
4553 var colors = new List < Color >
@@ -51,7 +59,7 @@ public async Task<IActionResult> InitializeDatabase(CancellationToken token)
5159 new Color ( ) { Name = "Blue" } ,
5260 new Color ( ) { Name = "Grey" } ,
5361 } ;
54- await _dbContext . Colors . InsertManyAsync ( colors , token ) ;
62+ await _dbContext . Colors . InsertManyAsync ( colors , cancellationToken ) ;
5563
5664 //phone number types
5765 var phoneNumberTypes = new List < PhoneNumberType >
@@ -60,8 +68,8 @@ public async Task<IActionResult> InitializeDatabase(CancellationToken token)
6068 new PhoneNumberType ( ) { Name = "Mobile" } ,
6169 new PhoneNumberType ( ) { Name = "Business" } ,
6270 } ;
63- await _dbContext . PhoneNumberTypes . InsertManyAsync ( phoneNumberTypes , token ) ;
64- await _dbContext . SaveChangesAsync ( token ) ; // save changes but do not commit transaction (not required in this case)
71+ await _dbContext . PhoneNumberTypes . InsertManyAsync ( phoneNumberTypes , cancellationToken ) ;
72+ await _dbContext . SaveChangesAsync ( cancellationToken ) ; // save changes but do not commit transaction (not required in this case)
6573
6674 var random = new Random ( 15 ) ;
6775
@@ -75,7 +83,7 @@ public async Task<IActionResult> InitializeDatabase(CancellationToken token)
7583 Price = random . Next ( 100 , 10000 ) / 100m
7684 }
7785 ) . ToList ( ) ;
78- await _dbContext . Products . InsertManyAsync ( products , token ) ;
86+ await _dbContext . Products . InsertManyAsync ( products , cancellationToken ) ;
7987
8088 //customers
8189 var customer = new Customer ( )
@@ -103,7 +111,7 @@ public async Task<IActionResult> InitializeDatabase(CancellationToken token)
103111 . ToList ( )
104112 . ForEach ( p => cart . Products . Add ( p ) ) ;
105113 cart . LastUpdated = DateTime . UtcNow ;
106- await _dbContext . Customers . InsertAsync ( customer , token ) ;
114+ await _dbContext . Customers . InsertAsync ( customer , cancellationToken ) ;
107115
108116 //customer 2 - empty cart
109117 var customer2 = new Customer ( )
@@ -125,7 +133,7 @@ public async Task<IActionResult> InitializeDatabase(CancellationToken token)
125133 PhoneNumberType = phoneNumberTypes . First ( )
126134 } ) ;
127135 customer2 . SetCart ( new CustomerCart ( ) ) ;
128- await _dbContext . Customers . InsertAsync ( customer2 , token ) ;
136+ await _dbContext . Customers . InsertAsync ( customer2 , cancellationToken ) ;
129137
130138 //customer 3 - basic info
131139 var customer3 = new Customer ( )
@@ -134,39 +142,57 @@ public async Task<IActionResult> InitializeDatabase(CancellationToken token)
134142 FirstName = "Mike" ,
135143 LastName = "Golden"
136144 } ;
137- await _dbContext . Customers . InsertAsync ( customer3 , token ) ;
145+ await _dbContext . Customers . InsertAsync ( customer3 , cancellationToken ) ;
138146
139- await _dbContext . SaveChangesAsync ( token ) ;
140- await _dbContext . CommitTransactionAsync ( token ) ;
147+ await _dbContext . SaveChangesAsync ( cancellationToken ) ;
148+ await _dbContext . CommitTransactionAsync ( cancellationToken ) ;
141149 return Ok ( "Initialization complete" ) ;
142150 }
143151
152+ /// <summary>
153+ /// A simple example of query wrap. You can join multiple entity types
154+ /// </summary>
155+ /// <param name="cancellationToken"></param>
156+ /// <returns></returns>
144157 [ HttpGet ( "/getAllCustomers" ) ]
145- public async Task < IActionResult > GetAllCustomer ( CancellationToken token )
158+ public async Task < IActionResult > GetAllCustomer ( CancellationToken cancellationToken )
146159 {
147160 var customers = await _dbContext . WrapQuery (
148161 _dbContext . Customers . All ( ) )
149162 . Unproxy ( ) //without this you might download the whole DB
150- . ListAsync ( token ) ;
163+ . ListAsync ( cancellationToken ) ;
151164 return Ok ( customers ) ;
152165 }
153166
167+ /// <summary>
168+ /// A simple example for loading an entity with all the nested children.
169+ /// Behind the scenes it's using join, new future queries or batch fetching.
170+ /// At the end it's stripping all the NHibernate proxy classes so you don't need to worry about casting or unwanted lazy loading.
171+ /// </summary>
172+ /// <param name="cancellationToken"></param>
173+ /// <param name="customerId"></param>
174+ /// <returns></returns>
154175 [ HttpGet ( "/getAllForCustomer" ) ]
155- public async Task < IActionResult > GetAllForCustomer ( CancellationToken token , int customerId = 11 )
176+ public async Task < IActionResult > GetAllForCustomer ( CancellationToken cancellationToken , int customerId = 11 )
156177 {
157178 //get the customer with all nested data and execute the above query too
158179 var customer = await _dbContext . Customers . Get ( customerId ) //returns a wrapper to configure the query
159180 . Include ( c => c . Addresses . Single ( ) . Country , //include Addresses in the result using the fastest approach: join, new query, batch fetch. Usage: c.Child1.ChildList2.Single().Child3 which means include Child1 and ChildList2 with ALL Child3 properties
160181 c => c . PhoneNumbers . Single ( ) . PhoneNumberType , //include all PhoneNumbers with PhoneNumberType
161182 c => c . Cart . Products . Single ( ) . Colors ) //include Cart with Products and details about products
162183 . Unproxy ( ) //instructs the framework to strip all the proxy classes when the Value is returned
163- . ValueAsync ( token ) ; //this is where the query(s) get executed
184+ . ValueAsync ( cancellationToken ) ; //this is where the query(s) get executed
164185
165186 return Ok ( customer ) ;
166187 }
167188
189+ /// <summary>
190+ /// A simple example of projecting a query
191+ /// </summary>
192+ /// <param name="cancellationToken"></param>
193+ /// <returns></returns>
168194 [ HttpGet ( "/testProjections" ) ]
169- public async Task < IActionResult > TestProjections ( CancellationToken token )
195+ public async Task < IActionResult > TestProjections ( CancellationToken cancellationToken )
170196 {
171197 int pageNumber = 3 ;
172198 int pageSize = 10 ;
@@ -181,7 +207,7 @@ public async Task<IActionResult> TestProjections(CancellationToken token)
181207 Colors = p . Colors . Select ( c => c . Name )
182208 } ) ;
183209
184- var pagedProducts = await _dbContext . WrapQuery ( query ) . ListAsync ( token ) ;
210+ var pagedProducts = await _dbContext . WrapQuery ( query ) . ListAsync ( cancellationToken ) ;
185211
186212 return Ok ( new
187213 {
@@ -192,8 +218,13 @@ public async Task<IActionResult> TestProjections(CancellationToken token)
192218 } ) ;
193219 }
194220
221+ /// <summary>
222+ /// Execute multiple queries in a single server trip.
223+ /// </summary>
224+ /// <param name="cancellationToken"></param>
225+ /// <returns></returns>
195226 [ HttpGet ( "/testMultipleQueries" ) ]
196- public async Task < IActionResult > TestMultipleQueries ( CancellationToken token )
227+ public async Task < IActionResult > TestMultipleQueries ( CancellationToken cancellationToken )
197228 {
198229 //notify framework we want the total number of products
199230 var redProductsCountPromise = _dbContext . WrapQuery (
@@ -227,14 +258,14 @@ public async Task<IActionResult> TestMultipleQueries(CancellationToken token)
227258 . Deferred ( ) ;
228259
229260 //execute all deferred queries. If the Database doesn't support futures (Ex: Oracle) the query will get executed when Value() is called
230- var customersWithEmptyCart = await customersWithEmptyCartPromise . ListAsync ( token ) ;
261+ var customersWithEmptyCart = await customersWithEmptyCartPromise . ListAsync ( cancellationToken ) ;
231262 //we can unproxy the object at any time
232263 customersWithEmptyCart = _dbContext . Unproxy ( customersWithEmptyCart ) ;
233264
234265 // List/ListAsync shouldn't hit the Database unless it doesn't support futures. Ex: Oracle
235- var expensiveProducts = await expensiveProductsPromise . ListAsync ( token ) ;
236- var lastActiveCustomer = await lastActiveCustomerPromise . ValueAsync ( token ) ;
237- var numberOfRedProducts = await redProductsCountPromise . ValueAsync ( token ) ;
266+ var expensiveProducts = await expensiveProductsPromise . ListAsync ( cancellationToken ) ;
267+ var lastActiveCustomer = await lastActiveCustomerPromise . ValueAsync ( cancellationToken ) ;
268+ var numberOfRedProducts = await redProductsCountPromise . ValueAsync ( cancellationToken ) ;
238269 return Ok ( new
239270 {
240271 NumberOfRedProducts = numberOfRedProducts ,
@@ -244,17 +275,58 @@ public async Task<IActionResult> TestMultipleQueries(CancellationToken token)
244275 } ) ;
245276 }
246277
278+ /// <summary>
279+ /// Execute your own SQL script using: ExecuteListAsync, ExecuteScalarAsync, ExecuteNonQueryAsync
280+ /// </summary>
281+ /// <param name="cancellationToken"></param>
282+ /// <param name="customerId"></param>
283+ /// <returns></returns>
247284 [ HttpGet ( "/testSqlQuery" ) ]
248- public async Task < IActionResult > TestSqlQuery ( CancellationToken token , int customerId = 11 )
285+ public async Task < IActionResult > TestSqlQuery ( CancellationToken cancellationToken , int customerId = 11 )
249286 {
250287 var sqlQuery = @"select Id as CustomerId,
251288 Concat(FirstName,' ',LastName) as FullName,
252289 BirthDate
253290 from ""Customer""
254291 where Id= :customerId" ;
255- var customResult = await _dbContext . ExecuteScalarAsync < SqlQueryCustomResult > ( sqlQuery , new { customerId } , token ) ;
292+ var customResult = await _dbContext . ExecuteScalarAsync < SqlQueryCustomResult > ( sqlQuery , new { customerId } , cancellationToken ) ;
256293 return Ok ( customResult ) ;
257294 }
295+
296+
297+ /// <summary>
298+ /// Execute a procedure or multiple queries which return multiple results.
299+ /// You'll need to cast/select the right collection.
300+ /// </summary>
301+ /// <param name="cancellationToken"></param>
302+ /// <param name="customerId"></param>
303+ /// <returns></returns>
304+ [ HttpGet ( "/testDataSetQuery" ) ]
305+ public async Task < IActionResult > TestDataSetQuery ( CancellationToken cancellationToken , int customerId = 11 )
306+ {
307+ var sqlQuery = @"select Id as CustomerId,
308+ Concat(FirstName,' ',LastName) as FullName,
309+ BirthDate
310+ from ""Customer""
311+ where Id= :customerId;
312+ select count(*) Count from ""Customer"";" ;
313+ var customResult = await _dbContext . ExecuteMultipleQueriesAsync ( sqlQuery , //query
314+ new { customerId } , //parameters: property name must be the same as the parameter
315+ cancellationToken ,
316+ typeof ( SqlQueryCustomResult ) , //first result type
317+ typeof ( long ) ) ; //second result type
318+
319+ //The results are returned in order in it's own collection.
320+ var customer = ( SqlQueryCustomResult ) customResult [ 0 ] . FirstOrDefault ( ) ; //we might not have any results
321+ var customerCount = ( long ) customResult [ 1 ] . First ( ) ; //the time must match: it will fail with int
322+
323+ return Ok ( new
324+ {
325+ Customer = customer ,
326+ CustomerCount = customerCount
327+ } ) ;
328+ }
329+
258330 class SqlQueryCustomResult
259331 {
260332 public SqlQueryCustomResult ( ) { }
0 commit comments