Skip to content

Commit 82a24db

Browse files
committed
Multiple enhancements
1 parent fc7f360 commit 82a24db

File tree

14 files changed

+240
-46
lines changed

14 files changed

+240
-46
lines changed

License.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Copyright 2021 CSharpBender
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4+
5+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6+
7+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

NHUnitExample/Controllers/TestController.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,12 @@ public async Task<IActionResult> TestProjections(CancellationToken token)
195195
[HttpGet("/testMultipleQueries")]
196196
public async Task<IActionResult> TestMultipleQueries(CancellationToken token)
197197
{
198+
//notify framework we want the total number of products
199+
var redProductsCountPromise = _dbContext.WrapQuery(
200+
_dbContext.Products.All().Where(p => p.Colors.Any(c => c.Name == "Red")))
201+
.Count()
202+
.Deferred();
203+
198204
// notify framework we need top 10 expensive products - no execution
199205
var expensiveProductsPromise = _dbContext.WrapQuery(
200206
_dbContext.Products.All().OrderByDescending(p => p.Price).Take(10))
@@ -228,11 +234,13 @@ public async Task<IActionResult> TestMultipleQueries(CancellationToken token)
228234
// List/ListAsync shouldn't hit the Database unless it doesn't support futures. Ex: Oracle
229235
var expensiveProducts = await expensiveProductsPromise.ListAsync(token);
230236
var lastActiveCustomer = await lastActiveCustomerPromise.ValueAsync(token);
237+
var numberOfRedProducts = await redProductsCountPromise.ValueAsync(token);
231238
return Ok(new
232239
{
233-
LastActiveCustomer = lastActiveCustomer,
234-
CustomersWithEmptyCart = customersWithEmptyCart,
240+
NumberOfRedProducts = numberOfRedProducts,
235241
Top10ExpensiveProducts = expensiveProducts,
242+
LastActiveCustomer = lastActiveCustomer,
243+
CustomersWithEmptyCart = customersWithEmptyCart
236244
});
237245
}
238246

NHUnitExample/Entities/Country.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
using System.Collections.Generic;
2-
using System.Security.Cryptography;
3-
using NHibernate.Mapping;
42

53
namespace NHUnitExample.Entities
64
{

NHUnitExample/Entities/CustomerPhone.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,7 @@ public CustomerPhone() { }
66

77
public virtual int Id { get; set; }
88
public virtual string PhoneNumber { get; set; }
9-
//public virtual int CustomerId { get; set; }
109
public virtual Customer Customer { get; set; }
11-
//public virtual int PhoneNumberTypeId { get; set; }
1210
public virtual PhoneNumberType PhoneNumberType { get; set; }
1311
}
1412
}

README.md

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ NHUnit is a simple but powerful Unit of Work and Repository pattern implementati
33
If at some point you decide to change the ORM, you just need to provide your own implementation for just two interfaces instead of rewriting the whole application.
44
NHUnit provides sync and async versions for each method.
55

6-
##License
6+
## License
77
MIT: Enjoy, share, contribute to the project.
88

99

@@ -64,6 +64,30 @@ var customer = await _dbContext.Customers
6464
.ValueAsync(token); //this is where the query(s) get executed
6565
```
6666

67+
An unproxied object will contain the foreign key ids (One to One and Many to One relations).
68+
For example in the above query the Cart property will be instantiated and popuplated with the Id field.
69+
70+
71+
## Multi Queries
72+
Using `Deferred()` you can execute multiple queries in a single server trip.
73+
Bellow is an example with 3 different queries that get executed in one server trip.
74+
75+
```csharp
76+
//product count future
77+
var prodCountP = _dbContext.WrapQuery(_dbContext.Products.All())
78+
.Count().Deferred();
79+
80+
//most expensive 10 products future
81+
var expProdsP = _dbContext.WrapQuery(_dbContext.Products.All().OrderByDescending(p => p.Price).Take(10))
82+
.Deferred();
83+
84+
//get customer by id - executes all queries
85+
var customer = await _dbContext.Customers.Get(customerId).Deferred().ValueAsync();
86+
var prodCount = await prodCountP.ValueAsync(); //returns value
87+
var expProds = await expProdsP.ListAsync(); //returns value
88+
```
89+
90+
6791
## Transactions
6892
IUnitOfWork exposes:
6993
- BeginTransaction

src/NHUnit/Helper/NhibernateHelper.cs renamed to src/NHUnit/Helper/NHUnitHelper.cs

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,11 @@
1717
using System.Reflection;
1818
using System.Runtime.CompilerServices;
1919
using System.Text;
20+
using System.Threading;
2021

2122
namespace NHUnit
2223
{
23-
internal static class NhibernateHelper
24+
internal static class NHUnitHelper
2425
{
2526
public static List<T> Unproxy<T>(List<T> entityList, ISession session)
2627
{
@@ -48,7 +49,11 @@ private static T Unproxy<T>(T entity, ISession session, HashSet<object> resolved
4849
{
4950
if (!NHibernateUtil.IsInitialized(entity))
5051
{
51-
return default(T);
52+
var id = session.GetIdentifier(entity);
53+
var et = NHibernateProxyHelper.GetClassWithoutInitializingProxy(entity);
54+
entity = (T)Activator.CreateInstance(et);
55+
session.SessionFactory.GetClassMetadata(et).SetIdentifier(entity,id);
56+
return entity;
5257
}
5358
resolvedEntity = (T)session.GetSessionImplementation().PersistenceContext.Unproxy(entity);
5459
}
@@ -61,8 +66,8 @@ private static T Unproxy<T>(T entity, ISession session, HashSet<object> resolved
6166
return resolvedEntity;
6267

6368
resolvedEntities.Add(resolvedEntity);
64-
6569
Type entityType = resolvedEntity.GetType();
70+
6671
var entityMetadata = session.SessionFactory.GetClassMetadata(entityType);
6772
if (entityMetadata == null)
6873
{
@@ -75,6 +80,15 @@ private static T Unproxy<T>(T entity, ISession session, HashSet<object> resolved
7580
}
7681
return entity;
7782
}
83+
else if (IsList(entityType))
84+
{
85+
var entityCollection = (IList)resolvedEntity;
86+
for (int i = 0; i < entityCollection.Count; i++)
87+
{
88+
entityCollection[i] = Unproxy(entityCollection[i], session, resolvedEntities);
89+
}
90+
return resolvedEntity;
91+
}
7892
else
7993
{
8094
return default(T);
@@ -152,6 +166,18 @@ public static void VisitNodes(object obj, ISession session, EntityNodeInfo inclu
152166
}
153167
}
154168

169+
private static bool IsList(Type t)
170+
{
171+
if (t.IsGenericType)
172+
{
173+
return t.GetInterfaces().Any(x =>
174+
x.IsGenericType &&
175+
x.GetGenericTypeDefinition() == typeof(IList<>));
176+
}
177+
178+
return typeof(IList).IsAssignableFrom(t);
179+
}
180+
155181
public static EntityNodeInfo GetExpressionTreeInfo<T>(Expression<Func<T, object>>[] includeChildNodes, EntityNodeInfo rootNode, bool includeParent = false)
156182
{
157183
foreach (var includeChildExpression in includeChildNodes)
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using System.Threading;
2+
using System.Threading.Tasks;
3+
4+
namespace NHUnit
5+
{
6+
public interface ICountWrapper<T>
7+
{
8+
/// <summary>
9+
/// Notifies framework that multi queries should be used to fetch data, using a single server roundtrip.
10+
/// This is just a configuration, the query is not executed until the Value is retrieved, which will execute the other deferred queries as well.
11+
/// </summary>
12+
/// <returns></returns>
13+
ICountWrapper<T> Deferred();
14+
15+
/// <summary>
16+
/// Executes Query and returns the number of rows
17+
/// If Deferred execution was used, the query will be evaluated only once for the whole batch.
18+
/// </summary>
19+
/// <returns></returns>
20+
int Value();
21+
22+
/// <summary>
23+
/// Executes Query asynchronously and returns the number of rows
24+
/// If Deferred execution was used, the query will be evaluated only once for the whole batch.
25+
/// </summary>
26+
/// <param name="cancellationToken"></param>
27+
/// <returns></returns>
28+
Task<int> ValueAsync(CancellationToken cancellationToken = default(CancellationToken));
29+
}
30+
}

src/NHUnit/Interfaces/IEntityListWrapper.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,13 @@ public interface IEntityListWrapper<T>
2323
/// <returns></returns>
2424
IEntityListWrapper<T> Unproxy();
2525

26+
/// <summary>
27+
/// Instruct the framework to compute the number of results
28+
/// This is just a configuration, the query is not executed until the Value is retrieved, which will execute the other deferred queries as well.
29+
/// </summary>
30+
/// <returns></returns>
31+
ICountWrapper<T> Count();
32+
2633
/// <summary>
2734
/// Notifies framework that multi queries should be used to fetch data, using a single server roundtrip.
2835
/// This is just a configuration, the query is not executed until the Value is retrieved, which will execute the other deferred queries as well.

src/NHUnit/NHUnit.csproj

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
</PropertyGroup>
1010

1111
<ItemGroup>
12-
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel.Core" Version="2.1.1" />
1312
<PackageReference Include="NHibernate" Version="5.3.5" />
1413
</ItemGroup>
1514

@@ -25,14 +24,18 @@
2524
<AssemblyName>NHUnit</AssemblyName>
2625
<Description>A powerful Unit of Work implementation for NHibernate which fixes proxy issues and simplifies child nodes loading.</Description>
2726
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
28-
<PackageReleaseNotes>Release of NHibernate Unit of Work</PackageReleaseNotes>
27+
<PackageReleaseNotes>NHUnit
28+
- populate unproxied objects with FK object Id
29+
- Count() functionality without loading the data
30+
- Unproxy() bug fix</PackageReleaseNotes>
2931
<PackageTags>Fluent, NHibernate, Unit of Work, ORM, DataBase, Core, Async</PackageTags>
3032
<RepositoryType>GitHub</RepositoryType>
3133
<RepositoryUrl>https://github.com/CSharpBender/NHUnit</RepositoryUrl>
3234
<PackageProjectUrl>https://github.com/CSharpBender/NHUnit</PackageProjectUrl>
3335
<PackageIcon>NHUnit.png</PackageIcon>
3436
<Copyright>CSharpBender</Copyright>
3537
<PackageLicenseExpression>MIT</PackageLicenseExpression>
38+
<Version>1.1.0</Version>
3639
</PropertyGroup>
3740

3841
<Target Name="CopyProjectReferencesToPackage" DependsOnTargets="ResolveReferences">

src/NHUnit/Repository.cs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,7 @@ public Repository(IUnitOfWork unitOfWork)
3434

3535
public ISingleEntityWrapper<T> Get(object id)
3636
{
37-
return new SingleEntityWrapper<T>()
38-
{
39-
Id = id,
40-
Session = Session,
41-
TimeoutInSeconds = _unitOfWork.CommandTimeout
42-
};
37+
return new SingleEntityWrapper<T>(id, Session, _unitOfWork.CommandTimeout);
4338
}
4439

4540
public IQueryable<T> All()

0 commit comments

Comments
 (0)