3
3
using Dataverse . ConfigurationMigrationTool . Console . Features . Shared ;
4
4
using Dataverse . ConfigurationMigrationTool . Console . Services . Dataverse ;
5
5
using Microsoft . Extensions . Logging ;
6
+ using Microsoft . PowerPlatform . Dataverse . Client ;
6
7
using Microsoft . Xrm . Sdk ;
7
8
using Microsoft . Xrm . Sdk . Messages ;
8
9
using Microsoft . Xrm . Sdk . Metadata ;
10
+ using System . ServiceModel ;
9
11
10
12
namespace Dataverse . ConfigurationMigrationTool . Console . Features . Import
11
13
{
@@ -25,13 +27,15 @@ public class ImportTaskProcessorService : IImportTaskProcessorService
25
27
private readonly ILogger < ImportTaskProcessorService > logger ;
26
28
private readonly IDataverseValueConverter _dataverseValueConverter ;
27
29
private readonly IBulkOrganizationService bulkOrganizationService ;
30
+ private IOrganizationServiceAsync2 _organizationService ;
28
31
29
- public ImportTaskProcessorService ( IMetadataService metadataService , ILogger < ImportTaskProcessorService > logger , IDataverseValueConverter dataverseValueConverter , IBulkOrganizationService bulkOrganizationService )
32
+ public ImportTaskProcessorService ( IMetadataService metadataService , ILogger < ImportTaskProcessorService > logger , IDataverseValueConverter dataverseValueConverter , IBulkOrganizationService bulkOrganizationService , IOrganizationServiceAsync2 organizationService )
30
33
{
31
34
this . metadataService = metadataService ;
32
35
this . logger = logger ;
33
36
_dataverseValueConverter = dataverseValueConverter ;
34
37
this . bulkOrganizationService = bulkOrganizationService ;
38
+ _organizationService = organizationService ;
35
39
}
36
40
37
41
public async Task < TaskResult > Execute ( ImportDataTask task , Entities dataImport )
@@ -65,17 +69,17 @@ private async Task<TaskResult> ImportRecords(EntityMetadata entity, ImportDataTa
65
69
{
66
70
67
71
var recordsWithNoSelfDependancies = entityImport . Records . Record . Where ( r =>
68
- ! entityImport . Records . Record . Any ( r2 => r2 . Id != r . Id &&
69
- r2 . Field . Any ( f => f . Lookupentity != null && f . Value == r . Id . ToString ( ) ) ) ) ;
72
+ ! r . Field . Any ( f => f . Lookupentity == entityImport . Name &&
73
+ entityImport . Records . Record . Any ( r2 => r2 . Id != r . Id && r2 . Id . ToString ( ) == f . Value ) ) ) . Select ( r => BuildUpsertRequest ( entity , entityImport , r ) ) . ToList ( ) ;
70
74
var recordsWithSelfDependancies = entityImport . Records . Record . Where ( r =>
71
- entityImport . Records . Record . Any ( r2 => r2 . Id != r . Id &&
72
- r2 . Field . Any ( f => f . Lookupentity != null && f . Value == r . Id . ToString ( ) ) ) ) ;
73
- var requests = recordsWithNoSelfDependancies . Select ( r => BuildUpsertRequest ( entity , entityImport , r ) ) . ToList ( ) ;
75
+ r . Field . Any ( f => f . Lookupentity == entityImport . Name &&
76
+ entityImport . Records . Record . Any ( r2 => r2 . Id != r . Id && r2 . Id . ToString ( ) == f . Value ) ) ) . ToList ( ) ;
77
+
74
78
75
79
//See if upsert request keep ids
76
80
77
81
//implement parallelism and batching
78
- var responses = await bulkOrganizationService . ExecuteBulk ( requests ) ;
82
+ var responses = await bulkOrganizationService . ExecuteBulk ( recordsWithNoSelfDependancies ) ;
79
83
80
84
foreach ( var response in responses )
81
85
{
@@ -88,11 +92,66 @@ private async Task<TaskResult> ImportRecords(EntityMetadata entity, ImportDataTa
88
92
89
93
}
90
94
var resultTask = responses . Any ( ) ? TaskResult . Failed : TaskResult . Completed ;
95
+
96
+ var singleResponses = await ProcessDependantRecords ( recordsWithSelfDependancies , entity , entityImport ) ;
97
+ foreach ( var response in singleResponses )
98
+ {
99
+
100
+ var targetRequest = ( response . OriginalRequest as UpsertRequest ) . Target ;
101
+
102
+
103
+ logger . LogError ( "{logicalname}({id}) upsert failed because: {fault}" , targetRequest . LogicalName , targetRequest . Id , response . Fault . Message ) ;
104
+
105
+
106
+ }
107
+ resultTask = singleResponses . Any ( ) ? TaskResult . Failed : resultTask ;
91
108
logger . LogInformation ( "Import Task of {entityname} records terminated in a {State} state" , entityImport . Name , resultTask ) ;
92
109
return resultTask ;
93
110
94
111
}
112
+ private async Task < IEnumerable < OrganizationResponseFaultedResult > > ProcessDependantRecords ( IEnumerable < Record > records , EntityMetadata entity , EntityImport entityImport )
113
+ {
114
+ var retries = new Dictionary < Guid , int > ( ) ;
115
+ var queue = new Queue < Record > ( records ) ;
116
+ var results = new List < OrganizationResponseFaultedResult > ( ) ;
117
+ while ( queue . Count > 0 )
118
+ {
119
+ var record = queue . Dequeue ( ) ;
120
+
121
+ if ( record . Field . Any ( f => f . Lookupentity == entityImport . Name && queue . Any ( r => r . Id . ToString ( ) == f . Value ) ) )
122
+ {
123
+
124
+ if ( retries . ContainsKey ( record . Id ) && retries [ record . Id ] >= 3 )
125
+ {
126
+ logger . LogWarning ( "{entityType}({id}) was skipped because his parent was not proccessed." , entityImport . Name , record . Id )
127
+ continue;
128
+ }
129
+ //Enqueue record again until his parent is processed.
130
+ queue . Enqueue ( record ) ;
131
+ retries [ record . Id ] = retries . ContainsKey ( record . Id ) ? retries [ record . Id ] + 1 : 1 ;
132
+ continue ;
133
+ }
134
+ var request = BuildUpsertRequest ( entity , entityImport , record ) ;
135
+ try
136
+ {
137
+
138
+ var response = ( await _organizationService . ExecuteAsync ( request ) ) as UpsertResponse ;
139
+
140
+
141
+ }
142
+ catch ( FaultException < OrganizationServiceFault > faultEx )
143
+ {
144
+ results . Add ( new OrganizationResponseFaultedResult
145
+ {
146
+ Fault = faultEx . Detail ,
147
+ OriginalRequest = request ,
148
+ } ) ;
149
+ }
150
+
151
+ }
152
+ return results ;
95
153
154
+ }
96
155
private UpsertRequest BuildUpsertRequest ( EntityMetadata entityMD , EntityImport entityImport , Record record )
97
156
{
98
157
var entity = new Entity ( entityImport . Name , record . Id ) ;
0 commit comments