1+ // Copyright (C) 2018 Xtensive LLC.
2+ // All rights reserved.
3+ // For conditions of distribution and use, see license.
4+ // Created by: Alexey Kulakov
5+ // Created: 2018.10.01
6+
7+ using System ;
8+ using System . Collections . Generic ;
9+ using System . Diagnostics ;
10+ using System . Linq ;
11+ using System . Reflection ;
12+ using NUnit . Framework ;
13+ using Xtensive . Collections ;
14+ using Xtensive . Core ;
15+ using Xtensive . IoC ;
16+ using Xtensive . Orm . Building ;
17+ using Xtensive . Orm . Building . Builders ;
18+ using Xtensive . Orm . Building . Definitions ;
19+ using Xtensive . Orm . Configuration ;
20+ using Xtensive . Orm . Providers ;
21+ using Xtensive . Orm . Upgrade ;
22+ using Xtensive . Reflection ;
23+ using Xtensive . Sql ;
24+
25+ namespace Xtensive . Orm . Tests . Model
26+ {
27+ [ TestFixture ]
28+ public abstract class ModelBuildingTest
29+ {
30+ private const string ErrorInTestFixtureSetup = "Error in TestFixtureSetUp:\r \n {0}" ;
31+
32+ protected bool shouldBuildRealDomain = false ;
33+
34+ [ OneTimeSetUp ]
35+ public void TestFixureSetUp ( )
36+ {
37+ try {
38+ CheckRequirements ( ) ;
39+ PopulateData ( ) ;
40+ }
41+ catch ( IgnoreException ) {
42+ throw ;
43+ }
44+ catch ( Exception e ) {
45+ Debug . WriteLine ( ErrorInTestFixtureSetup , e ) ;
46+ throw ;
47+ }
48+ }
49+
50+ protected virtual void CheckRequirements ( )
51+ {
52+ }
53+
54+ protected virtual void PopulateData ( )
55+ {
56+ }
57+
58+ protected void TestInvoker ( DomainConfiguration domainConfiguration , Action < Domain > domainValidationAction )
59+ => TestInvoker ( domainConfiguration , domainValidationAction , null ) ;
60+
61+ protected void TestInvoker ( DomainConfiguration domainConfiguration , Action < DomainModelDef > domainDefValidationAction )
62+ => TestInvoker ( domainConfiguration , null , domainDefValidationAction ) ;
63+
64+ protected void TestInvoker ( DomainConfiguration domainConfiguration , Action < Domain > domainValidationAction , Action < DomainModelDef > domainDefsValidationAction )
65+ {
66+ domainConfiguration . Types . Register ( typeof ( ModelDefCapturer ) ) ;
67+
68+ var upgradeContext = new UpgradeContext ( domainConfiguration ) ;
69+
70+ if ( shouldBuildRealDomain ) {
71+ using ( var domain = Domain . Build ( domainConfiguration ) ) {
72+ var modelDef = domain . Extensions . Get < DomainModelDef > ( ) ;
73+ if ( domainDefsValidationAction != null && modelDef != null ) {
74+ domainDefsValidationAction . Invoke ( modelDef ) ;
75+ }
76+
77+ if ( domainValidationAction != null ) {
78+ domainValidationAction . Invoke ( domain ) ;
79+ }
80+ }
81+ }
82+ else {
83+ using ( upgradeContext . Activate ( ) )
84+ using ( upgradeContext . Services ) {
85+ BuildServices ( upgradeContext ) ;
86+ var domain = CreateDomainBuilder ( upgradeContext ) . Invoke ( ) ;
87+ var modelDef = domain . Extensions . Get < DomainModelDef > ( ) ;
88+ if ( domainDefsValidationAction != null && modelDef != null )
89+ domainDefsValidationAction . Invoke ( modelDef ) ;
90+ if ( domainValidationAction != null )
91+ domainValidationAction . Invoke ( domain ) ;
92+ }
93+ }
94+ }
95+
96+ private Func < Domain > CreateDomainBuilder ( UpgradeContext context )
97+ {
98+ var buildingConfiguration = BuildBuilderConfiguration ( context ) ;
99+
100+ Func < DomainBuilderConfiguration , Domain > builder = DomainBuilder . Run ;
101+ return builder . Bind ( buildingConfiguration ) ;
102+ }
103+
104+ private DomainBuilderConfiguration BuildBuilderConfiguration ( UpgradeContext context )
105+ {
106+ var configuration = new DomainBuilderConfiguration {
107+ DomainConfiguration = context . Configuration ,
108+ Stage = context . Stage ,
109+ Services = context . Services ,
110+ ModelFilter = new StageModelFilter ( context . UpgradeHandlers , UpgradeStage . Final ) ,
111+ UpgradeContextCookie = context . Cookie ,
112+ RecycledDefinitions = context . RecycledDefinitions ,
113+ DefaultSchemaInfo = null
114+ } ;
115+
116+ return configuration ;
117+ }
118+
119+ private void BuildServices ( UpgradeContext context )
120+ {
121+ var services = context . Services ;
122+ var configuration = context . Configuration ;
123+
124+ services . Configuration = configuration ;
125+ services . IndexFilterCompiler = new PartialIndexFilterCompiler ( ) ;
126+
127+ var descriptor = ProviderDescriptor . Get ( configuration . ConnectionInfo . Provider ) ;
128+ var driverFactory = ( SqlDriverFactory ) Activator . CreateInstance ( descriptor . DriverFactory ) ;
129+ var handlerFactory = ( HandlerFactory ) Activator . CreateInstance ( descriptor . HandlerFactory ) ;
130+ var driver = StorageDriver . Create ( driverFactory , configuration ) ;
131+ services . HandlerFactory = handlerFactory ;
132+ services . StorageDriver = driver ;
133+ services . NameBuilder = new NameBuilder ( configuration , driver . ProviderInfo ) ;
134+ BuildExternalServices ( services , context ) ;
135+
136+ services . Lock ( ) ;
137+
138+ context . TypeIdProvider = new TypeIdProvider ( context ) ;
139+ }
140+
141+ private void BuildExternalServices ( UpgradeServiceAccessor serviceAccessor , UpgradeContext context )
142+ {
143+ var configuration = context . Configuration ;
144+ var standardRegistrations = new [ ] {
145+ new ServiceRegistration ( typeof ( DomainConfiguration ) , context . Configuration ) ,
146+ new ServiceRegistration ( typeof ( UpgradeContext ) , context )
147+ } ;
148+
149+ var modules = configuration . Types . Modules
150+ . Select ( type => new ServiceRegistration ( typeof ( IModule ) , type , false ) ) ;
151+ var handlers = configuration . Types . UpgradeHandlers
152+ . Select ( type => new ServiceRegistration ( typeof ( IUpgradeHandler ) , type , false ) ) ;
153+
154+ var registrations = standardRegistrations . Concat ( modules ) . Concat ( handlers ) ;
155+ var serviceContainer = new ServiceContainer ( registrations ) ;
156+ serviceAccessor . RegisterResource ( serviceContainer ) ;
157+
158+ BuildModules ( serviceAccessor , serviceContainer ) ;
159+ BuildUpgradeHandlers ( serviceAccessor , serviceContainer ) ;
160+ }
161+
162+ private static void BuildModules ( UpgradeServiceAccessor serviceAccessor , IServiceContainer serviceContainer )
163+ => serviceAccessor . Modules = new ReadOnlyList < IModule > ( serviceContainer . GetAll < IModule > ( ) . ToList ( ) ) ;
164+
165+ private static void BuildUpgradeHandlers ( UpgradeServiceAccessor serviceAccessor , IServiceContainer serviceContainer )
166+ {
167+ // Getting user handlers
168+ var userHandlers =
169+ from handler in serviceContainer . GetAll < IUpgradeHandler > ( )
170+ let assembly = handler . Assembly ?? handler . GetType ( ) . Assembly
171+ where handler . IsEnabled
172+ group handler by assembly ;
173+
174+ // Adding user handlers
175+ var handlers = new Dictionary < Assembly , IUpgradeHandler > ( ) ;
176+ foreach ( var group in userHandlers ) {
177+ var candidates = group . ToList ( ) ;
178+ if ( candidates . Count > 1 ) {
179+ throw new DomainBuilderException (
180+ string . Format ( Strings . ExMoreThanOneEnabledXIsProvidedForAssemblyY , typeof ( IUpgradeHandler ) . GetShortName ( ) , @group . Key ) ) ;
181+ }
182+ handlers . Add ( group . Key , candidates [ 0 ] ) ;
183+ }
184+
185+ // Adding default handlers
186+ var assembliesWithUserHandlers = handlers . Select ( pair => pair . Key ) ;
187+ var assembliesWithoutUserHandler =
188+ serviceAccessor . Configuration . Types . PersistentTypes
189+ . Select ( type => type . Assembly )
190+ . Distinct ( )
191+ . Except ( assembliesWithUserHandlers ) ;
192+
193+ foreach ( var assembly in assembliesWithoutUserHandler ) {
194+ var handler = new UpgradeHandler ( assembly ) ;
195+ handlers . Add ( assembly , handler ) ;
196+ }
197+
198+ // Building a list of handlers sorted by dependencies of their assemblies
199+ var dependencies = handlers . Keys . ToDictionary (
200+ assembly => assembly ,
201+ assembly => new HashSet < string > ( assembly . GetReferencedAssemblies ( ) . Select ( assemblyName => assemblyName . ToString ( ) ) ) ) ;
202+ var sortedHandlers = handlers
203+ . SortTopologically ( ( a0 , a1 ) => dependencies [ a1 . Key ] . Contains ( a0 . Key . GetName ( ) . ToString ( ) ) )
204+ . Select ( pair => pair . Value ) ;
205+
206+ // Storing the result
207+ serviceAccessor . UpgradeHandlers =
208+ new ReadOnlyDictionary < Assembly , IUpgradeHandler > ( handlers ) ;
209+ serviceAccessor . OrderedUpgradeHandlers =
210+ new ReadOnlyList < IUpgradeHandler > ( sortedHandlers . ToList ( ) ) ;
211+ }
212+
213+ public sealed class ModelDefCapturer : IModule
214+ {
215+ public void OnBuilt ( Domain domain )
216+ {
217+ }
218+
219+ public void OnDefinitionsBuilt ( BuildingContext context , DomainModelDef model )
220+ {
221+ var domain = context . Domain ;
222+ domain . Extensions . Set ( model ) ;
223+ }
224+ }
225+ }
226+ }
0 commit comments