/// <summary> /// Static CTOR /// </summary> static BaseRecordMatchingService() { foreach (var t in typeof(BaseRecordMatchingService).Assembly.ExportedTypes.Where(t => typeof(IQueryFilterExtension).IsAssignableFrom(t) && !t.IsAbstract)) { QueryFilterExtensions.AddExtendedFilter(Activator.CreateInstance(t) as IQueryFilterExtension); } ModelSerializationBinder.RegisterModelType(typeof(MatchConfiguration)); }
public void TestCanParseNonStandardBundle() { ModelSerializationBinder.RegisterModelType(typeof(EntityMaster <Patient>)); using (var stream = typeof(NonStandardEntityInBundleTest).Assembly.GetManifestResourceStream("SanteDB.Core.Api.Test.Resources.NonStandardBundle.xml")) { var xsz = XmlModelSerializerFactory.Current.CreateSerializer(typeof(Bundle)); var bundle = xsz.Deserialize(stream) as Bundle; Assert.AreEqual(4, bundle.Item.Count); } }
// Static ctor static AgsDispatchFormatter() { m_defaultViewModel = ViewModelDescription.Load(Assembly.Load("SanteDB.Rest.Common").GetManifestResourceStream("SanteDB.Rest.Common.Resources.ViewModel.xml")); var tracer = Tracer.GetTracer(typeof(AgsDispatchFormatter <TContract>)); foreach (var t in s_knownTypes) { ModelSerializationBinder.RegisterModelType(t); } tracer.TraceInfo("Will generate serializer for {0}", typeof(TContract).FullName); }
/// <summary> /// Get all types from core classes of entity and act and create shims in the model serialization binder /// </summary> static MdmDataManager() { foreach (var t in typeof(Entity).GetTypeInfo().Assembly.ExportedTypes.Where(o => typeof(Entity).GetTypeInfo().IsAssignableFrom(o.GetTypeInfo()))) { ModelSerializationBinder.RegisterModelType(typeof(EntityMaster <>).MakeGenericType(t)); } foreach (var t in typeof(Act).GetTypeInfo().Assembly.ExportedTypes.Where(o => typeof(Act).GetTypeInfo().IsAssignableFrom(o.GetTypeInfo()))) { ModelSerializationBinder.RegisterModelType(typeof(ActMaster <>).MakeGenericType(t)); } }
/// <summary> /// Start the service /// </summary> public bool Start() { // Don't startup unless in SanteDB if (Assembly.GetEntryAssembly().GetName().Name != "SanteDB") { return(true); } try { this.Starting?.Invoke(this, EventArgs.Empty); this.m_webHost = ApplicationContext.Current.GetService <IRestServiceFactory>().CreateService(typeof(AmiServiceBehavior)); this.m_webHost.AddServiceBehavior(new ErrorServiceBehavior()); // Add service behaviors foreach (ServiceEndpoint endpoint in this.m_webHost.Endpoints) { this.m_traceSource.TraceInfo("Starting AMI on {0}...", endpoint.Description.ListenUri); } // Start the webhost this.m_webHost.Start(); ModelSerializationBinder.RegisterModelType(typeof(SecurityPolicyInfo)); if (this.m_configuration.ResourceHandlers.Count() > 0) { AmiMessageHandler.ResourceHandler = new ResourceHandlerTool(this.configuration.ResourceHandlers, typeof(IAmiServiceContract)); } else { AmiMessageHandler.ResourceHandler = new ResourceHandlerTool( ApplicationServiceContext.Current.GetService <IServiceManager>().GetAllTypes() .Where(t => !t.IsAbstract && !t.IsInterface && typeof(IApiResourceHandler).IsAssignableFrom(t)) .ToList(), typeof(IAmiServiceContract) ); } this.Started?.Invoke(this, EventArgs.Empty); return(true); } catch (Exception e) { this.m_traceSource.TraceEvent(EventLevel.Error, e.ToString()); return(false); } }
/// <summary> /// Get all types from core classes of entity and act and create shims in the model serialization binder /// </summary> public RemoteRepositoryFactory(IConfigurationManager configurationManager, IServiceManager serviceManager, ILocalizationService localizationService) { foreach (var t in typeof(Entity).Assembly.ExportedTypes.Where(o => typeof(Entity).IsAssignableFrom(o))) { ModelSerializationBinder.RegisterModelType(typeof(EntityMaster <>).MakeGenericType(t)); } foreach (var t in typeof(Act).Assembly.ExportedTypes.Where(o => typeof(Act).IsAssignableFrom(o))) { ModelSerializationBinder.RegisterModelType(typeof(ActMaster <>).MakeGenericType(t)); } ModelSerializationBinder.RegisterModelType(typeof(EntityRelationshipMaster)); this.m_localizationService = localizationService; this.m_serviceManager = serviceManager; this.m_configuration = configurationManager.GetSection <ApplicationServiceContextConfigurationSection>(); }
/// <summary> /// Render the object /// </summary> public void Render(XElement element, XmlWriter writer, IRenderContext context) { var resourceType = new ModelSerializationBinder().BindToType(String.Empty, element.Attribute("resource")?.Value); if (resourceType == null) { writer.WriteStartElement("span"); writer.WriteAttributeString("style", "color:red"); writer.WriteString($"{element.Attribute("resource")?.Value} not valid"); writer.WriteEndElement(); } else { var render = element.Attribute("render")?.Value; var identifier = ReportViewUtil.GetValue(context, element.Value); if (identifier != null) { var dpType = typeof(IDataPersistenceService <>).MakeGenericType(resourceType); var dpService = ApplicationServiceContext.Current.GetService(dpType) as IDataPersistenceService; var key = identifier is Guid || identifier is Guid? ? (Guid)identifier : Guid.Parse(identifier.ToString()); var instance = dpService.Get(key); if (instance == null) { writer.WriteComment($"{resourceType.Name}/{key} not found"); } else if (!String.IsNullOrEmpty(render)) { var selKey = $"{resourceType.Name}.{render}"; if (!this.m_selectors.TryGetValue(selKey, out Delegate selDelegate)) { selDelegate = QueryExpressionParser.BuildPropertySelector(resourceType, render).Compile(); this.m_selectors.TryAdd(selKey, selDelegate); } writer.WriteString(selDelegate.DynamicInvoke(instance)?.ToString()); } else { writer.WriteString(instance?.ToString()); } } } }
/// <summary> /// Match configuration /// </summary> static MatchConfiguration() { ModelSerializationBinder.RegisterModelType(typeof(MatchConfiguration)); }
/// <summary> /// Let's resolve the specified resource /// </summary> public IdentifiedData ResolveResource(string data, bool validate = true) { try { var match = this.m_jwsFormat.Match(data); if (!match.Success) { throw new DetectedIssueException(new DetectedIssue(DetectedIssuePriorityType.Error, "jws.invalid", "Invalid Barcode Format", DetectedIssueKeys.InvalidDataIssue)); } // Get the parts of the header byte[] headerBytes = match.Groups[1].Value.ParseBase64UrlEncode(), bodyBytes = match.Groups[2].Value.ParseBase64UrlEncode(), signatureBytes = match.Groups[3].Value.ParseBase64UrlEncode(); // Now lets parse the JSON objects dynamic header = JsonConvert.DeserializeObject(Encoding.UTF8.GetString(headerBytes)); dynamic body = JsonConvert.DeserializeObject(Encoding.UTF8.GetString(bodyBytes)); // Now validate the payload if (!header.typ.ToString().StartsWith("x-santedb+")) { throw new DetectedIssueException(new DetectedIssue(DetectedIssuePriorityType.Error, "jws.invalid.type", "Invalid Barcode Type", DetectedIssueKeys.InvalidDataIssue)); } var type = new ModelSerializationBinder().BindToType(null, header.typ.ToString().Substring(10)); var algorithm = header.alg.ToString(); String keyId = header.key.ToString(); // Attempt to locate the record var domainQuery = new NameValueCollection(); foreach (var id in body.id) { domainQuery.Add($"identifier[{id.ns.ToString()}].value", id.value.ToString()); } var filterExpression = QueryExpressionParser.BuildLinqExpression(type, domainQuery); // Get query var repoType = typeof(IRepositoryService <>).MakeGenericType(type); var repoService = (ApplicationServiceContext.Current as IServiceProvider).GetService(repoType) as IRepositoryService; if (repoService == null) { throw new InvalidOperationException("Cannot find appropriate repository service"); } // HACK: .NET is using late binding and getting confused var results = repoService.Find(filterExpression, 0, 2, out int tr) as IEnumerable <IdentifiedData>; if (tr > 1) { throw new InvalidOperationException("Resource is ambiguous (points to more than one resource)"); } var result = results.FirstOrDefault(); // Validate the signature if we have the key if (validate) { // We have the key? if (!this.m_signingService.GetKeys().Any(k => k == keyId)) { // Is this an app key id? if (keyId.StartsWith("SA.")) { var appId = Guid.Parse(keyId.Substring(3)); var appInstance = ApplicationServiceContext.Current.GetService <IRepositoryService <SecurityApplication> >().Get(appId); if (appInstance == null) { throw new DetectedIssueException(new DetectedIssue(DetectedIssuePriorityType.Error, "jws.app", "Unknown source application", DetectedIssueKeys.SecurityIssue)); } var secret = ApplicationServiceContext.Current.GetService <IApplicationIdentityProviderService>()?.GetSecureKey(appInstance.Name); this.m_signingService.AddSigningKey(keyId, secret, "HS256"); } else { throw new DetectedIssueException(new DetectedIssue(DetectedIssuePriorityType.Error, "jws.key", "Invalid Key Type", DetectedIssueKeys.SecurityIssue)); } } if (this.m_signingService.GetSignatureAlgorithm(keyId) != algorithm) { throw new DetectedIssueException(new DetectedIssue(DetectedIssuePriorityType.Error, "jws.algorithm", $"Algorithm {algorithm} Not Supported (expected {this.m_signingService.GetSignatureAlgorithm(keyId)})", DetectedIssueKeys.SecurityIssue)); } var payload = Encoding.UTF8.GetBytes($"{match.Groups[1].Value}.{match.Groups[2].Value}"); if (!this.m_signingService.Verify(payload, signatureBytes, keyId)) { throw new DetectedIssueException(new DetectedIssue(DetectedIssuePriorityType.Error, "jws.verification", "Barcode Tampered", DetectedIssueKeys.SecurityIssue)); } } // Return the result return(result); } catch (DetectedIssueException) { throw; } catch (Exception e) { throw new Exception("Cannot resolve QR code", e); } }
/// <summary> /// Read from the stream /// </summary> public static PeerTransferPayload Read(Stream s, IDataSigningService signingProvider, bool validateSignature) { byte[] hdr = new byte[7]; s.Read(hdr, 0, 7); if (!hdr.Take(5).SequenceEqual(MAGIC_HEADER)) { throw new FormatException("Invalid payload"); } else if (hdr[5] >= VERSION_ID) { throw new InvalidOperationException($"Payload version {hdr[5]} is greater than supported version of {VERSION_ID}"); } var retVal = new PeerTransferPayload(); retVal.Encoding = (PeerTransferEncodingFlags)hdr[6]; // Read the rest of the stream if (retVal.Encoding.HasFlag(PeerTransferEncodingFlags.Compressed)) { s = new GZipStream(s, SharpCompress.Compressors.CompressionMode.Decompress); } using (var sr = new StreamReader(s)) { var data = sr.ReadToEnd(); // Read the JWS header var match = s_jwsFormat.Match(data); if (!match.Success) { throw new FormatException("Payload must be in JWS format"); } // Get the parts of the header byte[] headerBytes = match.Groups[1].Value.ParseBase64UrlEncode(), bodyBytes = match.Groups[2].Value.ParseBase64UrlEncode(), signatureBytes = match.Groups[3].Value.ParseBase64UrlEncode(); // Now lets parse the JSON objects dynamic header = JsonConvert.DeserializeObject(System.Text.Encoding.UTF8.GetString(headerBytes)); dynamic body = JsonConvert.DeserializeObject(System.Text.Encoding.UTF8.GetString(bodyBytes)); // Now validate the payload if (!header.typ.ToString().StartsWith("x-santedb+")) { throw new InvalidOperationException("Cannot determine type of data"); } var type = new ModelSerializationBinder().BindToType(null, header.typ.ToString().Substring(10)); var algorithm = header.alg.ToString(); String keyId = header.key.ToString(); // Validate the signature if we have the key if (validateSignature) { // We have the key? if (!signingProvider.GetKeys().Any(k => k == keyId)) { throw new InvalidOperationException("Cannot find appropriate validation key"); } if (signingProvider.GetSignatureAlgorithm(keyId) != algorithm) { throw new InvalidOperationException("Invalid signature algorithm"); } var payload = System.Text.Encoding.UTF8.GetBytes($"{match.Groups[1].Value}.{match.Groups[2].Value}"); if (!signingProvider.Verify(payload, signatureBytes, keyId)) { throw new SecurityException("Cannot verify authenticity of the specified data payload"); } } retVal.Payload = JsonConvert.DeserializeObject(System.Text.Encoding.UTF8.GetString(bodyBytes), type); // Return the result return(retVal); } }
// [PolicyPermission(System.Security.Permissions.SecurityAction.Demand, PolicyId = PermissionPolicyIdentifiers.CreateDevice)] internal static void QueryData(HdsiQueryParameters parms) { if (String.IsNullOrEmpty(parms.ResourceType)) { throw new ArgumentNullException("Require --resourceType or -r"); } // Get the type var type = new ModelSerializationBinder().BindToType(null, parms.ResourceType); if (type == null) { throw new InvalidOperationException($"Cannot find reosurce type {parms.ResourceType}"); } if (!parms.AsDataSet) { Console.WriteLine("Type: {0}", type); } // Build the parameter list NameValueCollection nvc = new NameValueCollection(); if (parms.Filter != null) { foreach (var kv in parms.Filter) { var f = kv.Split('='); nvc.Add(f[0], f[1]); } } Int32.TryParse(parms.Offset ?? "0", out int offset); Int32.TryParse(parms.Count ?? "25", out int count); if (parms.Display == null) { parms.Display = new System.Collections.Specialized.StringCollection() { "id", "ToString" } } ; // Get the specified lambda expression var builderMethod = typeof(QueryExpressionParser).GetGenericMethod(nameof(QueryExpressionParser.BuildLinqExpression), new Type[] { type }, new Type[] { typeof(NameValueCollection) }); var linqExpression = builderMethod.Invoke(null, new object[] { nvc }); if (!parms.AsDataSet) { Console.WriteLine("Filter: {0}", linqExpression); } // Fetch results var queryMethod = m_client.GetType().GetGenericMethod(nameof(HdsiServiceClient.Query), new Type[] { type }, new Type[] { linqExpression.GetType(), typeof(int), typeof(int?), typeof(String[]), typeof(Guid?), typeof(ModelSort <>).MakeGenericType(type).MakeArrayType() }); var result = queryMethod.Invoke(m_client, new object[] { linqExpression, offset, count, parms.Expand?.OfType <String>().ToArray(), null, null }) as Bundle; if (!parms.AsDataSet) { Console.WriteLine("Result: {0} .. {1} of {2}", result.Offset, result.Item.Count, result.TotalResults); var displayCols = parms.Display.OfType <String>().Select(o => { return((Expression <Func <IdentifiedData, Object> >)(col => o == "ToString" ? col.ToString() : QueryExpressionParser.BuildPropertySelector(type, o, true).Compile().DynamicInvoke(col))); }).ToArray(); DisplayUtil.TablePrint <IdentifiedData>(result.Item, parms.Display.OfType <String>().ToArray(), parms.Display.OfType <String>().Select(o => 40).ToArray(), displayCols); } else { Dataset ds = new Dataset($"sdbac Dataset for {type} filter {nvc}"); Delegate displaySelector = null; if (parms.Display.Count > 0) { displaySelector = QueryExpressionParser.BuildPropertySelector(type, parms.Display.OfType <String>().FirstOrDefault(), true).Compile(); } foreach (var itm in result.Item) { ds.Action.Add(new DataUpdate() { InsertIfNotExists = true, IgnoreErrors = true, Element = (IdentifiedData)(displaySelector != null ? displaySelector.DynamicInvoke(itm) : itm) }); } m_xsz.Serialize(Console.Out, ds); } } }
/// <summary> /// Follow property path /// </summary> /// TODO: Clean this up private object FollowPath(Type scopingType, string propertyPath, JObject variables) { // Get rid f the . if (propertyPath.StartsWith(".")) { propertyPath = propertyPath.Substring(1); } var retVal = new AutoCompleteTypeInfo(scopingType); var propertyExtract = this.m_propertyExtractor.Match(propertyPath); if (propertyExtract.Success) { var property = retVal.Properties.FirstOrDefault(o => o.Name == propertyExtract.Groups[1].Value); // Is the property path full? if (property == null) { // Is it a variable? if (propertyExtract.Groups[1].Value.StartsWith("$")) { var variable = variables[propertyExtract.Groups[1].Value]; if (variable != null) { return(this.FollowPath(new ModelSerializationBinder().BindToType(null, variable.Value <String>()), propertyExtract.Groups[3].Value, variables)); } else { return(variables.Values().Select(o => o.Path.Substring(1)).ToArray()); } } else if (propertyExtract.Groups[1].Value.StartsWith(":(")) // function { var functionMatch = this.m_functionExtractor.Match(propertyPath); if (functionMatch.Success && (!String.IsNullOrEmpty(functionMatch.Groups[2].Value) || !String.IsNullOrEmpty(functionMatch.Groups[3].Value))) { return(this.FollowPath(typeof(Object), String.IsNullOrEmpty(functionMatch.Groups[2].Value) ? functionMatch.Groups[3].Value : functionMatch.Groups[2].Value, variables)); } else { return(QueryFilterExtensions.GetExtendedFilters().Select(o => o.Name)); } } return(retVal); } else if (!String.IsNullOrEmpty(propertyExtract.Groups[2].Value)) { if (propertyExtract.Groups[2].Value.StartsWith("@")) // We're casting { var modelType = new ModelSerializationBinder().BindToType(null, propertyExtract.Groups[2].Value.Substring(1)); if (modelType != null) { return(this.FollowPath(modelType, propertyExtract.Groups[3].Value, variables)); } else { return(this.FollowPath(property.SourceProperty.PropertyType.StripGeneric(), propertyExtract.Groups[3].Value, variables)); } } else { return(this.FollowPath(property.SourceProperty.PropertyType.StripGeneric(), propertyExtract.Groups[3].Value, variables)); } } else if (propertyExtract.Groups[3].Value.StartsWith("@")) { return(AppDomain.CurrentDomain.GetAllTypes() .Where(o => property.SourceProperty.PropertyType.StripGeneric().IsAssignableFrom(o)) .Select(o => o.GetCustomAttribute <XmlTypeAttribute>()?.TypeName) .OfType <String>() .ToArray()); } else if (propertyExtract.Groups[3].Value.StartsWith("[")) { if (property.ClassifierValues?.Any() == true) { return(property.ClassifierValues); } else { return(property); } } else { return(retVal); } } else { return(retVal); } }
/// <summary> /// Edit the value /// </summary> public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value) { var msb = new ModelSerializationBinder(); if (provider != null) { var winService = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService)); if (typeof(IList).IsAssignableFrom(context.PropertyDescriptor.PropertyType)) // multi-select { var itemType = context.PropertyDescriptor.PropertyType.GetGenericArguments()[0]; var list = new ListView { View = View.Details, FullRowSelect = true, CheckBoxes = true, HeaderStyle = ColumnHeaderStyle.None, Sorting = SortOrder.Ascending }; list.Columns.Add("default"); var listValue = value as IEnumerable <ResourceTypeReferenceConfiguration>; // Get the types try { list.Items.AddRange(AppDomain.CurrentDomain.GetAllTypes() .Where(t => typeof(IdentifiedData).IsAssignableFrom(t) && t.GetCustomAttribute <XmlRootAttribute>() != null && !t.IsGenericTypeDefinition && !t.IsInterface && t.GetCustomAttribute <ObsoleteAttribute>() == null && !t.IsAbstract) .Select(o => { msb.BindToName(o, out var asm, out var type); return(new ListViewItem(type) { Checked = listValue?.Any(v => v.TypeXml == type) == true, Tag = new ResourceTypeReferenceConfiguration { TypeXml = type } }); }) .ToArray()); } catch (Exception e) { MessageBox.Show($"Error retrieving available types: {e.Message}"); return(value); } list.Columns[0].Width = -2; winService.DropDownControl(list); return(Activator.CreateInstance(context.PropertyDescriptor.PropertyType, list.CheckedItems.OfType <ListViewItem>().Select(o => o.Tag).OfType <ResourceTypeReferenceConfiguration>())); } else // Single select { var list = new ListBox(); list.Click += (o, e) => winService.CloseDropDown(); // Get the databases try { list.Items.AddRange(AppDomain.CurrentDomain.GetAllTypes() .Where(t => typeof(IdentifiedData).IsAssignableFrom(t) && t.GetCustomAttribute <XmlRootAttribute>() != null && !t.IsGenericTypeDefinition && !t.IsInterface && t.GetCustomAttribute <ObsoleteAttribute>() == null && !t.IsAbstract) .Select(o => { msb.BindToName(o, out var asm, out var type); return(new ResourceTypeReferenceConfiguration { TypeXml = type }); }) .ToArray()); } catch (Exception e) { MessageBox.Show($"Error retrieving available types : {e.Message}"); return(value); } winService.DropDownControl(list); if (list.SelectedItem != null) { return(list.SelectedItem as ResourceTypeReferenceConfiguration); } } } return(value); }
/// <summary> /// Start the daemon /// </summary> public bool Start() { this.Starting?.Invoke(this, EventArgs.Empty); // Pre-register types for serialization foreach (var itm in this.m_configuration.ResourceTypes) { if (itm.Type == typeof(Entity)) { throw new InvalidOperationException("Cannot bind MDM control to Entity or Act , only sub-classes"); } var rt = itm.Type; string typeName = $"{rt.Name}Master"; if (typeof(Entity).IsAssignableFrom(rt)) { rt = typeof(EntityMaster <>).MakeGenericType(rt); } else if (typeof(Act).IsAssignableFrom(rt)) { rt = typeof(ActMaster <>).MakeGenericType(rt); } ModelSerializationBinder.RegisterModelType(typeName, rt); } // Wait until application context is started ApplicationServiceContext.Current.Started += (o, e) => { if (this.m_matchingService == null) { this.m_traceSource.TraceWarning("The MDM Service should be using a record matching service"); } // Replace matching var mdmMatcher = this.m_serviceManager.CreateInjected <MdmRecordMatchingService>(); this.m_serviceManager.AddServiceProvider(mdmMatcher); var mdmMatchConfig = this.m_serviceManager.CreateInjected <MdmMatchConfigurationService>(); this.m_serviceManager.AddServiceProvider(mdmMatchConfig); if (this.m_matchingService != null) { this.m_serviceManager.RemoveServiceProvider(this.m_matchingService.GetType()); } if (this.m_matchConfigurationService != null) { this.m_serviceManager.RemoveServiceProvider(this.m_matchConfigurationService.GetType()); } foreach (var itm in this.m_configuration.ResourceTypes) { this.m_traceSource.TraceInfo("Adding MDM listener for {0}...", itm.Type.Name); MdmDataManagerFactory.RegisterDataManager(itm.Type); var idt = typeof(MdmResourceHandler <>).MakeGenericType(itm.Type); var ids = this.m_serviceManager.CreateInjected(idt) as IDisposable; this.m_listeners.Add(ids); this.m_serviceManager.AddServiceProvider(ids); this.m_serviceManager.AddServiceProvider(MdmDataManagerFactory.CreateMerger(itm.Type)); // Add job var jobType = typeof(MdmMatchJob <>).MakeGenericType(itm.Type); var job = this.m_serviceManager.CreateInjected(jobType) as IJob; this.m_jobManager?.AddJob(job, JobStartType.Never); } // Add an entity relationship and act relationship watcher to the persistence layer for after update // this will ensure that appropriate cleanup is performed on successful processing of data this.m_entityRelationshipService = ApplicationServiceContext.Current.GetService <IDataPersistenceService <EntityRelationship> >(); this.m_entityService = ApplicationServiceContext.Current.GetService <IDataPersistenceService <Entity> >(); ApplicationServiceContext.Current.GetService <IDataPersistenceService <Bundle> >().Inserted += RecheckBundleTrigger; ApplicationServiceContext.Current.GetService <IDataPersistenceService <Bundle> >().Updated += RecheckBundleTrigger; ApplicationServiceContext.Current.GetService <IDataPersistenceService <Bundle> >().Obsoleted += RecheckBundleTrigger; this.m_entityRelationshipService.Inserted += RecheckRelationshipTrigger; this.m_entityRelationshipService.Updated += RecheckRelationshipTrigger; this.m_entityRelationshipService.Obsoleted += RecheckRelationshipTrigger; // Add an MDM listener for subscriptions if (this.m_subscriptionExecutor != null) { m_subscriptionExecutor.Executing += MdmSubscriptionExecuting; m_subscriptionExecutor.Executed += MdmSubscriptionExecuted; } this.m_listeners.Add(new BundleResourceInterceptor(this.m_listeners)); // Slipstream the MdmEntityProvider //EntitySource.Current = new EntitySource(new MdmEntityProvider()); // HACK: Replace any freetext service with our own this.m_serviceManager.RemoveServiceProvider(typeof(IFreetextSearchService)); m_serviceManager.AddServiceProvider(new MdmFreetextSearchService()); }; this.Started?.Invoke(this, EventArgs.Empty); return(true); }