├── .config
└── dotnet-tools.json
├── .editorconfig
├── .gitattributes
├── .gitignore
├── AsyncGenerator.yml
├── LICENSE
├── NHibernate.Extensions.Tests
├── App.config
├── Async
│ ├── BatchFetchTests.cs
│ ├── DeepCloneTests.cs
│ └── LinqIncludeTests.cs
├── BaseIncludeTest.cs
├── BatchFetchTests.cs
├── CascadeConvention.cs
├── DeepCloneTests.cs
├── Entities
│ ├── Animal.cs
│ ├── AnimalType.cs
│ ├── BatchModel.cs
│ ├── Cat.cs
│ ├── EQBDrivingLicence.cs
│ ├── EQBHouse.cs
│ ├── EQBIdentity.cs
│ ├── EQBIdentityCard.cs
│ ├── EQBPerson.cs
│ ├── EQBRoadWorthyTest.cs
│ ├── EQBUser.cs
│ ├── EQBVehicle.cs
│ ├── IUser.cs
│ └── TestEQBWheel.cs
├── IEntity.cs
├── LinqIncludeTests.cs
├── LogSpy.cs
├── NHConfig.cs
├── NHibernate.Extensions.Tests.csproj
├── QueryRelationTreeTests.cs
├── SessionSubscriptionTests.cs
├── Test.ldf
├── Test.mdf
└── hibernate.cfg.xml
├── NHibernate.Extensions.sln
├── NHibernate.Extensions
├── Async
│ ├── BatchFetchBuilder.cs
│ ├── BatchFetchExtension.cs
│ ├── DeepClone
│ │ └── DeepCloneSessionExtension.cs
│ └── IBatchFetchBuilder.cs
├── BatchFetchBuilder.cs
├── BatchFetchExtension.cs
├── DeepClone
│ ├── DeepCloneMemberOptions.cs
│ ├── DeepCloneOptions.cs
│ ├── DeepCloneParentEntity.cs
│ ├── DeepCloneSessionExtension.cs
│ ├── DeepCloneTypeOptions.cs
│ ├── IDeepCloneMemberOptions.cs
│ ├── IDeepCloneTypeOptions.cs
│ └── IEntityResolver.cs
├── IBatchFetchBuilder.cs
├── Internal
│ ├── DelegateEntityResolver.cs
│ ├── ExpressionExtensions.cs
│ ├── ExpressionHelper.cs
│ ├── TypeExtensions.cs
│ └── TypeHelper.cs
├── Linq
│ ├── ExpressionInfo.cs
│ ├── IIncludeOptions.cs
│ ├── IIncludeQueryable.cs
│ ├── IncludeOptions.cs
│ ├── IncludeQueryProvider.cs
│ ├── IncludeQueryable.cs
│ ├── IncludeRewriterVisitor.cs
│ ├── LinqExtensions.cs
│ └── SkipTakeVisitor.cs
├── NHibernate.Extensions.csproj
├── QueryRelationNode.cs
├── QueryRelationTree.cs
└── Subscriptions
│ ├── ISessionSubscription.cs
│ ├── ITransactionSubscription.cs
│ ├── SessionSubscription.cs
│ ├── SessionSubscriptionExtension.cs
│ ├── TransactionListener.cs
│ └── TransactionSubscription.cs
├── README.md
└── build.cake
/.config/dotnet-tools.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 1,
3 | "isRoot": true,
4 | "tools": {
5 | "csharpasyncgenerator.tool": {
6 | "version": "0.19.1",
7 | "commands": [
8 | "async-generator"
9 | ]
10 | },
11 | "cake.tool": {
12 | "version": "1.0.0-rc0003",
13 | "commands": [
14 | "dotnet-cake"
15 | ]
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig is awesome:http://EditorConfig.org
2 |
3 | # top-most EditorConfig file
4 | root = true
5 |
6 | # Don't use tabs for indentation.
7 | [*]
8 | indent_style = space
9 | # (Please don't specify an indent_size here; that has too many unintended consequences.)
10 |
11 | # Code files
12 | [*.{cs,csx,vb,vbx}]
13 | indent_size = 4
14 | insert_final_newline = true
15 | charset = utf-8-bom
16 |
17 | # Xml project files
18 | [*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}]
19 | indent_size = 4
20 |
21 | # Xml config files
22 | [*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}]
23 | indent_size = 2
24 |
25 | [{src,scripts}/**.{ts,json,js}]
26 | end_of_line = crlf
27 | charset = utf-8
28 | trim_trailing_whitespace = true
29 | insert_final_newline = true
30 | indent_style = space
31 | indent_size = 4
32 |
33 | # Dotnet code style settings:
34 | [*.{cs,vb}]
35 | # Sort using and Import directives with System.* appearing first
36 | dotnet_sort_system_directives_first = true
37 | # Avoid "this." and "Me." if not necessary
38 | dotnet_style_qualification_for_field = false:suggestion
39 | dotnet_style_qualification_for_property = false:suggestion
40 | dotnet_style_qualification_for_method = false:suggestion
41 | dotnet_style_qualification_for_event = false:suggestion
42 |
43 | # Use language keywords instead of framework type names for type references
44 | dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
45 | dotnet_style_predefined_type_for_member_access = true:suggestion
46 |
47 | # Suggest more modern language features when available
48 | dotnet_style_object_initializer = true:suggestion
49 | dotnet_style_collection_initializer = true:suggestion
50 | dotnet_style_coalesce_expression = true:suggestion
51 | dotnet_style_null_propagation = true:suggestion
52 | dotnet_style_explicit_tuple_names = true:suggestion
53 |
54 | # CSharp code style settings:
55 | [*.cs]
56 | # Prefer "var" everywhere
57 | csharp_style_var_for_built_in_types = true:suggestion
58 | csharp_style_var_when_type_is_apparent = true:suggestion
59 | csharp_style_var_elsewhere = true:suggestion
60 |
61 | # Prefer method-like constructs to have a block body
62 | csharp_style_expression_bodied_methods = false:none
63 | csharp_style_expression_bodied_constructors = false:none
64 | csharp_style_expression_bodied_operators = false:none
65 |
66 | # Prefer property-like constructs to have an expression-body
67 | csharp_style_expression_bodied_properties = true:none
68 | csharp_style_expression_bodied_indexers = true:none
69 | csharp_style_expression_bodied_accessors = true:none
70 |
71 | # Suggest more modern language features when available
72 | csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
73 | csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
74 | csharp_style_inlined_variable_declaration = true:suggestion
75 | csharp_style_throw_expression = true:suggestion
76 | csharp_style_conditional_delegate_call = true:suggestion
77 |
78 | # Newline settings
79 | csharp_new_line_before_open_brace = all
80 | csharp_new_line_before_else = true
81 | csharp_new_line_before_catch = true
82 | csharp_new_line_before_finally = true
83 | csharp_new_line_before_members_in_object_initializers = true
84 | csharp_new_line_before_members_in_anonymous_types = true
85 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | [Xx]64/
19 | [Xx]86/
20 | [Bb]uild/
21 | bld/
22 | [Bb]in/
23 | [Oo]bj/
24 |
25 | # Visual Studio 2015 cache/options directory
26 | .vs/
27 | # Uncomment if you have tasks that create the project's static files in wwwroot
28 | #wwwroot/
29 |
30 | # MSTest test Results
31 | [Tt]est[Rr]esult*/
32 | [Bb]uild[Ll]og.*
33 |
34 | # NUNIT
35 | *.VisualState.xml
36 | TestResult.xml
37 |
38 | # Build Results of an ATL Project
39 | [Dd]ebugPS/
40 | [Rr]eleasePS/
41 | dlldata.c
42 |
43 | # DNX
44 | project.lock.json
45 | artifacts/
46 |
47 | *_i.c
48 | *_p.c
49 | *_i.h
50 | *.ilk
51 | *.meta
52 | *.obj
53 | *.pch
54 | *.pdb
55 | *.pgc
56 | *.pgd
57 | *.rsp
58 | *.sbr
59 | *.tlb
60 | *.tli
61 | *.tlh
62 | *.tmp
63 | *.tmp_proj
64 | *.log
65 | *.vspscc
66 | *.vssscc
67 | .builds
68 | *.pidb
69 | *.svclog
70 | *.scc
71 |
72 | # Chutzpah Test files
73 | _Chutzpah*
74 |
75 | # Visual C++ cache files
76 | ipch/
77 | *.aps
78 | *.ncb
79 | *.opendb
80 | *.opensdf
81 | *.sdf
82 | *.cachefile
83 | *.VC.db
84 |
85 | # Visual Studio profiler
86 | *.psess
87 | *.vsp
88 | *.vspx
89 | *.sap
90 |
91 | # TFS 2012 Local Workspace
92 | $tf/
93 |
94 | # Guidance Automation Toolkit
95 | *.gpState
96 |
97 | # ReSharper is a .NET coding add-in
98 | _ReSharper*/
99 | *.[Rr]e[Ss]harper
100 | *.DotSettings.user
101 |
102 | # JustCode is a .NET coding add-in
103 | .JustCode
104 |
105 | # TeamCity is a build add-in
106 | _TeamCity*
107 |
108 | # DotCover is a Code Coverage Tool
109 | *.dotCover
110 |
111 | # NCrunch
112 | _NCrunch_*
113 | .*crunch*.local.xml
114 | nCrunchTemp_*
115 |
116 | # MightyMoose
117 | *.mm.*
118 | AutoTest.Net/
119 |
120 | # Web workbench (sass)
121 | .sass-cache/
122 |
123 | # Installshield output folder
124 | [Ee]xpress/
125 |
126 | # DocProject is a documentation generator add-in
127 | DocProject/buildhelp/
128 | DocProject/Help/*.HxT
129 | DocProject/Help/*.HxC
130 | DocProject/Help/*.hhc
131 | DocProject/Help/*.hhk
132 | DocProject/Help/*.hhp
133 | DocProject/Help/Html2
134 | DocProject/Help/html
135 |
136 | # Click-Once directory
137 | publish/
138 |
139 | # Publish Web Output
140 | *.[Pp]ublish.xml
141 | *.azurePubxml
142 |
143 | # TODO: Un-comment the next line if you do not want to checkin
144 | # your web deploy settings because they may include unencrypted
145 | # passwords
146 | #*.pubxml
147 | *.publishproj
148 |
149 | # NuGet Packages
150 | *.nupkg
151 | # The packages folder can be ignored because of Package Restore
152 | **/packages/*
153 | # except build/, which is used as an MSBuild target.
154 | !**/packages/build/
155 | # Uncomment if necessary however generally it will be regenerated when needed
156 | #!**/packages/repositories.config
157 | # NuGet v3's project.json files produces more ignoreable files
158 | *.nuget.props
159 | *.nuget.targets
160 |
161 | # Microsoft Azure Build Output
162 | csx/
163 | *.build.csdef
164 |
165 | # Microsoft Azure Emulator
166 | ecf/
167 | rcf/
168 |
169 | # Microsoft Azure ApplicationInsights config file
170 | ApplicationInsights.config
171 |
172 | # Windows Store app package directory
173 | AppPackages/
174 | BundleArtifacts/
175 |
176 | # Visual Studio cache files
177 | # files ending in .cache can be ignored
178 | *.[Cc]ache
179 | # but keep track of directories ending in .cache
180 | !*.[Cc]ache/
181 |
182 | # Others
183 | ClientBin/
184 | [Ss]tyle[Cc]op.*
185 | ~$*
186 | *~
187 | *.dbmdl
188 | *.dbproj.schemaview
189 | *.pfx
190 | *.publishsettings
191 | node_modules/
192 | orleans.codegen.cs
193 |
194 | # RIA/Silverlight projects
195 | Generated_Code/
196 |
197 | # Backup & report files from converting an old project file
198 | # to a newer Visual Studio version. Backup files are not needed,
199 | # because we have git ;-)
200 | _UpgradeReport_Files/
201 | Backup*/
202 | UpgradeLog*.XML
203 | UpgradeLog*.htm
204 |
205 | # Business Intelligence projects
206 | *.rdl.data
207 | *.bim.layout
208 | *.bim_*.settings
209 |
210 | # Microsoft Fakes
211 | FakesAssemblies/
212 |
213 | # GhostDoc plugin setting file
214 | *.GhostDoc.xml
215 |
216 | # Node.js Tools for Visual Studio
217 | .ntvs_analysis.dat
218 |
219 | # Visual Studio 6 build log
220 | *.plg
221 |
222 | # Visual Studio 6 workspace options file
223 | *.opt
224 |
225 | # Visual Studio LightSwitch build output
226 | **/*.HTMLClient/GeneratedArtifacts
227 | **/*.DesktopClient/GeneratedArtifacts
228 | **/*.DesktopClient/ModelManifest.xml
229 | **/*.Server/GeneratedArtifacts
230 | **/*.Server/ModelManifest.xml
231 | _Pvt_Extensions
232 |
233 | # LightSwitch generated files
234 | GeneratedArtifacts/
235 | ModelManifest.xml
236 |
237 | # Paket dependency manager
238 | .paket/paket.exe
239 |
240 | # FAKE - F# Make
241 | .fake/
242 | [Tt]ools/
243 | .idea/
244 | *.dev.props
245 |
246 | package/
--------------------------------------------------------------------------------
/AsyncGenerator.yml:
--------------------------------------------------------------------------------
1 | projects:
2 | - filePath: Source/NHibernate.Extensions/NHibernate.Extensions.csproj
3 | targetFramework: net461
4 | concurrentRun: false
5 | applyChanges: true
6 | analyzation:
7 | methodConversion:
8 | - conversion: ToAsync
9 | containingTypeName: IBatchFetchBuilder
10 | name: Execute
11 | - conversion: Smart
12 | name: LoadEntity
13 | callForwarding: true
14 | cancellationTokens:
15 | guards: true
16 | methodParameter:
17 | - parameter: Optional
18 | scanMethodBody: true
19 | searchAsyncCounterpartsInInheritedTypes: true
20 | scanForMissingAsyncMembers:
21 | - all: true
22 | transformation:
23 | configureAwaitArgument: false
24 | localFunctions: true
25 | registerPlugin:
26 | - type: AsyncGenerator.Core.Plugins.EmptyRegionRemover
27 | assemblyName: AsyncGenerator.Core
28 | - filePath: Source/NHibernate.Extensions.Tests/NHibernate.Extensions.Tests.csproj
29 | targetFramework: net461
30 | concurrentRun: false
31 | applyChanges: true
32 | analyzation:
33 | methodConversion:
34 | - conversion: Ignore
35 | name: FillData
36 | cancellationTokens:
37 | enabled: true
38 | exceptionHandling:
39 | catchMethodBody:
40 | - all: true
41 | result: false
42 | scanMethodBody: true
43 | searchAsyncCounterpartsInInheritedTypes: true
44 | scanForMissingAsyncMembers:
45 | - all: true
46 | registerPlugin:
47 | - type: AsyncGenerator.Core.Plugins.NUnitPlugin
48 | parameters:
49 | - name: createNewTypes
50 | value: false
51 | assemblyName: AsyncGenerator.Core
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 maca88
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 all
13 | 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 THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/NHibernate.Extensions.Tests/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/NHibernate.Extensions.Tests/Async/BatchFetchTests.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by AsyncGenerator.
4 | //
5 | // Changes to this file may cause incorrect behavior and will be lost if
6 | // the code is regenerated.
7 | //
8 | //------------------------------------------------------------------------------
9 |
10 |
11 | using System;
12 | using System.Collections.Generic;
13 | using System.Diagnostics;
14 | using System.Linq;
15 | using NHibernate.Extensions.Tests.Entities;
16 | using NHibernate.Tool.hbm2ddl;
17 | using NUnit.Framework;
18 |
19 | namespace NHibernate.Extensions.Tests
20 | {
21 | using System.Threading.Tasks;
22 | public partial class BatchFetchTests
23 | {
24 |
25 | [Test]
26 | public async Task TestSelectAnonymousTypeAsync()
27 | {
28 | var keys = Enumerable.Range(1, 600).ToList();
29 |
30 | using (var session = NHConfig.OpenSession())
31 | {
32 | var stats = session.SessionFactory.Statistics;
33 | var queryCount = stats.PrepareStatementCount;
34 | var models = await (session.BatchFetch(50)
35 | .SetKeys(keys, o => o.Id)
36 | .BeforeQueryExecution(q => q.Where(o => o.Id > 400))
37 | .Select(o => new { o.Name })
38 | .ExecuteAsync());
39 |
40 | var expectedQueryCount = (int)Math.Ceiling(keys.Count / 50m);
41 | Assert.AreEqual(200, models.Count);
42 | Assert.AreEqual(expectedQueryCount, stats.PrepareStatementCount - queryCount);
43 | }
44 | }
45 |
46 | [Test]
47 | public async Task TestSelectStringAsync()
48 | {
49 | var keys = Enumerable.Range(1, 600).ToList();
50 |
51 | using (var session = NHConfig.OpenSession())
52 | {
53 | var stats = session.SessionFactory.Statistics;
54 | var queryCount = stats.PrepareStatementCount;
55 | var models = await (session.BatchFetch(50)
56 | .SetKeys(keys, o => o.Id)
57 | .Select(o => o.Name)
58 | .BeforeQueryExecution(q => q.Where(o => o.Id > 400))
59 | .ExecuteAsync());
60 |
61 | var expectedQueryCount = (int)Math.Ceiling(keys.Count / 50m);
62 | Assert.AreEqual(200, models.Count);
63 | Assert.AreEqual(expectedQueryCount, stats.PrepareStatementCount - queryCount);
64 | }
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/NHibernate.Extensions.Tests/Async/DeepCloneTests.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by AsyncGenerator.
4 | //
5 | // Changes to this file may cause incorrect behavior and will be lost if
6 | // the code is regenerated.
7 | //
8 | //------------------------------------------------------------------------------
9 |
10 |
11 | using System;
12 | using System.Collections.Generic;
13 | using System.Linq;
14 | using NHibernate.Extensions.Tests.Entities;
15 | using NHibernate.Linq;
16 | using NUnit.Framework;
17 |
18 | namespace NHibernate.Extensions.Tests
19 | {
20 | using System.Threading.Tasks;
21 | public partial class DeepCloneTests : BaseIncludeTest
22 | {
23 | [Test]
24 | public async Task TestSimplePropertiesAsync()
25 | {
26 | EQBPerson clone;
27 | EQBPerson petra;
28 |
29 | using (var session = NHConfig.OpenSession())
30 | {
31 | petra = session.Query()
32 | .First(o => o.Name == "Petra");
33 | clone = await (session.DeepCloneAsync(petra, o => o
34 | .ForType(t =>
35 | t.ForMember(m => m.Name, opts => opts.Ignore())
36 | )));
37 | // Lazy load some relations after cloning
38 | var friend = petra.BestFriend;
39 | var card = petra.IdentityCard;
40 |
41 | }
42 | Assert.AreEqual(petra.Id, clone.Id);
43 | Assert.AreEqual(null, clone.Name);
44 | Assert.AreEqual(petra.LastName, clone.LastName);
45 | Assert.IsNotNull(petra.BestFriend);
46 | Assert.IsNotNull(petra.IdentityCard);
47 | Assert.IsNull(clone.MarriedWith);
48 | Assert.IsNull(clone.BestFriend);
49 | Assert.IsNull(clone.IdentityCard);
50 | Assert.AreEqual(0, clone.OwnedHouses.Count);
51 | Assert.AreEqual(0, clone.CurrentOwnedVehicles.Count);
52 | }
53 |
54 | [Test]
55 | public async Task TestCloneAsReferenceAndIgnoreIdentifiersAsync()
56 | {
57 | EQBPerson clone;
58 | EQBPerson petra;
59 |
60 | using (var session = NHConfig.OpenSession())
61 | {
62 | petra = session.Query()
63 | .Include(o => o.IdentityCard)
64 | .First(o => o.Name == "Petra");
65 | clone = await (session.DeepCloneAsync(petra, o => o
66 | .ForType(t => t
67 | .ForMember(m => m.Name, opts => opts.Ignore())
68 | .CloneIdentifier(false)
69 | )
70 | .CloneIdentifier(true)
71 | .CanCloneAsReference(type => type == typeof(EQBIdentityCard))
72 | ));
73 |
74 | }
75 | Assert.AreEqual(default(int), clone.Id);
76 | Assert.IsNull(clone.Name);
77 | Assert.AreEqual(petra.LastName, clone.LastName);
78 | Assert.AreEqual(petra.IdentityCard, clone.IdentityCard);
79 | }
80 |
81 | [Test]
82 | public async Task TestReferencesAsync()
83 | {
84 | EQBPerson clone;
85 | EQBPerson petra;
86 |
87 | using (var session = NHConfig.OpenSession())
88 | {
89 | petra = session.Query()
90 | .Include(o => o.BestFriend.IdentityCard)
91 | .Include(o => o.BestFriend.BestFriend)
92 | .First(o => o.Name == "Petra");
93 |
94 | clone = await (session.DeepCloneAsync(petra));
95 | }
96 |
97 | Assert.AreEqual(petra.Id, clone.Id);
98 | Assert.IsNotNull(clone.BestFriend);
99 | Assert.IsNotNull(clone.BestFriend.IdentityCard);
100 | Assert.AreEqual(clone.BestFriend, clone.BestFriend.IdentityCard.Owner);
101 | Assert.IsNotNull(clone.BestFriend.BestFriend);
102 |
103 | Assert.IsNull(clone.BestFriend.BestFriend.BestFriend);
104 | Assert.IsNull(clone.IdentityCard);
105 | Assert.AreEqual(0, clone.OwnedHouses.Count);
106 | Assert.AreEqual(0, clone.CurrentOwnedVehicles.Count);
107 | }
108 |
109 | [Test]
110 | public async Task TestEntityResolverAsync()
111 | {
112 | EQBPerson clone;
113 | EQBPerson petra;
114 |
115 | using (var session = NHConfig.OpenSession())
116 | {
117 | petra = session.Query()
118 | .Include(o => o.BestFriend.IdentityCard)
119 | .Include(o => o.BestFriend.BestFriend)
120 | .First(o => o.Name == "Petra");
121 |
122 | clone = await (session.DeepCloneAsync(petra, o => o
123 | .AddEntityResolver(t => true, (entity, persister) => persister.CreateProxy(persister.GetIdentifier(entity), null))));
124 |
125 | Assert.AreEqual(petra.Id, clone.Id);
126 | Assert.False(NHibernateUtil.IsInitialized(clone));
127 | Assert.Throws(() =>
128 | {
129 | var friend = clone.BestFriend;
130 | });
131 | }
132 | }
133 |
134 | [Test]
135 | public async Task TestEntityResolverSkipRootAsync()
136 | {
137 | EQBPerson clone;
138 | EQBPerson petra;
139 |
140 | using (var session = NHConfig.OpenSession())
141 | {
142 | petra = session.Query()
143 | .Include(o => o.BestFriend.IdentityCard)
144 | .Include(o => o.BestFriend.BestFriend)
145 | .First(o => o.Name == "Petra");
146 |
147 | clone = await (session.DeepCloneAsync(petra, o => o
148 | .AddEntityResolver((t, e) => e != petra, (entity, persister) => persister.CreateProxy(persister.GetIdentifier(entity), null))));
149 |
150 | Assert.AreEqual(petra.Id, clone.Id);
151 | Assert.AreEqual(petra.Name, clone.Name);
152 | Assert.True(NHibernateUtil.IsInitialized(clone));
153 |
154 | Assert.False(NHibernateUtil.IsInitialized(clone.BestFriend));
155 | Assert.Throws(() =>
156 | {
157 | var card = clone.BestFriend.IdentityCard;
158 | });
159 | }
160 | }
161 |
162 | [Test]
163 | public async Task TestCollectionsAsync()
164 | {
165 | EQBPerson clone;
166 | EQBPerson petra;
167 |
168 | using (var session = NHConfig.OpenSession())
169 | {
170 | petra = session.Query()
171 | .Include(o => o.CurrentOwnedVehicles.First().Wheels)
172 | .Include(o => o.CurrentOwnedVehicles.First().RoadworthyTests)
173 | .Include(o => o.CurrentOwnedVehicles.First().MileageHistory)
174 | .Include(o => o.PreviouslyOwnedVehicles)
175 | .First(o => o.Name == "Petra");
176 |
177 | clone = await (session.DeepCloneAsync(petra));
178 | }
179 |
180 | Assert.IsNull(clone.BestFriend);
181 | Assert.IsNull(clone.IdentityCard);
182 | Assert.IsNull(clone.MarriedWith);
183 | Assert.AreEqual(1, clone.CurrentOwnedVehicles.Count);
184 | Assert.AreEqual(clone, clone.CurrentOwnedVehicles.First().CurrentOwner);
185 | Assert.AreEqual(4, clone.CurrentOwnedVehicles.First().Wheels.Count);
186 | Assert.AreEqual(clone.CurrentOwnedVehicles.First(), clone.CurrentOwnedVehicles.First().Wheels.First().Vehicle);
187 | Assert.AreEqual(2, clone.CurrentOwnedVehicles.First().RoadworthyTests.Count);
188 | Assert.AreEqual(clone.CurrentOwnedVehicles.First(), clone.CurrentOwnedVehicles.First().RoadworthyTests[new DateTime(2009, 2, 1)].Vehicle);
189 | Assert.AreEqual(2, clone.CurrentOwnedVehicles.First().MileageHistory.Count);
190 | Assert.AreEqual(5000, clone.CurrentOwnedVehicles.First().MileageHistory[new DateTime(2010, 1, 1)]);
191 |
192 | Assert.AreEqual(2, clone.PreviouslyOwnedVehicles.Count);
193 | Assert.AreEqual(clone, clone.CurrentOwnedVehicles.First().CurrentOwner);
194 | }
195 |
196 | [Test]
197 | public async Task TestFilterAsync()
198 | {
199 | EQBPerson clone;
200 | EQBPerson petra;
201 |
202 | using (var session = NHConfig.OpenSession())
203 | {
204 | petra = session.Query()
205 | .Include(o => o.PreviouslyOwnedVehicles)
206 | .First(o => o.Name == "Petra");
207 |
208 | clone = await (session.DeepCloneAsync(petra, o => o
209 | .ForType(t => t
210 | .ForMember(m => m.Name, m => m.Filter(n => n + "2"))
211 | .ForMember(m => m.PreviouslyOwnedVehicles, m => m
212 | .Filter(col => new HashSet(col.Take(1)))
213 | )
214 | )));
215 | }
216 |
217 | Assert.AreEqual("Petra2", clone.Name);
218 | Assert.IsNull(clone.BestFriend);
219 | Assert.IsNull(clone.IdentityCard);
220 | Assert.IsNull(clone.MarriedWith);
221 | Assert.AreEqual(0, clone.CurrentOwnedVehicles.Count);
222 | Assert.AreEqual(1, clone.PreviouslyOwnedVehicles.Count);
223 | }
224 |
225 | [Test]
226 | public async Task TestSkipEntityTypesAsync()
227 | {
228 | EQBPerson clone;
229 | EQBPerson petra;
230 |
231 | using (var session = NHConfig.OpenSession())
232 | {
233 | petra = session.Query()
234 | .Include(o => o.IdentityCard)
235 | .Include(o => o.BestFriend.IdentityCard)
236 | .Include(o => o.CurrentOwnedVehicles.First().Wheels)
237 | .Include(o => o.CurrentOwnedVehicles.First().RoadworthyTests)
238 | .Include(o => o.CurrentOwnedVehicles.First().MileageHistory)
239 | .First(o => o.Name == "Petra");
240 |
241 | clone = await (session.DeepCloneAsync(petra, o => o
242 | .SkipEntityTypes()));
243 | }
244 |
245 | Assert.IsNull(clone.IdentityCard);
246 | Assert.IsNull(clone.BestFriend);
247 | Assert.AreEqual(1, clone.CurrentOwnedVehicles.Count);
248 | Assert.IsNull(clone.CurrentOwnedVehicles.First().CurrentOwner);
249 | Assert.AreEqual(4, clone.CurrentOwnedVehicles.First().Wheels.Count);
250 | Assert.IsNull(clone.CurrentOwnedVehicles.First().Wheels.First().Vehicle);
251 | Assert.AreEqual(2, clone.CurrentOwnedVehicles.First().RoadworthyTests.Count);
252 | Assert.IsNull(clone.CurrentOwnedVehicles.First().RoadworthyTests.First().Value.Vehicle);
253 | Assert.AreEqual(2, clone.CurrentOwnedVehicles.First().MileageHistory.Count);
254 | Assert.IsNotNull(clone.CurrentOwnedVehicles.First().MileageHistory[new DateTime(2010, 1, 1)]);
255 | }
256 |
257 | [Test]
258 | public async Task TestWithoutIdentifiersAsync()
259 | {
260 | EQBPerson clone;
261 |
262 | using (var session = NHConfig.OpenSession())
263 | {
264 | var petra = session.Query()
265 | .Include(o => o.IdentityCard)
266 | .Include(o => o.BestFriend.IdentityCard)
267 | .Include(o => o.CurrentOwnedVehicles.First().Wheels)
268 | .Include(o => o.CurrentOwnedVehicles.First().RoadworthyTests)
269 | .Include(o => o.CurrentOwnedVehicles.First().MileageHistory)
270 | .First(o => o.Name == "Petra");
271 |
272 | clone = await (session.DeepCloneAsync(petra, o => o
273 | .CloneIdentifier(false)));
274 | }
275 |
276 | Assert.AreEqual(0, clone.Id);
277 | Assert.AreEqual(0, clone.IdentityCard.Id);
278 | Assert.AreEqual(0, clone.IdentityCard.Owner.Id);
279 | Assert.AreEqual(0, clone.BestFriend.Id);
280 | Assert.AreEqual(0, clone.BestFriend.IdentityCard.Id);
281 | Assert.AreEqual(1, clone.CurrentOwnedVehicles.Count);
282 | Assert.AreEqual(0, clone.CurrentOwnedVehicles.First().Id);
283 | Assert.AreEqual(0, clone.CurrentOwnedVehicles.First().CurrentOwner.Id);
284 | Assert.AreEqual(4, clone.CurrentOwnedVehicles.First().Wheels.Count);
285 | Assert.AreEqual(0, clone.CurrentOwnedVehicles.First().Wheels.First().Id);
286 | Assert.AreEqual(0, clone.CurrentOwnedVehicles.First().Wheels.First().Vehicle.Id);
287 | Assert.AreEqual(2, clone.CurrentOwnedVehicles.First().RoadworthyTests.Count);
288 | Assert.AreEqual(0, clone.CurrentOwnedVehicles.First().RoadworthyTests.First().Value.Id);
289 | Assert.AreEqual(0, clone.CurrentOwnedVehicles.First().RoadworthyTests.First().Value.Vehicle.Id);
290 | }
291 | }
292 | }
293 |
--------------------------------------------------------------------------------
/NHibernate.Extensions.Tests/Async/LinqIncludeTests.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by AsyncGenerator.
4 | //
5 | // Changes to this file may cause incorrect behavior and will be lost if
6 | // the code is regenerated.
7 | //
8 | //------------------------------------------------------------------------------
9 |
10 |
11 | using System;
12 | using System.Collections.Generic;
13 | using System.Linq;
14 | using System.Reflection;
15 | using System.Threading.Tasks;
16 | using NHibernate.Extensions.Linq;
17 | using NHibernate.Extensions.Tests.Entities;
18 | using NHibernate.Linq;
19 | using NHibernate.Stat;
20 | using NUnit.Framework;
21 |
22 | namespace NHibernate.Extensions.Tests
23 | {
24 | public partial class LinqIncludeTests : BaseIncludeTest
25 | {
26 |
27 | [Test]
28 | public async Task TestUsingAndThenIncludeAsync()
29 | {
30 | EQBPerson petra;
31 |
32 | using (var session = NHConfig.OpenSession())
33 | {
34 | var stats = session.SessionFactory.Statistics;
35 | stats.Clear();
36 | var future = session.Query()
37 | .Include(o => o.BestFriend.IdentityCard)
38 | .Include(o => o.BestFriend.BestFriend.BestFriend.BestFriend)
39 | .Include(o => o.CurrentOwnedVehicles).ThenInclude(o => o.Wheels)
40 | .Include(o => o.CurrentOwnedVehicles).ThenInclude(o => o.RoadworthyTests)
41 | .Include(o => o.CurrentOwnedVehicles).ThenInclude(o => o.MileageHistory)
42 | .Include(o => o.DrivingLicence)
43 | .Include(o => o.CreatedBy)
44 | .Include(o => o.IdentityCard)
45 | .Include(o => o.MarriedWith)
46 | .Include(o => o.OwnedHouses)
47 | .Include(o => o.PreviouslyOwnedVehicles)
48 | .Where(o => o.Name == "Petra")
49 | .ToFutureValue();
50 | petra = await (future.GetValueAsync());
51 | CheckStatistics(stats, 5);
52 | }
53 | ValidateGetEntityResult(petra);
54 | }
55 |
56 | #region FutureValue
57 |
58 | #endregion
59 | #region SingleOrDefault
60 |
61 | #endregion
62 | #region Single
63 |
64 | #endregion
65 | #region FirstOrDefault
66 |
67 | #endregion
68 | #region First
69 |
70 | #endregion
71 | #region LastOrDefault
72 |
73 | #endregion
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/NHibernate.Extensions.Tests/BaseIncludeTest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Text.RegularExpressions;
6 | using NHibernate.Engine;
7 | using NHibernate.Extensions.Tests.Entities;
8 | using NHibernate.Tool.hbm2ddl;
9 | using NUnit.Framework;
10 |
11 | namespace NHibernate.Extensions.Tests
12 | {
13 | [TestFixture]
14 | public abstract class BaseIncludeTest
15 | {
16 | protected void ValidateGetEntityResult(EQBPerson petra)
17 | {
18 | Assert.AreEqual("Ana", petra.BestFriend.Name);
19 | Assert.AreEqual("Simon", petra.BestFriend.BestFriend.Name);
20 | Assert.AreEqual("1", petra.BestFriend.IdentityCard.Code);
21 | Assert.AreEqual("Rok", petra.BestFriend.BestFriend.BestFriend.Name);
22 | Assert.AreEqual("Petra", petra.BestFriend.BestFriend.BestFriend.BestFriend.Name);
23 | Assert.AreEqual("Ana", petra.BestFriend.BestFriend.BestFriend.BestFriend.BestFriend.Name);
24 | Assert.AreEqual(1, petra.CurrentOwnedVehicles.Count);
25 | Assert.AreEqual("3", petra.DrivingLicence.Code);
26 | Assert.AreEqual("4", petra.IdentityCard.Code);
27 | Assert.AreEqual("System", petra.CreatedBy.UserName);
28 | Assert.AreEqual(petra.BestFriend.BestFriend, petra.MarriedWith);
29 | Assert.AreEqual(1, petra.OwnedHouses.Count);
30 | Assert.AreEqual(2, petra.PreviouslyOwnedVehicles.Count);
31 | Assert.AreEqual("Audi", petra.CurrentOwnedVehicles.First().Model);
32 | Assert.AreEqual(2, petra.CurrentOwnedVehicles.First().RoadworthyTests.Count);
33 | Assert.AreEqual(2, petra.CurrentOwnedVehicles.First().MileageHistory.Count);
34 | Assert.AreEqual(5000, petra.CurrentOwnedVehicles.First().MileageHistory[new DateTime(2010, 1, 1)]);
35 | foreach (var wheel in petra.CurrentOwnedVehicles.First().Wheels)
36 | {
37 | Assert.AreEqual(235, wheel.Width);
38 | }
39 | }
40 |
41 | [OneTimeSetUp]
42 | public void Initialize()
43 | {
44 | var schema = new SchemaExport(NHConfig.Configuration);
45 | schema.Drop(false, true);
46 | schema.Create(false, true);
47 | FillData();
48 | }
49 |
50 | [OneTimeTearDown]
51 | public void Cleanup()
52 | {
53 | var schema = new SchemaExport(NHConfig.Configuration);
54 | schema.Drop(false, true);
55 | }
56 |
57 | protected void FillData()
58 | {
59 | var system = new EQBUser { UserName = "System" };
60 | var ana = new EQBPerson("Ana") { Age = 23, CreatedBy = system };
61 | var rok = new EQBPerson("Rok") { Age = 24, CreatedBy = system };
62 | var simon = new EQBPerson("Simon") { Age = 25, CreatedBy = system };
63 | var petra = new EQBPerson("Petra") { Age = 22, CreatedBy = system };
64 |
65 | //Setting best friends
66 | petra.BestFriend = ana;
67 | ana.BestFriend = simon;
68 | simon.BestFriend = rok;
69 | rok.BestFriend = petra;
70 |
71 | //Setting Identity card
72 | ana.IdentityCard = new EQBIdentityCard { Code = "1", Owner = ana };
73 | ana.Identity = new EQBIdentity { Code = "1", Owner = ana };
74 | rok.IdentityCard = new EQBIdentityCard { Code = "2", Owner = rok };
75 | rok.Identity = new EQBIdentity { Code = "2", Owner = rok };
76 | simon.IdentityCard = new EQBIdentityCard { Code = "3", Owner = simon };
77 | simon.Identity = new EQBIdentity { Code = "3", Owner = simon };
78 | petra.IdentityCard = new EQBIdentityCard { Code = "4", Owner = petra };
79 | petra.Identity = new EQBIdentity { Code = "4", Owner = petra };
80 |
81 | //Setting Driving licence
82 | rok.DrivingLicence = new EQBDrivingLicence { Code = "1", Owner = rok };
83 | simon.DrivingLicence = new EQBDrivingLicence { Code = "2", Owner = simon };
84 | petra.DrivingLicence = new EQBDrivingLicence { Code = "3", Owner = petra };
85 |
86 | //Setting MerriedWith
87 | rok.MarriedWith = ana;
88 | ana.MarriedWith = rok;
89 |
90 | petra.MarriedWith = simon;
91 | simon.MarriedWith = petra;
92 |
93 | //Setting Vehicles
94 | var ferrari = new EQBVehicle { BuildYear = 2002, Model = "Ferrari" };
95 | ferrari.Wheels.Add(new TestEQBWheel { Diameter = 45, Width = 320, Vehicle = ferrari });
96 | ferrari.Wheels.Add(new TestEQBWheel { Diameter = 45, Width = 320, Vehicle = ferrari });
97 | ferrari.Wheels.Add(new TestEQBWheel { Diameter = 45, Width = 260, Vehicle = ferrari });
98 | ferrari.Wheels.Add(new TestEQBWheel { Diameter = 45, Width = 260, Vehicle = ferrari });
99 | ferrari.RoadworthyTests.Add(
100 | new DateTime(2002, 2, 1),
101 | new EQBRoadworthyTest
102 | {
103 | Vehicle = ferrari,
104 | TestDate = new DateTime(2002, 2, 1),
105 | Passed = true,
106 | Comments = "I like the shade of red."
107 | });
108 | ferrari.MileageHistory.Add(new DateTime(2002, 1, 1), 0);
109 | ferrari.MileageHistory.Add(new DateTime(2006, 1, 1), 60000);
110 | ferrari.MileageHistory.Add(new DateTime(2010, 1, 1), 100000);
111 |
112 | var audi = new EQBVehicle { BuildYear = 2009, Model = "Audi" };
113 | audi.Wheels.Add(new TestEQBWheel { Diameter = 45, Width = 235, Vehicle = audi });
114 | audi.Wheels.Add(new TestEQBWheel { Diameter = 45, Width = 235, Vehicle = audi });
115 | audi.Wheels.Add(new TestEQBWheel { Diameter = 45, Width = 235, Vehicle = audi });
116 | audi.Wheels.Add(new TestEQBWheel { Diameter = 45, Width = 235, Vehicle = audi });
117 | audi.RoadworthyTests.Add(
118 | new DateTime(2009, 2, 1),
119 | new EQBRoadworthyTest
120 | {
121 | Vehicle = audi,
122 | TestDate = new DateTime(2009, 2, 1),
123 | Passed = false,
124 | Comments = "Brakes failing."
125 | });
126 | audi.RoadworthyTests.Add(
127 | new DateTime(2009, 3, 1),
128 | new EQBRoadworthyTest
129 | {
130 | Vehicle = audi,
131 | TestDate = new DateTime(2009, 3, 1),
132 | Passed = true,
133 | Comments = "All good now."
134 | });
135 | audi.MileageHistory.Add(new DateTime(2009, 1, 1), 0);
136 | audi.MileageHistory.Add(new DateTime(2010, 1, 1), 5000);
137 |
138 | var bmw = new EQBVehicle { BuildYear = 1993, Model = "Bmw" };
139 | bmw.Wheels.Add(new TestEQBWheel { Diameter = 45, Width = 205, Vehicle = bmw });
140 | bmw.Wheels.Add(new TestEQBWheel { Diameter = 45, Width = 205, Vehicle = bmw });
141 | bmw.Wheels.Add(new TestEQBWheel { Diameter = 45, Width = 205, Vehicle = bmw });
142 | bmw.Wheels.Add(new TestEQBWheel { Diameter = 45, Width = 205, Vehicle = bmw });
143 | // Deliberately no roadworthy tests or mileage history
144 |
145 | var vw = new EQBVehicle { BuildYear = 2002, Model = "Vw" };
146 | vw.Wheels.Add(new TestEQBWheel { Diameter = 45, Width = 195, Vehicle = vw });
147 | vw.Wheels.Add(new TestEQBWheel { Diameter = 45, Width = 195, Vehicle = vw });
148 | vw.Wheels.Add(new TestEQBWheel { Diameter = 45, Width = 195, Vehicle = vw });
149 | vw.Wheels.Add(new TestEQBWheel { Diameter = 45, Width = 195, Vehicle = vw });
150 | vw.RoadworthyTests.Add(
151 | new DateTime(2002, 3, 1),
152 | new EQBRoadworthyTest
153 | {
154 | Vehicle = vw,
155 | TestDate = new DateTime(2002, 3, 1),
156 | Passed = true,
157 | Comments = "No problems."
158 | });
159 | vw.MileageHistory.Add(new DateTime(2002, 1, 1), 0);
160 | vw.MileageHistory.Add(new DateTime(2015, 1, 1), 150000);
161 |
162 | petra.PreviouslyOwnedVehicles.Add(vw);
163 | petra.PreviouslyOwnedVehicles.Add(bmw);
164 | petra.CurrentOwnedVehicles.Add(audi);
165 | audi.CurrentOwner = petra;
166 |
167 | simon.PreviouslyOwnedVehicles.Add(bmw);
168 | simon.PreviouslyOwnedVehicles.Add(audi);
169 | simon.CurrentOwnedVehicles.Add(ferrari);
170 | ferrari.CurrentOwner = simon;
171 |
172 | //Setting Houses
173 | var house1 = new EQBHouse { Address = "Address1" };
174 | var house2 = new EQBHouse { Address = "Address2" };
175 |
176 | house1.Owners.Add(ana);
177 | ana.OwnedHouses.Add(house1);
178 | house1.Owners.Add(rok);
179 | rok.OwnedHouses.Add(house1);
180 |
181 | house2.Owners.Add(simon);
182 | simon.OwnedHouses.Add(house2);
183 | house2.Owners.Add(petra);
184 | petra.OwnedHouses.Add(house2);
185 |
186 | //Saving entities
187 | using (var session = NHConfig.OpenSession())
188 | {
189 | session.Save(petra);
190 | session.Save(rok);
191 | session.Save(simon);
192 | session.Save(ana);
193 | session.Flush();
194 | }
195 | }
196 | }
197 | }
198 |
--------------------------------------------------------------------------------
/NHibernate.Extensions.Tests/BatchFetchTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.Linq;
5 | using NHibernate.Extensions.Tests.Entities;
6 | using NHibernate.Tool.hbm2ddl;
7 | using NUnit.Framework;
8 |
9 | namespace NHibernate.Extensions.Tests
10 | {
11 | [TestFixture]
12 | public partial class BatchFetchTests
13 | {
14 | [Test]
15 | public void TestStringProperty()
16 | {
17 | var keys = new HashSet();
18 | var r = new Random();
19 | for (var i = 0; i < 600; i++)
20 | {
21 | keys.Add($"Batch{r.Next(0, 1300)}");
22 | }
23 |
24 | using (var session = NHConfig.OpenSession())
25 | {
26 | var stats = session.SessionFactory.Statistics;
27 | var queryCount = stats.PrepareStatementCount;
28 | var models = session.BatchFetch(keys.ToList(), o => o.Name, 50);
29 |
30 | var expectedQueryCount = (int) Math.Ceiling(keys.Count/50m);
31 | Assert.AreEqual(keys.Count, models.Count);
32 | Assert.AreEqual(expectedQueryCount, stats.PrepareStatementCount - queryCount);
33 |
34 | foreach (var model in models)
35 | {
36 | Assert.IsTrue(keys.Contains(model.Name));
37 | }
38 | }
39 | }
40 |
41 | [Test]
42 | public void TestIntProperty()
43 | {
44 | var keys = new HashSet();
45 | var r = new Random();
46 | for (var i = 0; i < 600; i++)
47 | {
48 | keys.Add(r.Next(1, 1200));
49 | }
50 |
51 | using (var session = NHConfig.OpenSession())
52 | {
53 | var stats = session.SessionFactory.Statistics;
54 | var queryCount = stats.PrepareStatementCount;
55 | var models = session.BatchFetch(keys.ToList(), o => o.Id, 50);
56 |
57 | var expectedQueryCount = (int)Math.Ceiling(keys.Count / 50m);
58 | Assert.AreEqual(keys.Count, models.Count);
59 | Assert.AreEqual(expectedQueryCount, stats.PrepareStatementCount - queryCount);
60 |
61 | foreach (var model in models)
62 | {
63 | Assert.IsTrue(keys.Contains(model.Id));
64 | }
65 | }
66 | }
67 |
68 | [Test]
69 | public void TestFilter()
70 | {
71 | var keys = Enumerable.Range(1, 600).ToList();
72 |
73 | using (var session = NHConfig.OpenSession())
74 | {
75 | var stats = session.SessionFactory.Statistics;
76 | var queryCount = stats.PrepareStatementCount;
77 | var models = session.BatchFetch(keys, o => o.Id, 50, q => q.Where(o => o.Id > 400));
78 |
79 | var expectedQueryCount = (int)Math.Ceiling(keys.Count / 50m);
80 | Assert.AreEqual(200, models.Count);
81 | Assert.AreEqual(expectedQueryCount, stats.PrepareStatementCount - queryCount);
82 |
83 | foreach (var model in models)
84 | {
85 | Assert.IsTrue(keys.Contains(model.Id));
86 | }
87 | }
88 | }
89 |
90 | [Test]
91 | public void TestSelectAnonymousType()
92 | {
93 | var keys = Enumerable.Range(1, 600).ToList();
94 |
95 | using (var session = NHConfig.OpenSession())
96 | {
97 | var stats = session.SessionFactory.Statistics;
98 | var queryCount = stats.PrepareStatementCount;
99 | var models = session.BatchFetch(50)
100 | .SetKeys(keys, o => o.Id)
101 | .BeforeQueryExecution(q => q.Where(o => o.Id > 400))
102 | .Select(o => new { o.Name })
103 | .Execute();
104 |
105 | var expectedQueryCount = (int)Math.Ceiling(keys.Count / 50m);
106 | Assert.AreEqual(200, models.Count);
107 | Assert.AreEqual(expectedQueryCount, stats.PrepareStatementCount - queryCount);
108 | }
109 | }
110 |
111 | [Test]
112 | public void TestSelectString()
113 | {
114 | var keys = Enumerable.Range(1, 600).ToList();
115 |
116 | using (var session = NHConfig.OpenSession())
117 | {
118 | var stats = session.SessionFactory.Statistics;
119 | var queryCount = stats.PrepareStatementCount;
120 | var models = session.BatchFetch(50)
121 | .SetKeys(keys, o => o.Id)
122 | .Select(o => o.Name)
123 | .BeforeQueryExecution(q => q.Where(o => o.Id > 400))
124 | .Execute();
125 |
126 | var expectedQueryCount = (int)Math.Ceiling(keys.Count / 50m);
127 | Assert.AreEqual(200, models.Count);
128 | Assert.AreEqual(expectedQueryCount, stats.PrepareStatementCount - queryCount);
129 | }
130 | }
131 |
132 | [Explicit]
133 | public void TestPerformance()
134 | {
135 | var keys = Enumerable.Range(1, 5000).ToList();
136 | var batchSizes = new [] { 250, 1, 10, 50, 100, 250, 500, 1000};
137 | var coldStart = true; // skip the first time as the time is always higher
138 |
139 | foreach (var batchSize in batchSizes)
140 | {
141 | using (var session = NHConfig.OpenSession())
142 | {
143 | var stats = session.SessionFactory.Statistics;
144 | var queryCount = stats.PrepareStatementCount;
145 | var stopwatch = new Stopwatch();
146 | stopwatch.Start();
147 | var models = session.BatchFetch(keys, o => o.Id, batchSize);
148 | stopwatch.Stop();
149 | if (coldStart)
150 | {
151 | coldStart = false;
152 | }
153 | else
154 | {
155 | Console.WriteLine($"Elapsed time for batch size {batchSize}: {stopwatch.ElapsedMilliseconds}ms");
156 | }
157 | var expectedQueryCount = (int)Math.Ceiling(keys.Count / (decimal)batchSize);
158 | Assert.AreEqual(5000, models.Count);
159 | Assert.AreEqual(expectedQueryCount, stats.PrepareStatementCount - queryCount);
160 |
161 | foreach (var model in models)
162 | {
163 | Assert.IsTrue(keys.Contains(model.Id));
164 | }
165 | }
166 | }
167 | }
168 |
169 | [OneTimeSetUp]
170 | public void Initialize()
171 | {
172 | var schema = new SchemaExport(NHConfig.Configuration);
173 | schema.Drop(false, true);
174 | schema.Create(false, true);
175 | FillData();
176 | }
177 |
178 | [OneTimeTearDown]
179 | public void Cleanup()
180 | {
181 | var schema = new SchemaExport(NHConfig.Configuration);
182 | schema.Drop(false, true);
183 | }
184 |
185 | protected void FillData()
186 | {
187 | //Saving entities
188 | using (var session = NHConfig.SessionFactory.OpenStatelessSession())
189 | using (var transaction = session.BeginTransaction())
190 | {
191 | for (var i = 0; i < 5000; i++)
192 | {
193 | session.Insert(new BatchModel
194 | {
195 | Name = $"Batch{i}"
196 | });
197 | }
198 | transaction.Commit();
199 | }
200 | }
201 | }
202 | }
203 |
--------------------------------------------------------------------------------
/NHibernate.Extensions.Tests/CascadeConvention.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Reflection;
6 | using System.Text;
7 | using FluentNHibernate.Conventions;
8 | using FluentNHibernate.Conventions.Instances;
9 |
10 | namespace NHibernate.Extensions.Tests
11 | {
12 | public class CascadeConvention : IReferenceConvention, IHasManyConvention, IHasOneConvention, IHasManyToManyConvention
13 | {
14 | public void Apply(IManyToOneInstance instance)
15 | {
16 | instance.Cascade.SaveUpdate();
17 | }
18 |
19 | public void Apply(IOneToManyCollectionInstance instance)
20 | {
21 | var type = GetUnderlyingType(instance.Member);
22 | if (!typeof(IDictionary).IsAssignableFrom(type) && !IsAssignableToGenericType(type, (typeof(IDictionary<,>)))) //Map must have inverse set to false
23 | instance.Inverse();
24 | instance.Cascade.AllDeleteOrphan();
25 | }
26 |
27 | public void Apply(IManyToManyCollectionInstance instance)
28 | {
29 | instance.Cascade.SaveUpdate();
30 | }
31 |
32 | public void Apply(IOneToOneInstance instance)
33 | {
34 | instance.Cascade.SaveUpdate();
35 | }
36 |
37 | public static System.Type GetUnderlyingType(MemberInfo member)
38 | {
39 | switch (member.MemberType)
40 | {
41 | case MemberTypes.Event:
42 | return ((EventInfo)member).EventHandlerType;
43 | case MemberTypes.Field:
44 | return ((FieldInfo)member).FieldType;
45 | case MemberTypes.Method:
46 | return ((MethodInfo)member).ReturnType;
47 | case MemberTypes.Property:
48 | return ((PropertyInfo)member).PropertyType;
49 | default:
50 | throw new ArgumentException
51 | (
52 | "Input MemberInfo must be if type EventInfo, FieldInfo, MethodInfo, or PropertyInfo"
53 | );
54 | }
55 | }
56 |
57 | public static bool IsAssignableToGenericType(System.Type givenType, System.Type genericType)
58 | {
59 | var interfaceTypes = givenType.GetInterfaces();
60 |
61 | if (interfaceTypes.Any(it => it.IsGenericType && it.GetGenericTypeDefinition() == genericType))
62 | {
63 | return true;
64 | }
65 |
66 | if (givenType.IsGenericType && givenType.GetGenericTypeDefinition() == genericType)
67 | return true;
68 |
69 | var baseType = givenType.BaseType;
70 | return baseType != null && IsAssignableToGenericType(baseType, genericType);
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/NHibernate.Extensions.Tests/DeepCloneTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using NHibernate.Extensions.Tests.Entities;
5 | using NHibernate.Linq;
6 | using NUnit.Framework;
7 |
8 | namespace NHibernate.Extensions.Tests
9 | {
10 | [TestFixture]
11 | public partial class DeepCloneTests : BaseIncludeTest
12 | {
13 | [Test]
14 | public void TestSimpleProperties()
15 | {
16 | EQBPerson clone;
17 | EQBPerson petra;
18 |
19 | using (var session = NHConfig.OpenSession())
20 | {
21 | petra = session.Query()
22 | .First(o => o.Name == "Petra");
23 | clone = session.DeepClone(petra, o => o
24 | .ForType(t =>
25 | t.ForMember(m => m.Name, opts => opts.Ignore())
26 | ));
27 | // Lazy load some relations after cloning
28 | var friend = petra.BestFriend;
29 | var card = petra.IdentityCard;
30 |
31 | }
32 | Assert.AreEqual(petra.Id, clone.Id);
33 | Assert.AreEqual(null, clone.Name);
34 | Assert.AreEqual(petra.LastName, clone.LastName);
35 | Assert.IsNotNull(petra.BestFriend);
36 | Assert.IsNotNull(petra.IdentityCard);
37 | Assert.IsNull(clone.MarriedWith);
38 | Assert.IsNull(clone.BestFriend);
39 | Assert.IsNull(clone.IdentityCard);
40 | Assert.AreEqual(0, clone.OwnedHouses.Count);
41 | Assert.AreEqual(0, clone.CurrentOwnedVehicles.Count);
42 | }
43 |
44 | [Test]
45 | public void TestCloneAsReferenceAndIgnoreIdentifiers()
46 | {
47 | EQBPerson clone;
48 | EQBPerson petra;
49 |
50 | using (var session = NHConfig.OpenSession())
51 | {
52 | petra = session.Query()
53 | .Include(o => o.IdentityCard)
54 | .First(o => o.Name == "Petra");
55 | clone = session.DeepClone(petra, o => o
56 | .ForType(t => t
57 | .ForMember(m => m.Name, opts => opts.Ignore())
58 | .CloneIdentifier(false)
59 | )
60 | .CloneIdentifier(true)
61 | .CanCloneAsReference(type => type == typeof(EQBIdentityCard))
62 | );
63 |
64 | }
65 | Assert.AreEqual(default(int), clone.Id);
66 | Assert.IsNull(clone.Name);
67 | Assert.AreEqual(petra.LastName, clone.LastName);
68 | Assert.AreEqual(petra.IdentityCard, clone.IdentityCard);
69 | }
70 |
71 | [Test]
72 | public void TestReferences()
73 | {
74 | EQBPerson clone;
75 | EQBPerson petra;
76 |
77 | using (var session = NHConfig.OpenSession())
78 | {
79 | petra = session.Query()
80 | .Include(o => o.BestFriend.IdentityCard)
81 | .Include(o => o.BestFriend.BestFriend)
82 | .First(o => o.Name == "Petra");
83 |
84 | clone = session.DeepClone(petra);
85 | }
86 |
87 | Assert.AreEqual(petra.Id, clone.Id);
88 | Assert.IsNotNull(clone.BestFriend);
89 | Assert.IsNotNull(clone.BestFriend.IdentityCard);
90 | Assert.AreEqual(clone.BestFriend, clone.BestFriend.IdentityCard.Owner);
91 | Assert.IsNotNull(clone.BestFriend.BestFriend);
92 |
93 | Assert.IsNull(clone.BestFriend.BestFriend.BestFriend);
94 | Assert.IsNull(clone.IdentityCard);
95 | Assert.AreEqual(0, clone.OwnedHouses.Count);
96 | Assert.AreEqual(0, clone.CurrentOwnedVehicles.Count);
97 | }
98 |
99 | [Test]
100 | public void TestEntityResolver()
101 | {
102 | EQBPerson clone;
103 | EQBPerson petra;
104 |
105 | using (var session = NHConfig.OpenSession())
106 | {
107 | petra = session.Query()
108 | .Include(o => o.BestFriend.IdentityCard)
109 | .Include(o => o.BestFriend.BestFriend)
110 | .First(o => o.Name == "Petra");
111 |
112 | clone = session.DeepClone(petra, o => o
113 | .AddEntityResolver(t => true, (entity, persister) => persister.CreateProxy(persister.GetIdentifier(entity), null)));
114 |
115 | Assert.AreEqual(petra.Id, clone.Id);
116 | Assert.False(NHibernateUtil.IsInitialized(clone));
117 | Assert.Throws(() =>
118 | {
119 | var friend = clone.BestFriend;
120 | });
121 | }
122 | }
123 |
124 | [Test]
125 | public void TestEntityResolverSkipRoot()
126 | {
127 | EQBPerson clone;
128 | EQBPerson petra;
129 |
130 | using (var session = NHConfig.OpenSession())
131 | {
132 | petra = session.Query()
133 | .Include(o => o.BestFriend.IdentityCard)
134 | .Include(o => o.BestFriend.BestFriend)
135 | .First(o => o.Name == "Petra");
136 |
137 | clone = session.DeepClone(petra, o => o
138 | .AddEntityResolver((t, e) => e != petra, (entity, persister) => persister.CreateProxy(persister.GetIdentifier(entity), null)));
139 |
140 | Assert.AreEqual(petra.Id, clone.Id);
141 | Assert.AreEqual(petra.Name, clone.Name);
142 | Assert.True(NHibernateUtil.IsInitialized(clone));
143 |
144 | Assert.False(NHibernateUtil.IsInitialized(clone.BestFriend));
145 | Assert.Throws(() =>
146 | {
147 | var card = clone.BestFriend.IdentityCard;
148 | });
149 | }
150 | }
151 |
152 | [Test]
153 | public void TestCollections()
154 | {
155 | EQBPerson clone;
156 | EQBPerson petra;
157 |
158 | using (var session = NHConfig.OpenSession())
159 | {
160 | petra = session.Query()
161 | .Include(o => o.CurrentOwnedVehicles.First().Wheels)
162 | .Include(o => o.CurrentOwnedVehicles.First().RoadworthyTests)
163 | .Include(o => o.CurrentOwnedVehicles.First().MileageHistory)
164 | .Include(o => o.PreviouslyOwnedVehicles)
165 | .First(o => o.Name == "Petra");
166 |
167 | clone = session.DeepClone(petra);
168 | }
169 |
170 | Assert.IsNull(clone.BestFriend);
171 | Assert.IsNull(clone.IdentityCard);
172 | Assert.IsNull(clone.MarriedWith);
173 | Assert.AreEqual(1, clone.CurrentOwnedVehicles.Count);
174 | Assert.AreEqual(clone, clone.CurrentOwnedVehicles.First().CurrentOwner);
175 | Assert.AreEqual(4, clone.CurrentOwnedVehicles.First().Wheels.Count);
176 | Assert.AreEqual(clone.CurrentOwnedVehicles.First(), clone.CurrentOwnedVehicles.First().Wheels.First().Vehicle);
177 | Assert.AreEqual(2, clone.CurrentOwnedVehicles.First().RoadworthyTests.Count);
178 | Assert.AreEqual(clone.CurrentOwnedVehicles.First(), clone.CurrentOwnedVehicles.First().RoadworthyTests[new DateTime(2009, 2, 1)].Vehicle);
179 | Assert.AreEqual(2, clone.CurrentOwnedVehicles.First().MileageHistory.Count);
180 | Assert.AreEqual(5000, clone.CurrentOwnedVehicles.First().MileageHistory[new DateTime(2010, 1, 1)]);
181 |
182 | Assert.AreEqual(2, clone.PreviouslyOwnedVehicles.Count);
183 | Assert.AreEqual(clone, clone.CurrentOwnedVehicles.First().CurrentOwner);
184 | }
185 |
186 | [Test]
187 | public void TestFilter()
188 | {
189 | EQBPerson clone;
190 | EQBPerson petra;
191 |
192 | using (var session = NHConfig.OpenSession())
193 | {
194 | petra = session.Query()
195 | .Include(o => o.PreviouslyOwnedVehicles)
196 | .First(o => o.Name == "Petra");
197 |
198 | clone = session.DeepClone(petra, o => o
199 | .ForType(t => t
200 | .ForMember(m => m.Name, m => m.Filter(n => n + "2"))
201 | .ForMember(m => m.PreviouslyOwnedVehicles, m => m
202 | .Filter(col => new HashSet(col.Take(1)))
203 | )
204 | ));
205 | }
206 |
207 | Assert.AreEqual("Petra2", clone.Name);
208 | Assert.IsNull(clone.BestFriend);
209 | Assert.IsNull(clone.IdentityCard);
210 | Assert.IsNull(clone.MarriedWith);
211 | Assert.AreEqual(0, clone.CurrentOwnedVehicles.Count);
212 | Assert.AreEqual(1, clone.PreviouslyOwnedVehicles.Count);
213 | }
214 |
215 | [Test]
216 | public void TestSkipEntityTypes()
217 | {
218 | EQBPerson clone;
219 | EQBPerson petra;
220 |
221 | using (var session = NHConfig.OpenSession())
222 | {
223 | petra = session.Query()
224 | .Include(o => o.IdentityCard)
225 | .Include(o => o.BestFriend.IdentityCard)
226 | .Include(o => o.CurrentOwnedVehicles.First().Wheels)
227 | .Include(o => o.CurrentOwnedVehicles.First().RoadworthyTests)
228 | .Include(o => o.CurrentOwnedVehicles.First().MileageHistory)
229 | .First(o => o.Name == "Petra");
230 |
231 | clone = session.DeepClone(petra, o => o
232 | .SkipEntityTypes());
233 | }
234 |
235 | Assert.IsNull(clone.IdentityCard);
236 | Assert.IsNull(clone.BestFriend);
237 | Assert.AreEqual(1, clone.CurrentOwnedVehicles.Count);
238 | Assert.IsNull(clone.CurrentOwnedVehicles.First().CurrentOwner);
239 | Assert.AreEqual(4, clone.CurrentOwnedVehicles.First().Wheels.Count);
240 | Assert.IsNull(clone.CurrentOwnedVehicles.First().Wheels.First().Vehicle);
241 | Assert.AreEqual(2, clone.CurrentOwnedVehicles.First().RoadworthyTests.Count);
242 | Assert.IsNull(clone.CurrentOwnedVehicles.First().RoadworthyTests.First().Value.Vehicle);
243 | Assert.AreEqual(2, clone.CurrentOwnedVehicles.First().MileageHistory.Count);
244 | Assert.IsNotNull(clone.CurrentOwnedVehicles.First().MileageHistory[new DateTime(2010, 1, 1)]);
245 | }
246 |
247 | [Test]
248 | public void TestWithoutIdentifiers()
249 | {
250 | EQBPerson clone;
251 |
252 | using (var session = NHConfig.OpenSession())
253 | {
254 | var petra = session.Query()
255 | .Include(o => o.IdentityCard)
256 | .Include(o => o.BestFriend.IdentityCard)
257 | .Include(o => o.CurrentOwnedVehicles.First().Wheels)
258 | .Include(o => o.CurrentOwnedVehicles.First().RoadworthyTests)
259 | .Include(o => o.CurrentOwnedVehicles.First().MileageHistory)
260 | .First(o => o.Name == "Petra");
261 |
262 | clone = session.DeepClone(petra, o => o
263 | .CloneIdentifier(false));
264 | }
265 |
266 | Assert.AreEqual(0, clone.Id);
267 | Assert.AreEqual(0, clone.IdentityCard.Id);
268 | Assert.AreEqual(0, clone.IdentityCard.Owner.Id);
269 | Assert.AreEqual(0, clone.BestFriend.Id);
270 | Assert.AreEqual(0, clone.BestFriend.IdentityCard.Id);
271 | Assert.AreEqual(1, clone.CurrentOwnedVehicles.Count);
272 | Assert.AreEqual(0, clone.CurrentOwnedVehicles.First().Id);
273 | Assert.AreEqual(0, clone.CurrentOwnedVehicles.First().CurrentOwner.Id);
274 | Assert.AreEqual(4, clone.CurrentOwnedVehicles.First().Wheels.Count);
275 | Assert.AreEqual(0, clone.CurrentOwnedVehicles.First().Wheels.First().Id);
276 | Assert.AreEqual(0, clone.CurrentOwnedVehicles.First().Wheels.First().Vehicle.Id);
277 | Assert.AreEqual(2, clone.CurrentOwnedVehicles.First().RoadworthyTests.Count);
278 | Assert.AreEqual(0, clone.CurrentOwnedVehicles.First().RoadworthyTests.First().Value.Id);
279 | Assert.AreEqual(0, clone.CurrentOwnedVehicles.First().RoadworthyTests.First().Value.Vehicle.Id);
280 | }
281 | }
282 | }
283 |
--------------------------------------------------------------------------------
/NHibernate.Extensions.Tests/Entities/Animal.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace NHibernate.Extensions.Tests.Entities
8 | {
9 | public class Animal : Entity
10 | {
11 | public virtual string Name { get; set; }
12 |
13 | public virtual AnimalType Type { get; set; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/NHibernate.Extensions.Tests/Entities/AnimalType.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace NHibernate.Extensions.Tests.Entities
8 | {
9 | public class AnimalType : Entity
10 | {
11 | public virtual string Name { get; set; }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/NHibernate.Extensions.Tests/Entities/BatchModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace NHibernate.Extensions.Tests.Entities
8 | {
9 | public class BatchModel : Entity
10 | {
11 | public virtual string Name { get; set; }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/NHibernate.Extensions.Tests/Entities/Cat.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace NHibernate.Extensions.Tests.Entities
8 | {
9 | public class Cat : Animal
10 | {
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/NHibernate.Extensions.Tests/Entities/EQBDrivingLicence.cs:
--------------------------------------------------------------------------------
1 | namespace NHibernate.Extensions.Tests.Entities
2 | {
3 | public partial class EQBDrivingLicence : Entity
4 | {
5 | public virtual string Code { get; set; }
6 |
7 | public virtual EQBPerson Owner { get; set; }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/NHibernate.Extensions.Tests/Entities/EQBHouse.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using FluentNHibernate.Automapping;
3 | using FluentNHibernate.Automapping.Alterations;
4 |
5 | namespace NHibernate.Extensions.Tests.Entities
6 | {
7 | public partial class EQBHouse : Entity
8 | {
9 | public EQBHouse()
10 | {
11 | Owners = new HashSet();
12 | }
13 |
14 | public virtual string Address { get; set; }
15 |
16 | public virtual ISet Owners { get; set; }
17 | }
18 |
19 | public class EQBHouseMapping : IAutoMappingOverride
20 | {
21 | public void Override(AutoMapping mapping)
22 | {
23 | mapping.HasManyToMany(o => o.Owners).Inverse();
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/NHibernate.Extensions.Tests/Entities/EQBIdentity.cs:
--------------------------------------------------------------------------------
1 | namespace NHibernate.Extensions.Tests.Entities
2 | {
3 | public partial class EQBIdentityCard : Entity
4 | {
5 | public virtual string Code { get; set; }
6 |
7 | public virtual EQBPerson Owner { get; set; }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/NHibernate.Extensions.Tests/Entities/EQBIdentityCard.cs:
--------------------------------------------------------------------------------
1 | namespace NHibernate.Extensions.Tests.Entities
2 | {
3 | public partial class EQBIdentity : Entity
4 | {
5 | public virtual string Code { get; set; }
6 |
7 | public virtual EQBPerson Owner { get; set; }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/NHibernate.Extensions.Tests/Entities/EQBPerson.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using FluentNHibernate.Automapping;
3 | using FluentNHibernate.Automapping.Alterations;
4 |
5 | namespace NHibernate.Extensions.Tests.Entities
6 | {
7 | public interface IPerson
8 | {
9 | string Name { get; set; }
10 |
11 | IUser CreatedBy { get; set; }
12 |
13 | ISet CurrentOwnedVehicles { get; set; }
14 | }
15 |
16 | public partial class EQBPerson : Entity, IPerson
17 | {
18 | public EQBPerson(string name) : this()
19 | {
20 | Name = name;
21 | }
22 |
23 | protected EQBPerson()
24 | {
25 | CurrentOwnedVehicles = new HashSet();
26 | OwnedHouses = new HashSet();
27 | PreviouslyOwnedVehicles = new HashSet();
28 |
29 | }
30 |
31 | public virtual string Name { get; set; }
32 |
33 | public virtual string LastName { get; set; }
34 |
35 | public virtual int Age { get; set; }
36 |
37 | #region OneToMany
38 |
39 | public virtual ISet CurrentOwnedVehicles { get; set; }
40 |
41 | public virtual ISet CurrentOwnedVehiclesOld { get; set; }
42 |
43 | #endregion
44 |
45 | #region ManyToMany
46 |
47 | public virtual ISet OwnedHouses { get; set; }
48 |
49 | public virtual ISet PreviouslyOwnedVehicles { get; set; }
50 |
51 | #endregion
52 |
53 | #region ManyToOne
54 |
55 | public virtual EQBPerson BestFriend { get; set; }
56 |
57 | public virtual IUser CreatedBy { get; set; }
58 |
59 | #endregion
60 |
61 | #region OneToOne
62 |
63 | public virtual EQBPerson MarriedWith { get; set; }
64 |
65 | public virtual EQBDrivingLicence DrivingLicence { get; set; }
66 |
67 | public virtual EQBIdentityCard IdentityCard { get; set; }
68 |
69 | public virtual EQBIdentity Identity { get; set; }
70 |
71 | #endregion
72 |
73 | }
74 |
75 | public class EQBPersonMapping : IAutoMappingOverride
76 | {
77 | public void Override(AutoMapping mapping)
78 | {
79 | mapping.HasMany(o => o.CurrentOwnedVehicles).KeyColumn("CurrentOwnerId");
80 | mapping.HasMany(o => o.CurrentOwnedVehiclesOld).KeyColumn("OldOwnerId");
81 | mapping.HasManyToMany(o => o.PreviouslyOwnedVehicles);
82 | mapping.HasManyToMany(o => o.OwnedHouses);
83 | mapping.References(o => o.CreatedBy).Class();
84 | }
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/NHibernate.Extensions.Tests/Entities/EQBRoadWorthyTest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace NHibernate.Extensions.Tests.Entities
4 | {
5 | public partial class EQBRoadworthyTest : Entity
6 | {
7 | public virtual DateTime TestDate { get; set; }
8 |
9 | public virtual bool Passed { get; set; }
10 |
11 | public virtual string Comments { get; set; }
12 |
13 | public virtual EQBVehicle Vehicle { get; set; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/NHibernate.Extensions.Tests/Entities/EQBUser.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace NHibernate.Extensions.Tests.Entities
7 | {
8 | public class EQBUser : Entity, IUser
9 | {
10 | public virtual string UserName { get; set; }
11 |
12 | public virtual EQBUser Related { get; set; }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/NHibernate.Extensions.Tests/Entities/EQBVehicle.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.CodeDom;
3 | using System.Collections.Generic;
4 | using FluentNHibernate.Automapping;
5 | using FluentNHibernate.Automapping.Alterations;
6 |
7 | namespace NHibernate.Extensions.Tests.Entities
8 | {
9 | public partial class EQBVehicle : Entity
10 | {
11 | public EQBVehicle()
12 | {
13 | Wheels = new HashSet();
14 | PreviousUsers = new HashSet();
15 | RoadworthyTests = new Dictionary();
16 | MileageHistory = new SortedDictionary();
17 | }
18 |
19 | public virtual string Model { get; set; }
20 |
21 | public virtual int BuildYear { get; set; }
22 |
23 | public virtual EQBPerson CurrentOwner { get; set; }
24 |
25 | public virtual EQBPerson OldCurrentOwner { get; set; }
26 |
27 | public virtual ISet PreviousUsers { get; set; }
28 |
29 | public virtual ISet Wheels { get; set; }
30 |
31 | public virtual IDictionary RoadworthyTests { get; set; }
32 |
33 | public virtual IDictionary MileageHistory { get; set; }
34 | }
35 |
36 | public class EQBVehicleMapping : IAutoMappingOverride
37 | {
38 | public void Override(AutoMapping mapping)
39 | {
40 | mapping.HasManyToMany(o => o.PreviousUsers).Inverse();
41 | mapping.HasMany(o => o.Wheels).KeyColumn("VehicleId");
42 | mapping.HasMany(o => o.RoadworthyTests).AsMap(rwt => rwt.TestDate);
43 | mapping.HasMany(o => o.MileageHistory).AsMap("ObservationDate").Element("Mileage");
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/NHibernate.Extensions.Tests/Entities/IUser.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace NHibernate.Extensions.Tests.Entities
7 | {
8 | public interface IUser
9 | {
10 | string UserName { get; set; }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/NHibernate.Extensions.Tests/Entities/TestEQBWheel.cs:
--------------------------------------------------------------------------------
1 | namespace NHibernate.Extensions.Tests.Entities
2 | {
3 | public partial class TestEQBWheel : Entity
4 | {
5 | public virtual int Diameter { get; set; }
6 |
7 | public virtual int Width { get; set; }
8 |
9 | public virtual EQBVehicle Vehicle { get; set; }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/NHibernate.Extensions.Tests/IEntity.cs:
--------------------------------------------------------------------------------
1 | namespace NHibernate.Extensions.Tests
2 | {
3 | public interface IEntity
4 | {
5 | }
6 |
7 | public abstract partial class Entity : IEntity
8 | {
9 | public virtual int Id { get; set; }
10 |
11 | public virtual bool IsTransient()
12 | {
13 | return Id.Equals(default(int));
14 | }
15 |
16 | public virtual System.Type GetTypeUnproxied()
17 | {
18 | return GetType();
19 | }
20 |
21 | public override bool Equals(object obj)
22 | {
23 | var compareTo = obj as Entity;
24 | if (ReferenceEquals(this, compareTo))
25 | return true;
26 | if (compareTo == null || GetType() != compareTo.GetTypeUnproxied())
27 | return false;
28 | return HasSameNonDefaultIdAs(compareTo);
29 | }
30 |
31 | public override int GetHashCode()
32 | {
33 | if(IsTransient())
34 | // ReSharper disable once BaseObjectGetHashCodeCallInGetHashCode
35 | return base.GetHashCode();
36 | // ReSharper disable once NonReadonlyMemberInGetHashCode
37 | return (GetType().GetHashCode() * 31) ^ Id.GetHashCode();
38 | }
39 |
40 | private bool HasSameNonDefaultIdAs(Entity compareTo)
41 | {
42 | return !IsTransient() && !compareTo.IsTransient() && Id.Equals(compareTo.Id);
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/NHibernate.Extensions.Tests/LogSpy.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using log4net;
7 | using log4net.Appender;
8 | using log4net.Core;
9 | using log4net.Repository.Hierarchy;
10 |
11 | namespace NHibernate.Extensions.Tests
12 | {
13 | public class LogSpy : IDisposable
14 | {
15 | private readonly MemoryAppender appender;
16 | private readonly Logger logger;
17 | private readonly Level prevLogLevel;
18 |
19 | public LogSpy(ILog log, Level level)
20 | {
21 | logger = log.Logger as Logger;
22 | if (logger == null)
23 | {
24 | throw new InvalidOperationException("Unable to get the logger");
25 | }
26 |
27 | // Change the log level to DEBUG and temporarily save the previous log level
28 | prevLogLevel = logger.Level;
29 | logger.Level = level;
30 |
31 | // Add a new MemoryAppender to the logger.
32 | appender = new MemoryAppender();
33 | logger.AddAppender(appender);
34 | }
35 |
36 | public LogSpy(ILog log, bool disable)
37 | : this(log, disable ? Level.Off : Level.Debug)
38 | {
39 | }
40 |
41 | public LogSpy(ILog log) : this(log, false) { }
42 | public LogSpy(System.Type loggerType) : this(LogManager.GetLogger(loggerType), false) { }
43 | public LogSpy(System.Type loggerType, bool disable) : this(LogManager.GetLogger(loggerType), disable) { }
44 |
45 | public LogSpy(string loggerName) : this(LogManager.GetLogger(typeof(LogSpy).Assembly, loggerName), false) { }
46 | public LogSpy(string loggerName, bool disable) : this(LogManager.GetLogger(typeof(LogSpy).Assembly, loggerName), disable) { }
47 |
48 | public MemoryAppender Appender
49 | {
50 | get { return appender; }
51 | }
52 |
53 | public virtual string GetWholeLog()
54 | {
55 | var wholeMessage = new StringBuilder();
56 | foreach (LoggingEvent loggingEvent in Appender.GetEvents())
57 | {
58 | wholeMessage
59 | .Append(loggingEvent.LoggerName)
60 | .Append(" ")
61 | .Append(loggingEvent.RenderedMessage)
62 | .AppendLine();
63 | }
64 | return wholeMessage.ToString();
65 | }
66 |
67 | #region IDisposable Members
68 |
69 | public void Dispose()
70 | {
71 | // Restore the previous log level of the SQL logger and remove the MemoryAppender
72 | logger.Level = prevLogLevel;
73 | logger.RemoveAppender(appender);
74 | }
75 |
76 | #endregion
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/NHibernate.Extensions.Tests/NHConfig.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using FluentNHibernate.Automapping;
4 | using FluentNHibernate.Cfg;
5 | using FluentNHibernate.Conventions.Helpers;
6 | using log4net;
7 | using log4net.Config;
8 | using NHibernate.Cfg;
9 | using NHibernate.Tool.hbm2ddl;
10 | using Environment = NHibernate.Cfg.Environment;
11 |
12 |
13 | namespace NHibernate.Extensions.Tests
14 | {
15 | public class AutomappingConfiguration : DefaultAutomappingConfiguration
16 | {
17 | public override bool ShouldMap(System.Type type)
18 | {
19 | return base.ShouldMap(type) && typeof(Entity).IsAssignableFrom(type);
20 | }
21 | }
22 |
23 | public static class NHConfig
24 | {
25 | public static readonly ISessionFactory SessionFactory;
26 |
27 | public static readonly Configuration Configuration;
28 |
29 | static NHConfig()
30 | {
31 | XmlConfigurator.Configure(LogManager.GetRepository(typeof(NHConfig).Assembly));
32 | #if DEBUG
33 | HibernatingRhinos.Profiler.Appender.NHibernate.NHibernateProfiler.Initialize();
34 | #endif
35 |
36 | var modelAssembly = typeof(NHConfig).Assembly;
37 | var configuration = Configuration = new Configuration();
38 | configuration.SetProperty(Environment.GenerateStatistics, "true");
39 | configuration.SetProperty(Environment.UseSqlComments, "false");
40 | configuration.SetProperty(Environment.ShowSql, "false");
41 | configuration.Configure(); // Configure from the hibernate.cfg.config
42 |
43 | // We have to replace |DataDirectory| as it is not supported on .NET Core
44 | var connString = configuration.Properties[Environment.ConnectionString];
45 | configuration.Properties[Environment.ConnectionString] =
46 | connString.Replace("|DataDirectory|",
47 | Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"..\..")));
48 |
49 | var fluentConfig = Fluently.Configure(configuration);
50 | var autoPestModel = AutoMap
51 | .Assemblies(new AutomappingConfiguration(), new[] { modelAssembly })
52 | .UseOverridesFromAssembly(modelAssembly)
53 | .IgnoreBase()
54 | .Conventions.Add()
55 | .Conventions.Add(PrimaryKey.Name.Is(o => "Id"))
56 | .Conventions.Add(ForeignKey.EndsWith("Id"));
57 | fluentConfig
58 | .Diagnostics(o => o.Enable(true))
59 | .Mappings(m =>
60 | {
61 | m.HbmMappings.AddFromAssembly(modelAssembly);
62 | m.AutoMappings.Add(autoPestModel);
63 | #if DEBUG
64 | var mappingsDirecotry = Path.Combine(Directory.GetCurrentDirectory(), "Mappings");
65 | if (!Directory.Exists(mappingsDirecotry))
66 | Directory.CreateDirectory(mappingsDirecotry);
67 | m.AutoMappings.ExportTo(mappingsDirecotry);
68 | m.FluentMappings.ExportTo(mappingsDirecotry);
69 | #endif
70 | });
71 |
72 | SessionFactory = fluentConfig.BuildSessionFactory();
73 | }
74 |
75 | public static ISession OpenSession()
76 | {
77 | return SessionFactory.OpenSession();
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/NHibernate.Extensions.Tests/NHibernate.Extensions.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net461;netcoreapp3.1
5 | 7.2
6 | false
7 | maca88
8 |
9 | Copyright © 2017
10 | https://github.com/maca88/NHibernate.Extensions/blob/master/LICENSE
11 | https://github.com/maca88/NHibernate.Extensions
12 | https://github.com/maca88/NHibernate.Extensions
13 | true
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | PreserveNewest
33 |
34 |
35 |
36 | Test.mdf
37 | None
38 |
39 |
40 | None
41 |
42 |
43 |
44 | PreserveNewest
45 | ..\..\Test.ldf
46 |
47 |
48 |
49 | PreserveNewest
50 | ..\..\Test.mdf
51 |
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/NHibernate.Extensions.Tests/QueryRelationTreeTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Linq.Expressions;
4 | using NHibernate.Extensions.Tests.Entities;
5 | using NUnit.Framework;
6 |
7 | namespace NHibernate.Extensions.Tests
8 | {
9 | [TestFixture]
10 | public class QueryRelationTreeTests
11 | {
12 | [Test]
13 | public void Test1()
14 | {
15 | var tree = new QueryRelationTree();
16 | Expression> A = person => person.BestFriend;
17 | Expression> AB = person => person.BestFriend.IdentityCard;
18 | Expression> AA = person => person.BestFriend.BestFriend;
19 | Expression> AAA = person => person.BestFriend.BestFriend.BestFriend;
20 | Expression> AAAA = person => person.BestFriend.BestFriend.BestFriend.BestFriend;
21 |
22 | //Input
23 | tree.AddNode(A);
24 | tree.AddNode(AB);
25 | tree.AddNode(AA);
26 | tree.AddNode(AAA);
27 | tree.AddNode(AAAA);
28 |
29 | var results = tree.DeepFirstSearch();
30 | //Output
31 | Assert.AreEqual("BestFriend", results[0][0]);
32 | Assert.AreEqual("BestFriend.IdentityCard", results[0][1]);
33 |
34 | Assert.AreEqual("BestFriend", results[1][0]);
35 | Assert.AreEqual("BestFriend.BestFriend", results[1][1]);
36 | Assert.AreEqual("BestFriend.BestFriend.BestFriend", results[1][2]);
37 | Assert.AreEqual("BestFriend.BestFriend.BestFriend.BestFriend", results[1][3]);
38 | }
39 |
40 | [Test]
41 | public void Test2()
42 | {
43 | var tree = new QueryRelationTree();
44 | Expression> AB = person => person.BestFriend.IdentityCard;
45 | Expression> AAAA = person => person.BestFriend.BestFriend.BestFriend.BestFriend;
46 | Expression> CD = person => person.CurrentOwnedVehicles.First().Wheels;
47 | Expression> CE = person => person.CurrentOwnedVehicles.First().RoadworthyTests;
48 |
49 | //Input
50 | tree.AddNode(AB);
51 | tree.AddNode(AAAA);
52 | tree.AddNode(CD);
53 | tree.AddNode(CE);
54 |
55 | var results = tree.DeepFirstSearch();
56 | //Output
57 | Assert.AreEqual("BestFriend", results[0][0]);
58 | Assert.AreEqual("BestFriend.IdentityCard", results[0][1]);
59 |
60 | Assert.AreEqual("BestFriend", results[1][0]);
61 | Assert.AreEqual("BestFriend.BestFriend", results[1][1]);
62 | Assert.AreEqual("BestFriend.BestFriend.BestFriend", results[1][2]);
63 | Assert.AreEqual("BestFriend.BestFriend.BestFriend.BestFriend", results[1][3]);
64 |
65 | Assert.AreEqual("CurrentOwnedVehicles", results[2][0]);
66 | Assert.AreEqual("CurrentOwnedVehicles.Wheels", results[2][1]);
67 |
68 | Assert.AreEqual("CurrentOwnedVehicles", results[3][0]);
69 | Assert.AreEqual("CurrentOwnedVehicles.RoadworthyTests", results[3][1]);
70 | }
71 |
72 | [Test]
73 | public void Test3()
74 | {
75 | var tree = new QueryRelationTree();
76 | Expression> AB = person => person.Identity;
77 | Expression> CD = person => person.IdentityCard;
78 |
79 | //Input
80 | tree.AddNode(AB);
81 | tree.AddNode(CD);
82 |
83 | var results = tree.DeepFirstSearch();
84 | //Output
85 | Assert.AreEqual("Identity", results[0][0]);
86 | Assert.AreEqual("IdentityCard", results[1][0]);
87 | }
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/NHibernate.Extensions.Tests/SessionSubscriptionTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 | using NHibernate.Tool.hbm2ddl;
4 | using NUnit.Framework;
5 |
6 | namespace NHibernate.Extensions.Tests
7 | {
8 | [TestFixture]
9 | public class SessionSubscriptionTests
10 | {
11 | [Test]
12 | public void TestTransactionSubscription()
13 | {
14 | using (var session = NHConfig.OpenSession())
15 | using (var transaction = session.BeginTransaction())
16 | {
17 | var beforeCommitExecuted = false;
18 | var afterCommitExecuted = false;
19 |
20 | session.Subscribe(o => o.Transaction
21 | .BeforeCommit(s =>
22 | {
23 | Assert.AreEqual(session, s);
24 | Assert.IsTrue(transaction.IsActive);
25 | beforeCommitExecuted = true;
26 | })
27 | .AfterCommit((s, success) =>
28 | {
29 | Assert.IsTrue(success);
30 | Assert.AreEqual(session, s);
31 | Assert.IsFalse(transaction.IsActive);
32 | afterCommitExecuted = true;
33 | }));
34 |
35 | Assert.IsFalse(beforeCommitExecuted);
36 | Assert.IsFalse(afterCommitExecuted);
37 | transaction.Commit();
38 | Assert.IsTrue(beforeCommitExecuted);
39 | Assert.IsTrue(afterCommitExecuted);
40 | }
41 | }
42 |
43 | [Test]
44 | public async Task TestTransactionSubscriptionAsync()
45 | {
46 | using (var session = NHConfig.OpenSession())
47 | using (var transaction = session.BeginTransaction())
48 | {
49 | var beforeCommitExecuted = false;
50 | var afterCommitExecuted = false;
51 |
52 | session.Subscribe(o => o.Transaction
53 | .BeforeCommit(async s =>
54 | {
55 | await Task.Delay(0);
56 | Assert.AreEqual(session, s);
57 | Assert.IsTrue(transaction.IsActive);
58 | beforeCommitExecuted = true;
59 | })
60 | .AfterCommit(async (s, success) =>
61 | {
62 | await Task.Delay(0);
63 | Assert.IsTrue(success);
64 | Assert.AreEqual(session, s);
65 | Assert.IsFalse(transaction.IsActive);
66 | afterCommitExecuted = true;
67 | }));
68 |
69 | Assert.IsFalse(beforeCommitExecuted);
70 | Assert.IsFalse(afterCommitExecuted);
71 | await transaction.CommitAsync();
72 | Assert.IsTrue(beforeCommitExecuted);
73 | Assert.IsTrue(afterCommitExecuted);
74 | }
75 | }
76 |
77 | [Test]
78 | public void TestTransactionSubscriptionRollback()
79 | {
80 | using (var session = NHConfig.OpenSession())
81 | using (var transaction = session.BeginTransaction())
82 | {
83 | var beforeCommitExecuted = false;
84 | var afterCommitExecuted = false;
85 |
86 | // BeforeCommit wont be executed on rollback
87 | session.Subscribe(o => o.Transaction
88 | .BeforeCommit(s =>
89 | {
90 | Assert.AreEqual(session, s);
91 | Assert.IsTrue(transaction.IsActive);
92 | beforeCommitExecuted = true;
93 | })
94 | .AfterCommit((s, success) =>
95 | {
96 | Assert.IsFalse(success);
97 | Assert.AreEqual(session, s);
98 | Assert.IsFalse(transaction.IsActive);
99 | afterCommitExecuted = true;
100 | }));
101 |
102 | Assert.IsFalse(beforeCommitExecuted);
103 | Assert.IsFalse(afterCommitExecuted);
104 | transaction.Rollback();
105 | Assert.IsFalse(beforeCommitExecuted);
106 | Assert.IsTrue(afterCommitExecuted);
107 | }
108 | }
109 |
110 | [Test]
111 | public async Task TestTransactionSubscriptionRollbackAsync()
112 | {
113 | using (var session = NHConfig.OpenSession())
114 | using (var transaction = session.BeginTransaction())
115 | {
116 | var beforeCommitExecuted = false;
117 | var afterCommitExecuted = false;
118 |
119 | // BeforeCommit wont be executed on rollback
120 | session.Subscribe(o => o.Transaction
121 | .BeforeCommit(async s =>
122 | {
123 | await Task.Delay(0);
124 | Assert.AreEqual(session, s);
125 | Assert.IsTrue(transaction.IsActive);
126 | beforeCommitExecuted = true;
127 | })
128 | .AfterCommit(async (s, success) =>
129 | {
130 | await Task.Delay(0);
131 | Assert.IsFalse(success);
132 | Assert.AreEqual(session, s);
133 | Assert.IsFalse(transaction.IsActive);
134 | afterCommitExecuted = true;
135 | }));
136 |
137 | Assert.IsFalse(beforeCommitExecuted);
138 | Assert.IsFalse(afterCommitExecuted);
139 | await transaction.RollbackAsync();
140 | Assert.IsFalse(beforeCommitExecuted);
141 | Assert.IsTrue(afterCommitExecuted);
142 | }
143 | }
144 |
145 | [Test]
146 | public void TestTransactionSubscriptionWithoutTransaction()
147 | {
148 | using (var session = NHConfig.OpenSession())
149 | {
150 | Assert.Throws(() =>
151 | {
152 | session.Subscribe(o => o.Transaction
153 | .BeforeCommit(s => { })
154 | .AfterCommit((s, success) => { }));
155 | });
156 | }
157 | }
158 |
159 | [Test]
160 | public void TestAsyncTransactionSubscriptionInSyncCommit()
161 | {
162 | using (var session = NHConfig.OpenSession())
163 | using (var transaction = session.BeginTransaction())
164 | {
165 | session.Subscribe(o => o.Transaction
166 | .BeforeCommit(s => Task.CompletedTask)
167 | .AfterCommit((s, success) => Task.CompletedTask));
168 |
169 | Assert.Throws(() =>
170 | {
171 | transaction.Commit();
172 | });
173 | }
174 | }
175 |
176 | [OneTimeSetUp]
177 | public void Initialize()
178 | {
179 | var schema = new SchemaExport(NHConfig.Configuration);
180 | schema.Drop(false, true);
181 | schema.Create(false, true);
182 | }
183 |
184 | [OneTimeTearDown]
185 | public void Cleanup()
186 | {
187 | var schema = new SchemaExport(NHConfig.Configuration);
188 | schema.Drop(false, true);
189 | }
190 | }
191 | }
192 |
--------------------------------------------------------------------------------
/NHibernate.Extensions.Tests/Test.ldf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maca88/NHibernate.Extensions/66c84026616f9f88996f16d08ba07d297c067e0b/NHibernate.Extensions.Tests/Test.ldf
--------------------------------------------------------------------------------
/NHibernate.Extensions.Tests/Test.mdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maca88/NHibernate.Extensions/66c84026616f9f88996f16d08ba07d297c067e0b/NHibernate.Extensions.Tests/Test.mdf
--------------------------------------------------------------------------------
/NHibernate.Extensions.Tests/hibernate.cfg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | NHibernate.Connection.DriverConnectionProvider
5 | NHibernate.Driver.SqlClientDriver
6 | Data Source=(LocalDb)\MSSQLLocalDB;Initial Catalog=NHibernate;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|\Test.mdf
7 | NHibernate.Dialect.MsSql2008Dialect
8 |
9 |
10 |
--------------------------------------------------------------------------------
/NHibernate.Extensions.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.26403.7
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NHibernate.Extensions.Tests", "NHibernate.Extensions.Tests\NHibernate.Extensions.Tests.csproj", "{DC0789BE-E6C7-4F62-9B97-4D6F1343FE2F}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NHibernate.Extensions", "NHibernate.Extensions\NHibernate.Extensions.csproj", "{ECE5FA2C-A6B7-4577-AD3C-DD13098FCE37}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|Any CPU = Debug|Any CPU
13 | Release|Any CPU = Release|Any CPU
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {DC0789BE-E6C7-4F62-9B97-4D6F1343FE2F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
17 | {DC0789BE-E6C7-4F62-9B97-4D6F1343FE2F}.Debug|Any CPU.Build.0 = Debug|Any CPU
18 | {DC0789BE-E6C7-4F62-9B97-4D6F1343FE2F}.Release|Any CPU.ActiveCfg = Release|Any CPU
19 | {DC0789BE-E6C7-4F62-9B97-4D6F1343FE2F}.Release|Any CPU.Build.0 = Release|Any CPU
20 | {ECE5FA2C-A6B7-4577-AD3C-DD13098FCE37}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {ECE5FA2C-A6B7-4577-AD3C-DD13098FCE37}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {ECE5FA2C-A6B7-4577-AD3C-DD13098FCE37}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {ECE5FA2C-A6B7-4577-AD3C-DD13098FCE37}.Release|Any CPU.Build.0 = Release|Any CPU
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | EndGlobal
29 |
--------------------------------------------------------------------------------
/NHibernate.Extensions/Async/BatchFetchBuilder.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by AsyncGenerator.
4 | //
5 | // Changes to this file may cause incorrect behavior and will be lost if
6 | // the code is regenerated.
7 | //
8 | //------------------------------------------------------------------------------
9 |
10 |
11 | using NHibernate.Linq;
12 | using System;
13 | using System.Collections.Generic;
14 | using System.Linq;
15 | using System.Linq.Expressions;
16 | using System.Threading;
17 | using System.Threading.Tasks;
18 |
19 | namespace NHibernate.Extensions
20 | {
21 |
22 | public partial class BatchFetchBuilder : IBatchFetchBuilder
23 | {
24 |
25 | Task> IBatchFetchBuilder.ExecuteAsync(CancellationToken cancellationToken)
26 | {
27 | if (cancellationToken.IsCancellationRequested)
28 | {
29 | return Task.FromCanceled>(cancellationToken);
30 | }
31 | return ExecuteAsync(q => q, cancellationToken);
32 | }
33 |
34 | protected async Task> ExecuteAsync(Func, IQueryable> convertQuery, CancellationToken cancellationToken = default(CancellationToken))
35 | {
36 | cancellationToken.ThrowIfCancellationRequested();
37 | var parameter = KeyExpresion.Parameters[0];
38 | var method = BatchFetchExtension.ContainsMethodInfo.MakeGenericMethod(typeof(TKey));
39 | var result = new List();
40 | var currIndex = 0;
41 | var itemsCount = Keys.Count;
42 | while (currIndex < itemsCount)
43 | {
44 | var batchNum = Math.Min(BatchSize, itemsCount - currIndex);
45 | var batchItems = Keys.Skip(currIndex).Take(batchNum).ToList();
46 | var value = Expression.Constant(batchItems, typeof(IEnumerable));
47 | var containsMethod = Expression.Call(method, value, KeyExpresion.Body);
48 | var predicate = Expression.Lambda>(containsMethod, parameter);
49 | var query = Session.Query()
50 | .Where(predicate);
51 |
52 | if (BeforeQueryExecutionFunction != null)
53 | {
54 | query = BeforeQueryExecutionFunction(query);
55 | }
56 |
57 | result.AddRange(await (ToListAsync(convertQuery(query), cancellationToken)).ConfigureAwait(false));
58 | currIndex += batchNum;
59 | }
60 |
61 | return result;
62 | }
63 | }
64 |
65 |
66 | public partial class BatchFetchBuilder : BatchFetchBuilder, IBatchFetchBuilder
67 | {
68 |
69 | Task> IBatchFetchBuilder.ExecuteAsync(CancellationToken cancellationToken)
70 | {
71 | if (cancellationToken.IsCancellationRequested)
72 | {
73 | return Task.FromCanceled>(cancellationToken);
74 | }
75 | return ExecuteAsync(q => q.Select(SelectExpression), cancellationToken);
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/NHibernate.Extensions/Async/BatchFetchExtension.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by AsyncGenerator.
4 | //
5 | // Changes to this file may cause incorrect behavior and will be lost if
6 | // the code is regenerated.
7 | //
8 | //------------------------------------------------------------------------------
9 |
10 |
11 | using System;
12 | using System.CodeDom;
13 | using System.Collections.Generic;
14 | using System.Linq;
15 | using System.Linq.Expressions;
16 | using System.Reflection;
17 |
18 | namespace NHibernate.Extensions
19 | {
20 | using System.Threading.Tasks;
21 | using System.Threading;
22 | public static partial class BatchFetchExtension
23 | {
24 |
25 | ///
26 | /// Batch fetching a collecion of keys by using ISession Linq provider
27 | ///
28 | ///
29 | ///
30 | /// NHibernate session
31 | /// Collection of keys that will be retrieved from the database
32 | /// Expression pointing to the property that represents the key
33 | /// Number of records that will be retrieved within one execution
34 | /// Function to modify the query prior execution
35 | /// A cancellation token that can be used to cancel the work
36 | /// The fetched entites.
37 | public static Task> BatchFetchAsync(this ISession session, ICollection keys,
38 | Expression> propertyExpr,
39 | int batchSize,
40 | Func, IQueryable> queryFunc = null, CancellationToken cancellationToken = default(CancellationToken))
41 | {
42 | if (propertyExpr == null)
43 | {
44 | throw new ArgumentNullException(nameof(propertyExpr));
45 | }
46 |
47 | if (keys == null)
48 | {
49 | throw new ArgumentNullException(nameof(keys));
50 | }
51 | if (cancellationToken.IsCancellationRequested)
52 | {
53 | return Task.FromCanceled>(cancellationToken);
54 | }
55 | try
56 | {
57 |
58 | return session.BatchFetch(batchSize)
59 | .SetKeys(keys, propertyExpr)
60 | .BeforeQueryExecution(queryFunc)
61 | .ExecuteAsync(cancellationToken);
62 | }
63 | catch (Exception ex)
64 | {
65 | return Task.FromException>(ex);
66 | }
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/NHibernate.Extensions/Async/IBatchFetchBuilder.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by AsyncGenerator.
4 | //
5 | // Changes to this file may cause incorrect behavior and will be lost if
6 | // the code is regenerated.
7 | //
8 | //------------------------------------------------------------------------------
9 |
10 |
11 | using System;
12 | using System.Collections.Generic;
13 | using System.Linq;
14 | using System.Linq.Expressions;
15 |
16 | namespace NHibernate.Extensions
17 | {
18 | using System.Threading.Tasks;
19 | using System.Threading;
20 |
21 | public partial interface IBatchFetchBuilder
22 | {
23 |
24 | Task> ExecuteAsync(CancellationToken cancellationToken = default(CancellationToken));
25 | }
26 |
27 | public partial interface IBatchFetchBuilder
28 | {
29 |
30 | Task> ExecuteAsync(CancellationToken cancellationToken = default(CancellationToken));
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/NHibernate.Extensions/BatchFetchBuilder.cs:
--------------------------------------------------------------------------------
1 | using NHibernate.Linq;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Linq.Expressions;
6 | using System.Threading;
7 | using System.Threading.Tasks;
8 |
9 | namespace NHibernate.Extensions
10 | {
11 | public class BatchFetchBuilder : IBatchFetchBuilder
12 | {
13 | private readonly ISession _session;
14 | private readonly int _batchSize;
15 |
16 | public BatchFetchBuilder(ISession session, int batchSize)
17 | {
18 | _session = session;
19 | _batchSize = batchSize;
20 | }
21 |
22 | IBatchFetchBuilder IBatchFetchBuilder.SetKeys(ICollection keys, Expression> keyExpresion)
23 | {
24 | return new BatchFetchBuilder(_session, keys, keyExpresion, _batchSize);
25 | }
26 | }
27 |
28 | public partial class BatchFetchBuilder : IBatchFetchBuilder
29 | {
30 | protected readonly ISession Session;
31 | protected readonly ICollection Keys;
32 | protected readonly Expression> KeyExpresion;
33 | protected readonly int BatchSize;
34 |
35 | public BatchFetchBuilder(ISession session, ICollection keys, Expression> keyExpresion,
36 | int batchSize)
37 | {
38 | Session = session;
39 | Keys = keys;
40 | KeyExpresion = keyExpresion;
41 | BatchSize = batchSize;
42 | }
43 |
44 | public Func, IQueryable> BeforeQueryExecutionFunction { get; protected set; }
45 |
46 | IBatchFetchBuilder IBatchFetchBuilder.BeforeQueryExecution(Func, IQueryable> queryFunc)
47 | {
48 | return new BatchFetchBuilder(Session, Keys.ToList(), KeyExpresion, BatchSize)
49 | {
50 | BeforeQueryExecutionFunction = queryFunc
51 | };
52 | }
53 |
54 | IBatchFetchBuilder IBatchFetchBuilder.Select(Expression> selectExpr)
55 | {
56 | return new BatchFetchBuilder(Session, Keys.ToList(), KeyExpresion, BatchSize, selectExpr)
57 | {
58 | BeforeQueryExecutionFunction = BeforeQueryExecutionFunction
59 | };
60 | }
61 |
62 | List IBatchFetchBuilder.Execute()
63 | {
64 | return Execute(q => q);
65 | }
66 |
67 | protected List Execute(Func, IQueryable> convertQuery)
68 | {
69 | var parameter = KeyExpresion.Parameters[0];
70 | var method = BatchFetchExtension.ContainsMethodInfo.MakeGenericMethod(typeof(TKey));
71 | var result = new List();
72 | var currIndex = 0;
73 | var itemsCount = Keys.Count;
74 | while (currIndex < itemsCount)
75 | {
76 | var batchNum = Math.Min(BatchSize, itemsCount - currIndex);
77 | var batchItems = Keys.Skip(currIndex).Take(batchNum).ToList();
78 | var value = Expression.Constant(batchItems, typeof(IEnumerable));
79 | var containsMethod = Expression.Call(method, value, KeyExpresion.Body);
80 | var predicate = Expression.Lambda>(containsMethod, parameter);
81 | var query = Session.Query()
82 | .Where(predicate);
83 |
84 | if (BeforeQueryExecutionFunction != null)
85 | {
86 | query = BeforeQueryExecutionFunction(query);
87 | }
88 |
89 | result.AddRange(ToList(convertQuery(query)));
90 | currIndex += batchNum;
91 | }
92 |
93 | return result;
94 | }
95 |
96 | private List ToList(IQueryable query)
97 | {
98 | return query.ToList();
99 | }
100 |
101 | private Task> ToListAsync(IQueryable query, CancellationToken cancellationToken)
102 | {
103 | return query.ToListAsync(cancellationToken);
104 | }
105 | }
106 |
107 |
108 | public partial class BatchFetchBuilder : BatchFetchBuilder, IBatchFetchBuilder
109 | {
110 | public BatchFetchBuilder(ISession session, ICollection keys, Expression> keyExpresion, int batchSize,
111 | Expression> selectExpression)
112 | : base(session, keys, keyExpresion, batchSize)
113 | {
114 | SelectExpression = selectExpression;
115 | }
116 |
117 | public Expression> SelectExpression { get; }
118 |
119 |
120 |
121 | IBatchFetchBuilder IBatchFetchBuilder.BeforeQueryExecution(Func, IQueryable> queryFunc)
122 | {
123 | return new BatchFetchBuilder(Session, Keys.ToList(), KeyExpresion, BatchSize, SelectExpression)
124 | {
125 | BeforeQueryExecutionFunction = queryFunc
126 | };
127 | }
128 |
129 | List IBatchFetchBuilder.Execute()
130 | {
131 | return Execute(q => q.Select(SelectExpression));
132 | }
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/NHibernate.Extensions/BatchFetchExtension.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.CodeDom;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Linq.Expressions;
6 | using System.Reflection;
7 |
8 | namespace NHibernate.Extensions
9 | {
10 | public static partial class BatchFetchExtension
11 | {
12 | internal static readonly MethodInfo ContainsMethodInfo;
13 |
14 | static BatchFetchExtension()
15 | {
16 | ContainsMethodInfo = typeof(Enumerable)
17 | .GetMethods()
18 | .Where(x => x.Name == "Contains")
19 | .Single(x => x.GetParameters().Length == 2);
20 | }
21 |
22 | ///
23 | /// Batch fetching a collecion of keys by using ISession Linq provider
24 | ///
25 | ///
26 | ///
27 | /// NHibernate session
28 | /// Collection of keys that will be retrieved from the database
29 | /// Expression pointing to the property that represents the key
30 | /// Number of records that will be retrieved within one execution
31 | /// Function to modify the query prior execution
32 | /// The fetched entites.
33 | public static List BatchFetch(this ISession session, ICollection keys,
34 | Expression> propertyExpr,
35 | int batchSize,
36 | Func, IQueryable> queryFunc = null)
37 | {
38 | if (propertyExpr == null)
39 | {
40 | throw new ArgumentNullException(nameof(propertyExpr));
41 | }
42 |
43 | if (keys == null)
44 | {
45 | throw new ArgumentNullException(nameof(keys));
46 | }
47 |
48 | return session.BatchFetch(batchSize)
49 | .SetKeys(keys, propertyExpr)
50 | .BeforeQueryExecution(queryFunc)
51 | .Execute();
52 | }
53 |
54 | ///
55 | /// Batch fetching a collecion of keys by using ISession Linq provider
56 | ///
57 | ///
58 | /// NHibernate session
59 | /// Number of records that will be retrieved within one execution
60 | /// The batch fetch builder.
61 | public static IBatchFetchBuilder BatchFetch(this ISession session, int batchSize)
62 | {
63 | return new BatchFetchBuilder(session, batchSize);
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/NHibernate.Extensions/DeepClone/DeepCloneMemberOptions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace NHibernate.Extensions
4 | {
5 | public class DeepCloneMemberOptions : IDeepCloneMemberOptions
6 | {
7 | public string MemberName { get; set; }
8 |
9 | public Func