diff --git a/.claude/settings.local.json b/.claude/settings.local.json
index ddf5f63e..6cb145e9 100644
--- a/.claude/settings.local.json
+++ b/.claude/settings.local.json
@@ -5,7 +5,8 @@
"Bash(cat:*)",
"Bash(dotnet build:*)",
"Bash(dotnet test:*)",
- "Bash(dotnet pack:*)"
+ "Bash(dotnet pack:*)",
+ "Bash(find:*)"
]
}
}
diff --git a/.gitignore b/.gitignore
index 7a89f14f..f6d83a36 100644
--- a/.gitignore
+++ b/.gitignore
@@ -207,3 +207,6 @@ docs/tools/FSharp.Formatting.svclog
/XrmMockup/tools/Signature/delegate.snk
/tools/XrmContext/EnvInfo.config
/src/MetadataGen/XrmMockupTest.csproj
+
+# Claude temp files
+tmpclaude-*-cwd
diff --git a/Tools/Tools.csproj b/Tools/Tools.csproj
index d3e29fe4..ab766e06 100644
--- a/Tools/Tools.csproj
+++ b/Tools/Tools.csproj
@@ -64,7 +64,7 @@
True
- ..\packages\Microsoft.CrmSdk.Workflow.9.0.2.23\lib\net462\Microsoft.Xrm.Sdk.Workflow.dll
+ ..\packages\Microsoft.CrmSdk.Workflow.9.0.2.60\lib\net462\Microsoft.Xrm.Sdk.Workflow.dll
True
diff --git a/Tools/packages.config b/Tools/packages.config
index c42adad0..135f44eb 100644
--- a/Tools/packages.config
+++ b/Tools/packages.config
@@ -5,7 +5,7 @@
-
+
diff --git a/XrmMockup.slnx b/XrmMockup.slnx
index 60946edd..d88d98c5 100644
--- a/XrmMockup.slnx
+++ b/XrmMockup.slnx
@@ -25,6 +25,8 @@
+
+
diff --git a/scripts/Pack-Local.ps1 b/scripts/Pack-Local.ps1
index 7ebaa44d..4f336378 100644
--- a/scripts/Pack-Local.ps1
+++ b/scripts/Pack-Local.ps1
@@ -1,3 +1,7 @@
+param(
+ [string]$Output = "./nupkg"
+)
+
# Local pack script for XrmMockup packages
# Sets versions from changelogs and creates NuGet packages locally
@@ -13,5 +17,5 @@
dotnet build --configuration Release
# Pack specific projects (not the entire solution to avoid legacy project errors)
-dotnet pack ./src/XrmMockup365/XrmMockup365.csproj --configuration Release --no-build --output ./nupkg
-dotnet pack ./src/MetadataGen/MetadataGenerator.Tool/MetadataGenerator.Tool.csproj --configuration Release --no-build --output ./nupkg
+dotnet pack ./src/XrmMockup365/XrmMockup365.csproj --configuration Release --no-build --output $Output
+dotnet pack ./src/MetadataGen/MetadataGenerator.Tool/MetadataGenerator.Tool.csproj --configuration Release --no-build --output $Output
diff --git a/src/MetadataGen/MetadataGenerator.Tool.Tests/MetadataGenerator.Tool.Tests.csproj b/src/MetadataGen/MetadataGenerator.Tool.Tests/MetadataGenerator.Tool.Tests.csproj
index a35d53e9..18317d0e 100644
--- a/src/MetadataGen/MetadataGenerator.Tool.Tests/MetadataGenerator.Tool.Tests.csproj
+++ b/src/MetadataGen/MetadataGenerator.Tool.Tests/MetadataGenerator.Tool.Tests.csproj
@@ -20,7 +20,7 @@
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/src/XrmMockup365/Core.cs b/src/XrmMockup365/Core.cs
index 8fe3d26d..a5b8df16 100644
--- a/src/XrmMockup365/Core.cs
+++ b/src/XrmMockup365/Core.cs
@@ -1,10 +1,10 @@
-using DG.Tools.XrmMockup.Database;
+using DG.Tools.XrmMockup.Database;
using DG.Tools.XrmMockup.Internal;
using DG.Tools.XrmMockup.Serialization;
+using DG.Tools.XrmMockup.Online;
using XrmPluginCore.Enums;
using Microsoft.Crm.Sdk.Messages;
using Microsoft.Xrm.Sdk;
-using Microsoft.Xrm.Sdk.Client;
using Microsoft.Xrm.Sdk.Messages;
using Microsoft.Xrm.Sdk.Metadata;
using Microsoft.Xrm.Sdk.Organization;
@@ -59,7 +59,7 @@ internal class Core : IXrmMockupExtension
private XrmDb db;
private Dictionary snapshots;
private Dictionary entityTypeMap = new Dictionary();
- private OrganizationServiceProxy OnlineProxy;
+ private IOnlineDataService OnlineDataService;
private int baseCurrencyPrecision;
private FormulaFieldEvaluator FormulaFieldEvaluator { get; set; }
private List systemAttributeNames;
@@ -79,7 +79,7 @@ public Core(XrmMockupSettings Settings, MetadataSkeleton metadata, List
SecurityRoles = SecurityRoles,
BaseCurrency = metadata.BaseOrganization.GetAttributeValue("basecurrencyid"),
BaseCurrencyPrecision = metadata.BaseOrganization.GetAttributeValue("pricingdecimalprecision"),
- OnlineProxy = null,
+ OnlineDataService = null,
EntityTypeMap = new Dictionary()
};
@@ -99,7 +99,7 @@ public Core(XrmMockupSettings Settings, StaticMetadataCache staticCache)
SecurityRoles = staticCache.SecurityRoles,
BaseCurrency = staticCache.BaseCurrency,
BaseCurrencyPrecision = staticCache.BaseCurrencyPrecision,
- OnlineProxy = staticCache.OnlineProxy,
+ OnlineDataService = staticCache.OnlineDataService,
EntityTypeMap = staticCache.EntityTypeMap
};
@@ -116,10 +116,9 @@ private void InitializeCore(CoreInitializationData initData)
metadata = initData.Metadata;
BaseCurrency = initData.BaseCurrency;
baseCurrencyPrecision = initData.BaseCurrencyPrecision;
- OnlineProxy = initData.OnlineProxy;
+ OnlineDataService = initData.OnlineDataService;
+ db = new XrmDb(initData.Metadata.EntityMetadata, initData.OnlineDataService);
entityTypeMap = initData.EntityTypeMap;
-
- db = new XrmDb(initData.Metadata.EntityMetadata, initData.OnlineProxy);
EnsureFileAttachmentMetadata();
FileBlockStore = new FileBlockStore();
snapshots = new Dictionary();
@@ -185,7 +184,7 @@ public static StaticMetadataCache BuildStaticMetadataCache(XrmMockupSettings set
var baseCurrency = metadata.BaseOrganization.GetAttributeValue("basecurrencyid");
var baseCurrencyPrecision = metadata.BaseOrganization.GetAttributeValue("pricingdecimalprecision");
- var onlineProxy = BuildOnlineProxy(settings);
+ var onlineDataService = BuildOnlineDataService(settings);
var entityTypeMap = new Dictionary();
// Build entity type map for proxy types if enabled
@@ -194,29 +193,28 @@ public static StaticMetadataCache BuildStaticMetadataCache(XrmMockupSettings set
BuildEntityTypeMap(settings, entityTypeMap);
}
- // Note: IPluginMetadata is handled per-instance in the Core constructor
+ // Note: IPluginMetadata is handled per-instance in the Core constructor
// to avoid modifying the shared cache
- return new StaticMetadataCache(metadata, workflows, securityRoles, entityTypeMap,
- baseCurrency, baseCurrencyPrecision, onlineProxy);
+ return new StaticMetadataCache(metadata, workflows, securityRoles, entityTypeMap,
+ baseCurrency, baseCurrencyPrecision, onlineDataService);
}
- private static OrganizationServiceProxy BuildOnlineProxy(XrmMockupSettings settings)
+ private static IOnlineDataService BuildOnlineDataService(XrmMockupSettings settings)
{
+#if DATAVERSE_SERVICE_CLIENT
+ // Allow injection for testing
+ if (settings.OnlineDataServiceFactory != null)
+ {
+ return settings.OnlineDataServiceFactory();
+ }
+
if (settings.OnlineEnvironment.HasValue)
{
var env = settings.OnlineEnvironment.Value;
- var orgHelper = new OrganizationHelper(
- new Uri(env.uri),
- env.providerType,
- env.username,
- env.password,
- env.domain);
- var proxy = orgHelper.GetServiceProxy();
- if (settings.EnableProxyTypes == true)
- proxy.EnableProxyTypes();
- return proxy;
+ return new OnlineDataService(env.Url);
}
+#endif
return null;
}
@@ -427,25 +425,6 @@ internal void EnableProxyTypes(Assembly assembly)
}
}
- private OrganizationServiceProxy GetOnlineProxy()
- {
- if (OnlineProxy == null && settings.OnlineEnvironment.HasValue)
- {
- var env = settings.OnlineEnvironment.Value;
- var orgHelper = new OrganizationHelper(
- new Uri(env.uri),
- env.providerType,
- env.username,
- env.password,
- env.domain);
- this.OnlineProxy = orgHelper.GetServiceProxy();
- if (settings.EnableProxyTypes == true)
- OnlineProxy.EnableProxyTypes();
- }
-
- return OnlineProxy;
- }
-
internal IOrganizationService GetWorkflowService()
{
return ServiceFactory.CreateOrganizationService(null,
@@ -1121,6 +1100,14 @@ internal void PopulateWith(Entity[] entities)
}
}
+ ///
+ /// Prefills the local database with data from the online service based on the query.
+ ///
+ internal void PrefillDBWithOnlineData(QueryExpression query)
+ {
+ db.PrefillDBWithOnlineData(query);
+ }
+
internal Dictionary> GetPrivilege(Guid principleId)
{
return security.GetPrincipalPrivilege(principleId);
@@ -1356,7 +1343,7 @@ internal void ResetEnvironment()
workflowManager.ResetWorkflows(settings.IncludeAllWorkflows);
pluginManager.ResetPlugins();
- this.db = new XrmDb(metadata.EntityMetadata, GetOnlineProxy());
+ this.db = new XrmDb(metadata.EntityMetadata, OnlineDataService);
EnsureFileAttachmentMetadata();
this.RequestHandlers = GetRequestHandlers(db);
InitializeDB();
diff --git a/src/XrmMockup365/Database/DeviceIdManager.cs b/src/XrmMockup365/Database/DeviceIdManager.cs
deleted file mode 100644
index d3e340ce..00000000
--- a/src/XrmMockup365/Database/DeviceIdManager.cs
+++ /dev/null
@@ -1,871 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-
-namespace DG.Tools.XrmMockup
-{
- // ====================================================================
- //
- // This file is part of the Microsoft Dynamics CRM SDK code samples.
- //
- // Copyright (C) Microsoft Corporation. All rights reserved.
- //
- // This source code is intended only as a supplement to Microsoft
- // Development Tools and/or on-line documentation. See these other
- // materials for detailed information regarding Microsoft code samples.
- //
- // THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
- // KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
- // IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
- // PARTICULAR PURPOSE.
- //
- // ====================================================================
- //
- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Diagnostics.CodeAnalysis;
- using System.Globalization;
- using System.IO;
- using System.Net;
- using System.Runtime.Serialization;
- using System.Security.Cryptography;
- using System.ServiceModel.Description;
- using System.Text;
- using System.Xml;
- using System.Xml.Serialization;
-
- namespace Microsoft.Crm.Services.Utility {
- ///
- /// Management utility for the Device Id
- ///
- public static class DeviceIdManager {
- #region Fields
- private static readonly Random RandomInstance = new Random();
-
- public const int MaxDeviceNameLength = 24;
- public const int MaxDevicePasswordLength = 24;
- #endregion
-
- #region Constructor
- static DeviceIdManager() {
- PersistToFile = true;
- }
- #endregion
-
- #region Properties
- ///
- /// Indicates whether the registered device credentials should be persisted to the database
- ///
- public static bool PersistToFile { get; set; }
-
- ///
- /// Indicates that the credentials should be persisted to the disk if registration fails with DeviceAlreadyExists.
- ///
- ///
- /// If the device already exists, there is a possibility that the credentials are the same as the current credentials that
- /// are being registered. This is especially true in automated environments where the same credentials are used continually (to avoid
- /// registering spurious device credentials.
- ///
- public static bool PersistIfDeviceAlreadyExists { get; set; }
- #endregion
-
- #region Methods
- ///
- /// Loads the device credentials (if they exist).
- ///
- ///
- public static ClientCredentials LoadOrRegisterDevice() {
- return LoadOrRegisterDevice(null);
- }
-
- ///
- /// Loads the device credentials (if they exist).
- ///
- /// Device name that should be registered
- /// Device password that should be registered
- public static ClientCredentials LoadOrRegisterDevice(string deviceName, string devicePassword) {
- return LoadOrRegisterDevice(null, deviceName, devicePassword);
- }
-
- ///
- /// Loads the device credentials (if they exist).
- ///
- /// URL for the current token issuer
- ///
- /// The issuerUri can be retrieved from the IServiceConfiguration interface's CurrentIssuer property.
- ///
- public static ClientCredentials LoadOrRegisterDevice(Uri issuerUri) {
- return LoadOrRegisterDevice(issuerUri, null, null);
- }
-
- ///
- /// Loads the device credentials (if they exist).
- ///
- /// URL for the current token issuer
- /// Device name that should be registered
- /// Device password that should be registered
- ///
- /// The issuerUri can be retrieved from the IServiceConfiguration interface's CurrentIssuer property.
- ///
- public static ClientCredentials LoadOrRegisterDevice(Uri issuerUri, string deviceName, string devicePassword) {
- ClientCredentials credentials = LoadDeviceCredentials(issuerUri);
- if (null == credentials) {
- credentials = RegisterDevice(Guid.NewGuid(), issuerUri, deviceName, devicePassword);
- }
-
- return credentials;
- }
-
- ///
- /// Registers the given device with Microsoft account with a random application ID
- ///
- /// ClientCredentials that were registered
- public static ClientCredentials RegisterDevice() {
- return RegisterDevice(Guid.NewGuid());
- }
-
- ///
- /// Registers the given device with Microsoft account
- ///
- /// ID for the application
- /// ClientCredentials that were registered
- public static ClientCredentials RegisterDevice(Guid applicationId) {
- return RegisterDevice(applicationId, (Uri)null);
- }
-
- ///
- /// Registers the given device with Microsoft account
- ///
- /// ID for the application
- /// URL for the current token issuer
- /// ClientCredentials that were registered
- ///
- /// The issuerUri can be retrieved from the IServiceConfiguration interface's CurrentIssuer property.
- ///
- public static ClientCredentials RegisterDevice(Guid applicationId, Uri issuerUri) {
- return RegisterDevice(applicationId, issuerUri, null, null);
- }
-
- ///
- /// Registers the given device with Microsoft account
- ///
- /// ID for the application
- /// Device name that should be registered
- /// Device password that should be registered
- /// ClientCredentials that were registered
- public static ClientCredentials RegisterDevice(Guid applicationId, string deviceName, string devicePassword) {
- return RegisterDevice(applicationId, (Uri)null, deviceName, devicePassword);
- }
-
- ///
- /// Registers the given device with Microsoft account
- ///
- /// ID for the application
- /// URL for the current token issuer
- /// Device name that should be registered
- /// Device password that should be registered
- /// ClientCredentials that were registered
- ///
- /// The issuerUri can be retrieved from the IServiceConfiguration interface's CurrentIssuer property.
- ///
- public static ClientCredentials RegisterDevice(Guid applicationId, Uri issuerUri, string deviceName, string devicePassword) {
- if (string.IsNullOrEmpty(deviceName) && !PersistToFile) {
- throw new ArgumentNullException("deviceName", "If PersistToFile is false, then deviceName must be specified.");
- } else if (string.IsNullOrEmpty(deviceName) != string.IsNullOrEmpty(devicePassword)) {
- throw new ArgumentNullException("deviceName", "Either deviceName/devicePassword should both be specified or they should be null.");
- }
-
- LiveDevice device = GenerateDevice(deviceName, devicePassword);
- return RegisterDevice(applicationId, issuerUri, device);
- }
-
- ///
- /// Loads the device's credentials from the file system
- ///
- /// Device Credentials (if set) or null
- public static ClientCredentials LoadDeviceCredentials() {
- return LoadDeviceCredentials(null);
- }
-
- ///
- /// Loads the device's credentials from the file system
- ///
- /// URL for the current token issuer
- /// Device Credentials (if set) or null
- ///
- /// The issuerUri can be retrieved from the IServiceConfiguration interface's CurrentIssuer property.
- ///
- public static ClientCredentials LoadDeviceCredentials(Uri issuerUri) {
- //If the credentials should not be persisted to a file, then they won't be present on the disk.
- if (!PersistToFile) {
- return null;
- }
-
- EnvironmentConfiguration environment = DiscoverEnvironmentInternal(issuerUri);
-
- LiveDevice device = ReadExistingDevice(environment);
- if (null == device || null == device.User) {
- return null;
- }
-
- return device.User.ToClientCredentials();
- }
-
- ///
- /// Discovers the Microsoft account environment based on the Token Issuer
- ///
- public static string DiscoverEnvironment(Uri issuerUri) {
- return DiscoverEnvironmentInternal(issuerUri).Environment;
- }
- #endregion
-
- #region Private Methods
- private static EnvironmentConfiguration DiscoverEnvironmentInternal(Uri issuerUri) {
- if (null == issuerUri) {
- return new EnvironmentConfiguration(EnvironmentType.LiveDeviceID, "login.live.com", null);
- }
-
- Dictionary searchList = new Dictionary();
- searchList.Add(EnvironmentType.LiveDeviceID, "login.live");
- searchList.Add(EnvironmentType.OrgDeviceID, "login.microsoftonline");
-
- foreach (KeyValuePair searchPair in searchList) {
- if (issuerUri.Host.Length > searchPair.Value.Length &&
- issuerUri.Host.StartsWith(searchPair.Value, StringComparison.OrdinalIgnoreCase)) {
- string environment = issuerUri.Host.Substring(searchPair.Value.Length);
-
- //Parse out the environment
- if ('-' == environment[0]) {
- int separatorIndex = environment.IndexOf('.', 1);
- if (-1 != separatorIndex) {
- environment = environment.Substring(1, separatorIndex - 1);
- } else {
- environment = null;
- }
- } else {
- environment = null;
- }
-
- return new EnvironmentConfiguration(searchPair.Key, issuerUri.Host, environment);
- }
- }
-
- //In all other cases the environment is either not applicable or it is a production system
- return new EnvironmentConfiguration(EnvironmentType.LiveDeviceID, issuerUri.Host, null);
- }
-
- private static void Serialize(Stream stream, T value) {
- XmlSerializer serializer = new XmlSerializer(typeof(T), string.Empty);
-
- XmlSerializerNamespaces xmlNamespaces = new XmlSerializerNamespaces();
- xmlNamespaces.Add(string.Empty, string.Empty);
-
- serializer.Serialize(stream, value, xmlNamespaces);
- }
-
- private static T Deserialize(string operationName, Stream stream) {
- //Read the XML into memory so that the data can be used in an exception if necessary
- using (StreamReader reader = new StreamReader(stream)) {
- return Deserialize(operationName, reader.ReadToEnd());
- }
- }
-
- private static T Deserialize(string operationName, string xml) {
- //Attempt to deserialize the data. If deserialization fails, include the XML in the exception that is thrown for further
- //investigation
- using (StringReader reader = new StringReader(xml)) {
- try {
- XmlSerializer serializer = new XmlSerializer(typeof(T), string.Empty);
- return (T)serializer.Deserialize(reader);
- } catch (InvalidOperationException ex) {
- throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture,
- "Unable to Deserialize XML (Operation = {0}):{1}{2}", operationName, Environment.NewLine, xml), ex);
- }
- }
- }
-
- private static FileInfo GetDeviceFile(EnvironmentConfiguration environment) {
- return new FileInfo(string.Format(CultureInfo.InvariantCulture, LiveIdConstants.FileNameFormat,
- environment.Type,
- string.IsNullOrEmpty(environment.Environment) ? null : "-" + environment.Environment.ToUpperInvariant()));
- }
-
- private static ClientCredentials RegisterDevice(Guid applicationId, Uri issuerUri, LiveDevice device) {
- EnvironmentConfiguration environment = DiscoverEnvironmentInternal(issuerUri);
-
- DeviceRegistrationRequest request = new DeviceRegistrationRequest(applicationId, device);
-
- string url = string.Format(CultureInfo.InvariantCulture, LiveIdConstants.RegistrationEndpointUriFormat,
- environment.HostName);
-
- DeviceRegistrationResponse response = ExecuteRegistrationRequest(url, request);
- if (!response.IsSuccess) {
- bool throwException = true;
- if (DeviceRegistrationErrorCode.DeviceAlreadyExists == response.Error.RegistrationErrorCode) {
- if (!PersistToFile) {
- //If the file is not persisted, the registration will always occur (since the credentials are not
- //persisted to the disk. However, the credentials may already exist. To avoid an exception being continually
- //processed by the calling user, DeviceAlreadyExists will be ignored if the credentials are not persisted to the disk.
- return device.User.ToClientCredentials();
- } else if (PersistIfDeviceAlreadyExists) {
- // This flag indicates that the
- throwException = false;
- }
- }
-
- if (throwException) {
- throw new DeviceRegistrationFailedException(response.Error.RegistrationErrorCode, response.ErrorSubCode);
- }
- }
-
- if (PersistToFile || PersistIfDeviceAlreadyExists) {
- WriteDevice(environment, device);
- }
-
- return device.User.ToClientCredentials();
- }
-
- private static LiveDevice GenerateDevice(string deviceName, string devicePassword) {
- // If the deviceName hasn't been specified, it should be generated using random characters.
- DeviceUserName userNameCredentials;
- if (string.IsNullOrEmpty(deviceName)) {
- userNameCredentials = GenerateDeviceUserName();
- } else {
- userNameCredentials = new DeviceUserName() { DeviceName = deviceName, DecryptedPassword = devicePassword };
- }
-
- return new LiveDevice() { User = userNameCredentials, Version = 1 };
- }
-
- private static LiveDevice ReadExistingDevice(EnvironmentConfiguration environment) {
- //Retrieve the file info
- FileInfo file = GetDeviceFile(environment);
- if (!file.Exists) {
- return null;
- }
-
- using (FileStream stream = file.Open(FileMode.Open, FileAccess.Read, FileShare.Read)) {
- return Deserialize("Loading Device Credentials from Disk", stream);
- }
- }
-
- private static void WriteDevice(EnvironmentConfiguration environment, LiveDevice device) {
- FileInfo file = GetDeviceFile(environment);
- if (!file.Directory.Exists) {
- file.Directory.Create();
- }
-
- using (FileStream stream = file.Open(FileMode.CreateNew, FileAccess.Write, FileShare.None)) {
- Serialize(stream, device);
- }
- }
-
- private static DeviceRegistrationResponse ExecuteRegistrationRequest(string url, DeviceRegistrationRequest registrationRequest) {
- //Create the request that will submit the request to the server
- WebRequest request = WebRequest.Create(url);
- request.ContentType = "application/soap+xml; charset=UTF-8";
- request.Method = "POST";
- request.Timeout = 180000;
-
- //Write the envelope to the RequestStream
- using (Stream stream = request.GetRequestStream()) {
- Serialize(stream, registrationRequest);
- }
-
- // Read the response into an XmlDocument and return that doc
- try {
- using (WebResponse response = request.GetResponse()) {
- using (Stream stream = response.GetResponseStream()) {
- return Deserialize("Deserializing Registration Response", stream);
- }
- }
- } catch (WebException ex) {
- System.Diagnostics.Trace.TraceError("Microsoft account Device Registration Failed (HTTP Code: {0}): {1}",
- ex.Status, ex.Message);
-
- if (null != ex.Response) {
- using (Stream stream = ex.Response.GetResponseStream()) {
- return Deserialize("Deserializing Failed Registration Response", stream);
- }
- }
-
- throw;
- }
- }
-
- private static DeviceUserName GenerateDeviceUserName() {
- DeviceUserName userName = new DeviceUserName();
- userName.DeviceName = GenerateRandomString(LiveIdConstants.ValidDeviceNameCharacters, MaxDeviceNameLength);
- userName.DecryptedPassword = GenerateRandomString(LiveIdConstants.ValidDevicePasswordCharacters, MaxDevicePasswordLength);
-
- return userName;
- }
-
- private static string GenerateRandomString(string characterSet, int count) {
- //Create an array of the characters that will hold the final list of random characters
- char[] value = new char[count];
-
- //Convert the character set to an array that can be randomly accessed
- char[] set = characterSet.ToCharArray();
-
- lock (RandomInstance) {
- //Populate the array with random characters from the character set
- for (int i = 0; i < count; i++) {
- value[i] = set[RandomInstance.Next(0, set.Length)];
- }
- }
-
- return new string(value);
- }
- #endregion
-
- #region Private Classes
- private enum EnvironmentType {
- LiveDeviceID,
- OrgDeviceID
- }
-
- private sealed class EnvironmentConfiguration {
- public EnvironmentConfiguration(EnvironmentType type, string hostName, string environment) {
- if (string.IsNullOrWhiteSpace(hostName)) {
- throw new ArgumentNullException("hostName");
- }
-
- this.Type = type;
- this.HostName = hostName;
- this.Environment = environment;
- }
-
- #region Properties
- public EnvironmentType Type { get; private set; }
-
- public string HostName { get; private set; }
-
- public string Environment { get; private set; }
- #endregion
- }
-
- private static class LiveIdConstants {
- public const string RegistrationEndpointUriFormat = @"https://{0}/ppsecure/DeviceAddCredential.srf";
-
- public static readonly string FileNameFormat = Path.Combine(
- Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "LiveDeviceID"),
- "{0}{1}.xml");
-
- public const string ValidDeviceNameCharacters = "0123456789abcdefghijklmnopqrstuvqxyz";
-
- //Consists of the list of characters specified in the documentation
- public const string ValidDevicePasswordCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^*()-_=+;,./?`~";
- }
- #endregion
- }
-
- #region Public Classes & Enums
- ///
- /// Indicates an error during registration
- ///
- public enum DeviceRegistrationErrorCode {
- ///
- /// Unspecified or Unknown Error occurred
- ///
- Unknown = 0,
-
- ///
- /// Interface Disabled
- ///
- InterfaceDisabled = 1,
-
- ///
- /// Invalid Request Format
- ///
- InvalidRequestFormat = 3,
-
- ///
- /// Unknown Client Version
- ///
- UnknownClientVersion = 4,
-
- ///
- /// Blank Password
- ///
- BlankPassword = 6,
-
- ///
- /// Missing Device User Name or Password
- ///
- MissingDeviceUserNameOrPassword = 7,
-
- ///
- /// Invalid Parameter Syntax
- ///
- InvalidParameterSyntax = 8,
-
- ///
- /// Invalid Characters are used in the device credentials.
- ///
- InvalidCharactersInCredentials = 9,
-
- ///
- /// Internal Error
- ///
- InternalError = 11,
-
- ///
- /// Device Already Exists
- ///
- DeviceAlreadyExists = 13
- }
-
- ///
- /// Indicates that Device Registration failed
- ///
- [Serializable]
- public sealed class DeviceRegistrationFailedException : Exception {
- ///
- /// Construct an instance of the DeviceRegistrationFailedException class
- ///
- public DeviceRegistrationFailedException()
- : base() {
- }
-
- ///
- /// Construct an instance of the DeviceRegistrationFailedException class
- ///
- /// Message to pass
- public DeviceRegistrationFailedException(string message)
- : base(message) {
- }
-
- ///
- /// Construct an instance of the DeviceRegistrationFailedException class
- ///
- /// Message to pass
- /// Exception to include
- public DeviceRegistrationFailedException(string message, Exception innerException)
- : base(message, innerException) {
- }
-
- ///
- /// Construct an instance of the DeviceRegistrationFailedException class
- ///
- /// Error code that occurred
- /// Subcode that occurred
- public DeviceRegistrationFailedException(DeviceRegistrationErrorCode code, string subCode)
- : this(code, subCode, null) {
- }
-
- ///
- /// Construct an instance of the DeviceRegistrationFailedException class
- ///
- /// Error code that occurred
- /// Subcode that occurred
- /// Inner exception
- public DeviceRegistrationFailedException(DeviceRegistrationErrorCode code, string subCode, Exception innerException)
- : base(string.Concat(code.ToString(), ": ", subCode), innerException) {
- this.RegistrationErrorCode = code;
- }
-
- ///
- /// Construct an instance of the DeviceRegistrationFailedException class
- ///
- ///
- ///
- private DeviceRegistrationFailedException(SerializationInfo si, StreamingContext sc)
- : base(si, sc) {
- }
-
- #region Properties
- ///
- /// Error code that occurred during registration
- ///
- public DeviceRegistrationErrorCode RegistrationErrorCode { get; private set; }
- #endregion
-
- #region Methods
- public override void GetObjectData(SerializationInfo info, StreamingContext context) {
- base.GetObjectData(info, context);
- }
- #endregion
- }
-
- #region Serialization Classes
- #region DeviceRegistrationRequest Class
- [EditorBrowsable(EditorBrowsableState.Never)]
- [XmlRoot("DeviceAddRequest")]
- public sealed class DeviceRegistrationRequest {
- #region Constructors
- public DeviceRegistrationRequest() {
- }
-
- public DeviceRegistrationRequest(Guid applicationId, LiveDevice device)
- : this() {
- if (null == device) {
- throw new ArgumentNullException("device");
- }
-
- this.ClientInfo = new DeviceRegistrationClientInfo() { ApplicationId = applicationId, Version = "1.0" };
- this.Authentication = new DeviceRegistrationAuthentication() {
- MemberName = device.User.DeviceId,
- Password = device.User.DecryptedPassword
- };
- }
- #endregion
-
- #region Properties
- [XmlElement("ClientInfo")]
- public DeviceRegistrationClientInfo ClientInfo { get; set; }
-
- [XmlElement("Authentication")]
- public DeviceRegistrationAuthentication Authentication { get; set; }
- #endregion
- }
- #endregion
-
- #region DeviceRegistrationClientInfo Class
- [EditorBrowsable(EditorBrowsableState.Never)]
- [XmlRoot("ClientInfo")]
- public sealed class DeviceRegistrationClientInfo {
- #region Properties
- [XmlAttribute("name")]
- public Guid ApplicationId { get; set; }
-
- [XmlAttribute("version")]
- public string Version { get; set; }
- #endregion
- }
- #endregion
-
- #region DeviceRegistrationAuthentication Class
- [EditorBrowsable(EditorBrowsableState.Never)]
- [XmlRoot("Authentication")]
- public sealed class DeviceRegistrationAuthentication {
- #region Properties
- [XmlElement("Membername")]
- public string MemberName { get; set; }
-
- [XmlElement("Password")]
- public string Password { get; set; }
- #endregion
- }
- #endregion
-
- #region DeviceRegistrationResponse Class
- [EditorBrowsable(EditorBrowsableState.Never)]
- [XmlRoot("DeviceAddResponse")]
- public sealed class DeviceRegistrationResponse {
- #region Properties
- [XmlElement("success")]
- public bool IsSuccess { get; set; }
-
- [XmlElement("puid")]
- public string Puid { get; set; }
-
- [XmlElement("Error")]
- public DeviceRegistrationResponseError Error { get; set; }
-
- [XmlElement("ErrorSubcode")]
- public string ErrorSubCode { get; set; }
- #endregion
- }
- #endregion
-
- #region DeviceRegistrationResponse Class
- [EditorBrowsable(EditorBrowsableState.Never)]
- [XmlRoot("Error")]
- public sealed class DeviceRegistrationResponseError {
- private string _code;
-
- #region Properties
- [XmlAttribute("Code")]
- public string Code {
- get {
- return this._code;
- }
-
- set {
- this._code = value;
-
- //Parse the error code
- if (!string.IsNullOrEmpty(value)) {
- //Parse the error code
- if (value.StartsWith("dc", StringComparison.Ordinal)) {
- int code;
- if (int.TryParse(value.Substring(2), NumberStyles.Integer,
- CultureInfo.InvariantCulture, out code) &&
- Enum.IsDefined(typeof(DeviceRegistrationErrorCode), code)) {
- this.RegistrationErrorCode = (DeviceRegistrationErrorCode)Enum.ToObject(
- typeof(DeviceRegistrationErrorCode), code);
- }
- }
- }
- }
- }
-
- [XmlIgnore]
- public DeviceRegistrationErrorCode RegistrationErrorCode { get; private set; }
- #endregion
- }
- #endregion
-
- #region LiveDevice Class
- [EditorBrowsable(EditorBrowsableState.Never)]
- [XmlRoot("Data")]
- public sealed class LiveDevice {
- #region Properties
- [XmlAttribute("version")]
- public int Version { get; set; }
-
- [XmlElement("User")]
- public DeviceUserName User { get; set; }
-
- [SuppressMessage("Microsoft.Design", "CA1059:MembersShouldNotExposeCertainConcreteTypes", MessageId = "System.Xml.XmlNode", Justification = "This is required for proper XML Serialization")]
- [XmlElement("Token")]
- public XmlNode Token { get; set; }
-
- [XmlElement("Expiry")]
- public string Expiry { get; set; }
-
- [XmlElement("ClockSkew")]
- public string ClockSkew { get; set; }
- #endregion
- }
- #endregion
-
- #region DeviceUserName Class
- [EditorBrowsable(EditorBrowsableState.Never)]
- public sealed class DeviceUserName {
- private string _encryptedPassword;
- private string _decryptedPassword;
- private bool _encryptedValueIsUpdated;
-
- #region Constants
- private const string UserNamePrefix = "11";
- #endregion
-
- #region Constructors
- public DeviceUserName() {
- this.UserNameType = "Logical";
- }
- #endregion
-
- #region Properties
- [XmlAttribute("username")]
- public string DeviceName { get; set; }
-
- [XmlAttribute("type")]
- public string UserNameType { get; set; }
-
- [XmlElement("Pwd")]
- public string EncryptedPassword {
- get {
- this.ThrowIfNoEncryption();
-
- if (!this._encryptedValueIsUpdated) {
- this._encryptedPassword = this.Encrypt(this._decryptedPassword);
- this._encryptedValueIsUpdated = true;
- }
-
- return this._encryptedPassword;
- }
-
- set {
- this.ThrowIfNoEncryption();
- this.UpdateCredentials(value, null);
- }
- }
-
- public string DeviceId {
- get {
- return UserNamePrefix + DeviceName;
- }
- }
-
- [XmlIgnore]
- public string DecryptedPassword {
- get {
- return this._decryptedPassword;
- }
-
- set {
- this.UpdateCredentials(null, value);
- }
- }
-
- private bool IsEncryptionEnabled {
- get {
- //If the object is not going to be persisted to a file, then the value does not need to be encrypted. This is extra
- //overhead and will not function in partial trust.
- return DeviceIdManager.PersistToFile;
- }
- }
- #endregion
-
- #region Methods
- public ClientCredentials ToClientCredentials() {
- ClientCredentials credentials = new ClientCredentials();
- credentials.UserName.UserName = this.DeviceId;
- credentials.UserName.Password = this.DecryptedPassword;
-
- return credentials;
- }
-
- private void ThrowIfNoEncryption() {
- if (!this.IsEncryptionEnabled) {
- throw new NotSupportedException("Not supported when DeviceIdManager.UseEncryptionApis is false.");
- }
- }
-
- private void UpdateCredentials(string encryptedValue, string decryptedValue) {
- bool isValueUpdated = false;
- if (string.IsNullOrEmpty(encryptedValue) && string.IsNullOrEmpty(decryptedValue)) {
- isValueUpdated = true;
- } else if (string.IsNullOrEmpty(encryptedValue)) {
- if (this.IsEncryptionEnabled) {
- encryptedValue = this.Encrypt(decryptedValue);
- isValueUpdated = true;
- } else {
- encryptedValue = null;
- isValueUpdated = false;
- }
- } else {
- this.ThrowIfNoEncryption();
-
- decryptedValue = this.Decrypt(encryptedValue);
- isValueUpdated = true;
- }
-
- this._encryptedPassword = encryptedValue;
- this._decryptedPassword = decryptedValue;
- this._encryptedValueIsUpdated = isValueUpdated;
- }
-
- private string Encrypt(string value) {
- if (string.IsNullOrEmpty(value)) {
- return value;
- }
-
- byte[] encryptedBytes = ProtectedData.Protect(Encoding.UTF8.GetBytes(value), null, DataProtectionScope.CurrentUser);
- return Convert.ToBase64String(encryptedBytes);
- }
-
- private string Decrypt(string value) {
- if (string.IsNullOrEmpty(value)) {
- return value;
- }
-
- byte[] decryptedBytes = ProtectedData.Unprotect(Convert.FromBase64String(value), null, DataProtectionScope.CurrentUser);
- if (null == decryptedBytes || 0 == decryptedBytes.Length) {
- return null;
- }
-
- return Encoding.UTF8.GetString(decryptedBytes, 0, decryptedBytes.Length);
- }
- #endregion
- }
- #endregion
- #endregion
- #endregion
- }
- //
-}
diff --git a/src/XrmMockup365/Database/OrganizationHelper.cs b/src/XrmMockup365/Database/OrganizationHelper.cs
deleted file mode 100644
index 3d3c9d96..00000000
--- a/src/XrmMockup365/Database/OrganizationHelper.cs
+++ /dev/null
@@ -1,99 +0,0 @@
-using Microsoft.Xrm.Sdk;
-using Microsoft.Xrm.Sdk.Client;
-using System;
-using System.Collections.Generic;
-using System.Configuration;
-using System.Text;
-
-namespace DG.Tools.XrmMockup {
- public class OrganizationHelper {
-
- private Uri _uri;
- private AuthenticationProviderType _ap;
- private string _userName;
- private string _password;
- private string _domain;
-
- public OrganizationHelper() {
- _uri = new Uri(GetConnectionString("CrmUri") ?? "");
- _ap = (AuthenticationProviderType)Enum.Parse(typeof(AuthenticationProviderType),
- GetConnectionString("CrmAp"));
- _userName = GetConnectionString("CrmUsr");
- _password = GetConnectionString("CrmPwd");
- _domain = GetConnectionString("CrmDmn");
- }
-
- public OrganizationHelper(Uri uri, AuthenticationProviderType ap, string userName, string password, string domain = null) {
- _uri = uri;
- _ap = ap;
- _userName = userName;
- _password = password;
- _domain = domain;
- }
-
- private string GetConnectionString(string name) {
- return ConfigurationManager.ConnectionStrings[name]?.ConnectionString;
- }
-
- public OrganizationServiceProxy GetServiceProxy() {
- var proxy = GetServiceProxyInternal();
-#if DATAVERSE_SERVICE_CLIENT
- proxy.ServiceConfiguration.CurrentServiceEndpoint.EndpointBehaviors.Add(new ProxyTypesBehavior());
-#else
- proxy.ServiceConfiguration.CurrentServiceEndpoint.Behaviors.Add(new ProxyTypesBehavior());
-#endif
- proxy.Timeout = new TimeSpan(1, 0, 0);
- return proxy;
- }
-
- private OrganizationServiceProxy GetServiceProxyInternal() {
- var management = ServiceConfigurationFactory.CreateManagement(_uri);
- var ac = management.Authenticate(GetCredentials(management, _ap));
-
- switch (_ap) {
- case AuthenticationProviderType.ActiveDirectory:
- return new OrganizationServiceProxy(management, ac.ClientCredentials);
- default:
- return new OrganizationServiceProxy(management, ac.SecurityTokenResponse);
- }
- }
-
- private AuthenticationCredentials GetCredentials(IServiceManagement service, AuthenticationProviderType endpointType) {
- AuthenticationCredentials authCredentials = new AuthenticationCredentials();
-
- switch (endpointType) {
- case AuthenticationProviderType.ActiveDirectory:
- authCredentials.ClientCredentials.Windows.ClientCredential =
- new System.Net.NetworkCredential(_userName, _password, _domain);
- break;
- case AuthenticationProviderType.LiveId:
- authCredentials.ClientCredentials.UserName.UserName = _userName;
- authCredentials.ClientCredentials.UserName.Password = _password;
- authCredentials.SupportingCredentials = new AuthenticationCredentials();
- authCredentials.SupportingCredentials.ClientCredentials =
- Microsoft.Crm.Services.Utility.DeviceIdManager.LoadOrRegisterDevice();
- break;
- default: // For Federated and OnlineFederated environments.
- authCredentials.ClientCredentials.UserName.UserName = _userName;
- authCredentials.ClientCredentials.UserName.Password = _password;
- // For OnlineFederated single-sign on, you could just use current UserPrincipalName instead of passing user name and password.
- // authCredentials.UserPrincipalName = UserPrincipal.Current.UserPrincipalName; // Windows Kerberos
-
- // The service is configured for User Id authentication, but the user might provide Microsoft
- // account credentials. If so, the supporting credentials must contain the device credentials.
- if (endpointType == AuthenticationProviderType.OnlineFederation) {
- IdentityProvider provider = service.GetIdentityProvider(authCredentials.ClientCredentials.UserName.UserName);
- if (provider != null && provider.IdentityProviderType == IdentityProviderType.LiveId) {
- authCredentials.SupportingCredentials = new AuthenticationCredentials();
- authCredentials.SupportingCredentials.ClientCredentials =
- Microsoft.Crm.Services.Utility.DeviceIdManager.LoadOrRegisterDevice();
- }
- }
-
- break;
- }
-
- return authCredentials;
- }
- }
-}
diff --git a/src/XrmMockup365/Database/XrmDb.cs b/src/XrmMockup365/Database/XrmDb.cs
index 30055182..546d18fa 100644
--- a/src/XrmMockup365/Database/XrmDb.cs
+++ b/src/XrmMockup365/Database/XrmDb.cs
@@ -1,28 +1,28 @@
-using Microsoft.Xrm.Sdk;
+using Microsoft.Xrm.Sdk;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using Microsoft.Xrm.Sdk.Metadata;
-using Microsoft.Xrm.Sdk.Client;
using Microsoft.Xrm.Sdk.Query;
using System.Threading;
using DG.Tools.XrmMockup.Serialization;
using DG.Tools.XrmMockup.Internal;
+using DG.Tools.XrmMockup.Online;
namespace DG.Tools.XrmMockup.Database {
internal class XrmDb {
// Using ConcurrentDictionary for thread-safe table access in parallel test scenarios
private ConcurrentDictionary TableDict = new ConcurrentDictionary();
- private Dictionary EntityMetadata;
- private OrganizationServiceProxy OnlineProxy;
+ private readonly Dictionary EntityMetadata;
+ private readonly IOnlineDataService OnlineDataService;
private int sequence;
- public XrmDb(Dictionary entityMetadata, OrganizationServiceProxy onlineProxy) {
+ public XrmDb(Dictionary entityMetadata, IOnlineDataService onlineDataService) {
this.EntityMetadata = entityMetadata;
- this.OnlineProxy = onlineProxy;
+ this.OnlineDataService = onlineDataService;
sequence = 0;
}
@@ -37,7 +37,7 @@ public DbTable this[string tableName] {
}
}
- public void Add(Entity xrmEntity, bool withReferenceChecks = true)
+ public void Add(Entity xrmEntity, bool withReferenceChecks = true)
{
int nextSequence = Interlocked.Increment(ref sequence);
var dbEntity = ToDbRow(xrmEntity,nextSequence, withReferenceChecks);
@@ -113,9 +113,9 @@ internal void RegisterEntityMetadata(EntityMetadata entityMetadata)
internal void PrefillDBWithOnlineData(QueryExpression queryExpr)
{
- if (OnlineProxy != null)
+ if (OnlineDataService != null)
{
- var onlineEntities = OnlineProxy.RetrieveMultiple(queryExpr).Entities;
+ var onlineEntities = OnlineDataService.RetrieveMultiple(queryExpr).Entities;
foreach (var onlineEntity in onlineEntities)
{
if (this[onlineEntity.LogicalName][onlineEntity.Id] == null)
@@ -133,13 +133,15 @@ internal DbRow GetDbRow(EntityReference reference, bool withReferenceCheck = tru
if (reference?.Id != Guid.Empty)
{
currentDbRow = this[reference.LogicalName][reference.Id];
- if (currentDbRow == null && OnlineProxy != null)
+ if (currentDbRow == null && OnlineDataService != null)
{
if (!withReferenceCheck)
+ {
currentDbRow = DbRow.MakeDBRowRef(reference, this);
+ }
else
{
- var onlineEntity = OnlineProxy.Retrieve(reference.LogicalName, reference.Id, new ColumnSet(true));
+ var onlineEntity = OnlineDataService.Retrieve(reference.LogicalName, reference.Id, new ColumnSet(true));
Add(onlineEntity, withReferenceCheck);
currentDbRow = this[reference.LogicalName][reference.Id];
}
@@ -165,7 +167,7 @@ internal DbRow GetDbRow(EntityReference reference, bool withReferenceCheck = tru
// No identification given for the entity, throw error
else
{
- throw new FaultException($"Missing a form of identification for the desired record in order to retrieve it.");
+ throw new FaultException("Missing a form of identification for the desired record in order to retrieve it.");
}
return currentDbRow;
@@ -234,8 +236,7 @@ internal bool TryGetDbRow(EntityReference reference, out DbRow dbRow)
internal DbRow GetDbRowOrNull(EntityReference reference)
{
- DbRow row;
- if (TryGetDbRow(reference, out row))
+ if (TryGetDbRow(reference, out DbRow row))
{
return row;
}
@@ -247,8 +248,7 @@ internal DbRow GetDbRowOrNull(EntityReference reference)
internal Entity GetEntityOrNull(EntityReference reference)
{
- DbRow row;
- if (TryGetDbRow(reference, out row))
+ if (TryGetDbRow(reference, out DbRow row))
{
return row.ToEntity();
}
@@ -263,7 +263,7 @@ internal Entity GetEntityOrNull(EntityReference reference)
public XrmDb Clone()
{
var clonedTables = this.TableDict.ToDictionary(x => x.Key, x => x.Value.Clone());
- var clonedDB = new XrmDb(this.EntityMetadata, this.OnlineProxy)
+ var clonedDB = new XrmDb(this.EntityMetadata, this.OnlineDataService)
{
TableDict = new ConcurrentDictionary(clonedTables)
};
@@ -281,7 +281,7 @@ public DbDTO ToSerializableDTO()
public static XrmDb RestoreSerializableDTO(XrmDb current, DbDTO model)
{
var clonedTables = model.Tables.ToDictionary(x => x.Key, x => DbTable.RestoreSerializableDTO(new DbTable(current.EntityMetadata[x.Key]), x.Value));
- var clonedDB = new XrmDb(current.EntityMetadata, current.OnlineProxy)
+ var clonedDB = new XrmDb(current.EntityMetadata, current.OnlineDataService)
{
TableDict = new ConcurrentDictionary(clonedTables)
};
diff --git a/src/XrmMockup365/FormulaFieldEvaluator.cs b/src/XrmMockup365/FormulaFieldEvaluator.cs
index a6781a8d..11fb08dd 100644
--- a/src/XrmMockup365/FormulaFieldEvaluator.cs
+++ b/src/XrmMockup365/FormulaFieldEvaluator.cs
@@ -7,13 +7,14 @@
using System.Globalization;
using System.Threading;
using System.Threading.Tasks;
+using PowerFxDataverseConnection = Microsoft.PowerFx.Dataverse.DataverseConnection;
namespace DG.Tools.XrmMockup
{
internal class FormulaFieldEvaluator
{
private readonly IOrganizationService _organizationService;
- private readonly DataverseConnection _dataverseConnection;
+ private readonly PowerFxDataverseConnection _dataverseConnection;
public FormulaFieldEvaluator(IOrganizationServiceFactory serviceFactory)
{
diff --git a/src/XrmMockup365/Internal/CoreInitializationData.cs b/src/XrmMockup365/Internal/CoreInitializationData.cs
index 4be6b99e..df78bbb9 100644
--- a/src/XrmMockup365/Internal/CoreInitializationData.cs
+++ b/src/XrmMockup365/Internal/CoreInitializationData.cs
@@ -1,7 +1,7 @@
-using Microsoft.Xrm.Sdk;
-using Microsoft.Xrm.Sdk.Client;
+using Microsoft.Xrm.Sdk;
using System;
using System.Collections.Generic;
+using DG.Tools.XrmMockup.Online;
namespace DG.Tools.XrmMockup.Internal
{
@@ -16,7 +16,7 @@ internal class CoreInitializationData
public List SecurityRoles { get; set; }
public EntityReference BaseCurrency { get; set; }
public int BaseCurrencyPrecision { get; set; }
- public OrganizationServiceProxy OnlineProxy { get; set; }
+ public IOnlineDataService OnlineDataService { get; set; }
public Dictionary EntityTypeMap { get; set; }
}
}
diff --git a/src/XrmMockup365/Online/IOnlineDataService.cs b/src/XrmMockup365/Online/IOnlineDataService.cs
new file mode 100644
index 00000000..21c31a11
--- /dev/null
+++ b/src/XrmMockup365/Online/IOnlineDataService.cs
@@ -0,0 +1,27 @@
+using System;
+using Microsoft.Xrm.Sdk;
+using Microsoft.Xrm.Sdk.Query;
+
+namespace DG.Tools.XrmMockup.Online
+{
+ ///
+ /// Interface for fetching data from an online Dataverse environment.
+ ///
+ internal interface IOnlineDataService : IDisposable
+ {
+ ///
+ /// Retrieves a single entity by ID.
+ ///
+ Entity Retrieve(string entityName, Guid id, ColumnSet columnSet);
+
+ ///
+ /// Retrieves multiple entities using a QueryExpression.
+ ///
+ EntityCollection RetrieveMultiple(QueryExpression query);
+
+ ///
+ /// Gets whether the service is connected.
+ ///
+ bool IsConnected { get; }
+ }
+}
diff --git a/src/XrmMockup365/Online/OnlineDataService.cs b/src/XrmMockup365/Online/OnlineDataService.cs
new file mode 100644
index 00000000..e25b1927
--- /dev/null
+++ b/src/XrmMockup365/Online/OnlineDataService.cs
@@ -0,0 +1,63 @@
+#if DATAVERSE_SERVICE_CLIENT
+using System;
+using DataverseConnection;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.PowerPlatform.Dataverse.Client;
+using Microsoft.Xrm.Sdk;
+using Microsoft.Xrm.Sdk.Query;
+
+namespace DG.Tools.XrmMockup.Online
+{
+ ///
+ /// In-process implementation for connecting to a live Dataverse environment.
+ /// Used for testing with real data when OnlineEnvironment is configured.
+ /// Uses Azure DefaultAzureCredential for authentication (supports managed identity,
+ /// Visual Studio credentials, Azure CLI, etc.).
+ ///
+ internal class OnlineDataService : IOnlineDataService
+ {
+ private readonly ServiceProvider _serviceProvider;
+ private readonly ServiceClient _serviceClient;
+ private bool _disposed;
+
+ public OnlineDataService(string environmentUrl)
+ {
+ if (string.IsNullOrWhiteSpace(environmentUrl))
+ throw new ArgumentNullException(nameof(environmentUrl));
+
+ // Use DataverseConnection for authentication
+ var services = new ServiceCollection();
+ services.AddDataverse(options => options.DataverseUrl = environmentUrl);
+ _serviceProvider = services.BuildServiceProvider();
+ _serviceClient = _serviceProvider.GetRequiredService();
+ }
+
+ public bool IsConnected => _serviceClient?.IsReady == true;
+
+ public Entity Retrieve(string entityName, Guid id, ColumnSet columnSet)
+ {
+ if (entityName == null) throw new ArgumentNullException(nameof(entityName));
+ if (columnSet == null) throw new ArgumentNullException(nameof(columnSet));
+
+ return _serviceClient.Retrieve(entityName, id, columnSet);
+ }
+
+ public EntityCollection RetrieveMultiple(QueryExpression query)
+ {
+ if (query == null) throw new ArgumentNullException(nameof(query));
+
+ return _serviceClient.RetrieveMultiple(query);
+ }
+
+ public void Dispose()
+ {
+ if (_disposed)
+ return;
+
+ _disposed = true;
+ _serviceClient?.Dispose();
+ _serviceProvider?.Dispose();
+ }
+ }
+}
+#endif
diff --git a/src/XrmMockup365/StaticMetadataCache.cs b/src/XrmMockup365/StaticMetadataCache.cs
index cb630bd2..7a081a6c 100644
--- a/src/XrmMockup365/StaticMetadataCache.cs
+++ b/src/XrmMockup365/StaticMetadataCache.cs
@@ -1,7 +1,7 @@
-using Microsoft.Xrm.Sdk;
-using Microsoft.Xrm.Sdk.Client;
+using Microsoft.Xrm.Sdk;
using System;
using System.Collections.Generic;
+using DG.Tools.XrmMockup.Online;
namespace DG.Tools.XrmMockup
{
@@ -13,11 +13,11 @@ public class StaticMetadataCache
public Dictionary EntityTypeMap { get; }
public EntityReference BaseCurrency { get; }
public int BaseCurrencyPrecision { get; }
- public OrganizationServiceProxy OnlineProxy { get; }
+ internal IOnlineDataService OnlineDataService { get; }
- public StaticMetadataCache(MetadataSkeleton metadata, List workflows, List securityRoles,
- Dictionary entityTypeMap, EntityReference baseCurrency, int baseCurrencyPrecision,
- OrganizationServiceProxy onlineProxy)
+ internal StaticMetadataCache(MetadataSkeleton metadata, List workflows, List securityRoles,
+ Dictionary entityTypeMap, EntityReference baseCurrency, int baseCurrencyPrecision,
+ IOnlineDataService onlineDataService)
{
Metadata = metadata;
Workflows = workflows;
@@ -25,7 +25,7 @@ public StaticMetadataCache(MetadataSkeleton metadata, List workflows, Li
EntityTypeMap = entityTypeMap;
BaseCurrency = baseCurrency;
BaseCurrencyPrecision = baseCurrencyPrecision;
- OnlineProxy = onlineProxy;
+ OnlineDataService = onlineDataService;
}
}
}
diff --git a/src/XrmMockup365/XrmMockup.cs b/src/XrmMockup365/XrmMockup.cs
index aefe32c4..f7970bb8 100644
--- a/src/XrmMockup365/XrmMockup.cs
+++ b/src/XrmMockup365/XrmMockup.cs
@@ -73,8 +73,8 @@ public static XrmMockup365 GetInstance(XrmMockup365 xrmMockup, XrmMockupSettings
{
// Create a new cache entry using the existing instance's data
cache = new StaticMetadataCache(
- xrmMockup.Metadata,
- xrmMockup.Workflows,
+ xrmMockup.Metadata,
+ xrmMockup.Workflows,
xrmMockup.SecurityRoles,
new Dictionary(), // Will be rebuilt if needed
xrmMockup.BaseCurrency,
diff --git a/src/XrmMockup365/XrmMockup365.csproj b/src/XrmMockup365/XrmMockup365.csproj
index a1cddfb6..dd774f77 100644
--- a/src/XrmMockup365/XrmMockup365.csproj
+++ b/src/XrmMockup365/XrmMockup365.csproj
@@ -5,7 +5,7 @@
portable
XrmMockup365
- 0.0.0
+ 1.18.0-rc.6
@@ -62,17 +62,23 @@
-
+
-
+
+
+
+
+ <_Parameter1>XrmMockup365Test
+
+
-
+
\ No newline at end of file
diff --git a/src/XrmMockup365/XrmMockupBase.cs b/src/XrmMockup365/XrmMockupBase.cs
index 4551d936..4202d1d5 100644
--- a/src/XrmMockup365/XrmMockupBase.cs
+++ b/src/XrmMockup365/XrmMockupBase.cs
@@ -285,6 +285,15 @@ public void PopulateWith(params Entity[] entities) {
Core.PopulateWith(entities);
}
+ ///
+ /// Prefills the local database with data from the online service based on the query.
+ /// Only works when OnlineDataServiceFactory or OnlineEnvironment is configured.
+ ///
+ /// The query to execute against the online service.
+ public void PrefillDBWithOnlineData(QueryExpression query) {
+ Core.PrefillDBWithOnlineData(query);
+ }
+
///
/// Create a new user with a specific businessunit
///
diff --git a/src/XrmMockup365/XrmMockupSettings.cs b/src/XrmMockup365/XrmMockupSettings.cs
index 69f4d67e..78a61e6b 100644
--- a/src/XrmMockup365/XrmMockupSettings.cs
+++ b/src/XrmMockup365/XrmMockupSettings.cs
@@ -1,8 +1,10 @@
-using Microsoft.Xrm.Sdk.Client;
using System;
using System.Collections.Generic;
using Microsoft.Xrm.Sdk.Organization;
using System.Reflection;
+#if DATAVERSE_SERVICE_CLIENT
+using DG.Tools.XrmMockup.Online;
+#endif
namespace DG.Tools.XrmMockup
{
@@ -43,10 +45,14 @@ public class XrmMockupSettings
///
public IEnumerable ExceptionFreeRequests { get; set; }
+#if DATAVERSE_SERVICE_CLIENT
///
- /// Environment settings for connection to an online environment for live debugging.
+ /// Settings for connecting to an online Dataverse environment for live debugging.
+ /// Uses Azure DefaultAzureCredential for authentication (supports managed identity,
+ /// Visual Studio credentials, Azure CLI, etc.).
///
public Env? OnlineEnvironment { get; set; }
+#endif
///
/// Overwrites the path to the directory containing metadata files. Default is '../../Metadata/'.
@@ -98,15 +104,27 @@ public class XrmMockupSettings
/// Default is true.
///
public bool EnablePowerFxFields { get; set; } = true;
- }
+#if DATAVERSE_SERVICE_CLIENT
+ ///
+ /// Optional factory for creating IOnlineDataService. For testing purposes.
+ /// If set, this takes precedence over OnlineEnvironment.
+ ///
+ internal Func OnlineDataServiceFactory { get; set; }
+#endif
+ }
+#if DATAVERSE_SERVICE_CLIENT
+ ///
+ /// Settings for connecting to an online Dataverse environment.
+ ///
public struct Env
{
- public string uri;
- public AuthenticationProviderType providerType;
- public string username;
- public string password;
- public string domain;
+ ///
+ /// URL of the Dataverse environment (e.g., https://org.crm.dynamics.com).
+ /// Uses Azure DefaultAzureCredential for authentication.
+ ///
+ public string Url;
}
-}
\ No newline at end of file
+#endif
+}
diff --git a/tests/TestPluginAssembly365/TestPluginAssembly365.csproj b/tests/TestPluginAssembly365/TestPluginAssembly365.csproj
index 8d8f57c1..f100b07d 100644
--- a/tests/TestPluginAssembly365/TestPluginAssembly365.csproj
+++ b/tests/TestPluginAssembly365/TestPluginAssembly365.csproj
@@ -27,7 +27,7 @@
-
+
diff --git a/tests/XrmMockup365Test/Online/MockOnlineDataService.cs b/tests/XrmMockup365Test/Online/MockOnlineDataService.cs
new file mode 100644
index 00000000..29523ce9
--- /dev/null
+++ b/tests/XrmMockup365Test/Online/MockOnlineDataService.cs
@@ -0,0 +1,123 @@
+#if DATAVERSE_SERVICE_CLIENT
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using DG.Tools.XrmMockup.Online;
+using Microsoft.Xrm.Sdk;
+using Microsoft.Xrm.Sdk.Query;
+
+namespace DG.XrmMockupTest.Online
+{
+ ///
+ /// Mock implementation of IOnlineDataService for unit testing XrmMockup's online data integration.
+ /// Tracks all calls made to verify correct behavior.
+ ///
+ internal class MockOnlineDataService : IOnlineDataService
+ {
+ private readonly Dictionary<(string LogicalName, Guid Id), Entity> _entities = new Dictionary<(string, Guid), Entity>();
+
+ ///
+ /// List of all Retrieve calls made to this service.
+ ///
+ public List<(string EntityName, Guid Id, ColumnSet ColumnSet)> RetrieveCalls { get; } = new List<(string, Guid, ColumnSet)>();
+
+ ///
+ /// List of all RetrieveMultiple calls made to this service.
+ ///
+ public List RetrieveMultipleCalls { get; } = new List();
+
+ ///
+ /// Configures the mock to return the specified entity when retrieved.
+ ///
+ public void SetupEntity(Entity entity)
+ {
+ if (entity == null) throw new ArgumentNullException(nameof(entity));
+ _entities[(entity.LogicalName, entity.Id)] = entity;
+ }
+
+ ///
+ /// Configures the mock to return multiple entities.
+ ///
+ public void SetupEntities(IEnumerable entities)
+ {
+ foreach (var entity in entities)
+ {
+ SetupEntity(entity);
+ }
+ }
+
+ ///
+ /// Clears all configured entities.
+ ///
+ public void ClearEntities()
+ {
+ _entities.Clear();
+ }
+
+ ///
+ /// Clears all recorded calls.
+ ///
+ public void ClearCalls()
+ {
+ RetrieveCalls.Clear();
+ RetrieveMultipleCalls.Clear();
+ }
+
+ public Entity Retrieve(string entityName, Guid id, ColumnSet columnSet)
+ {
+ RetrieveCalls.Add((entityName, id, columnSet));
+
+ if (_entities.TryGetValue((entityName, id), out var entity))
+ {
+ return CloneEntity(entity, columnSet);
+ }
+
+ throw new Exception($"Entity {entityName} with id {id} not found in mock online data service");
+ }
+
+ public EntityCollection RetrieveMultiple(QueryExpression query)
+ {
+ RetrieveMultipleCalls.Add(query);
+
+ var matches = _entities.Values
+ .Where(e => e.LogicalName == query.EntityName)
+ .Select(e => CloneEntity(e, query.ColumnSet))
+ .ToList();
+
+ return new EntityCollection(matches) { EntityName = query.EntityName };
+ }
+
+ public bool IsConnected => true;
+
+ public void Dispose()
+ {
+ // Nothing to dispose
+ }
+
+ private static Entity CloneEntity(Entity entity, ColumnSet columnSet)
+ {
+ var clone = new Entity(entity.LogicalName, entity.Id);
+
+ if (columnSet.AllColumns)
+ {
+ foreach (var attr in entity.Attributes)
+ {
+ clone[attr.Key] = attr.Value;
+ }
+ }
+ else
+ {
+ foreach (var column in columnSet.Columns)
+ {
+ if (entity.Contains(column))
+ {
+ clone[column] = entity[column];
+ }
+ }
+ }
+
+ return clone;
+ }
+ }
+}
+#endif
diff --git a/tests/XrmMockup365Test/Online/OnlineDataServiceUnitTests.cs b/tests/XrmMockup365Test/Online/OnlineDataServiceUnitTests.cs
new file mode 100644
index 00000000..fa783e0a
--- /dev/null
+++ b/tests/XrmMockup365Test/Online/OnlineDataServiceUnitTests.cs
@@ -0,0 +1,233 @@
+#if DATAVERSE_SERVICE_CLIENT
+using System;
+using System.Linq;
+using DG.Tools.XrmMockup;
+using DG.XrmFramework.BusinessDomain.ServiceContext;
+using Microsoft.Xrm.Sdk;
+using Microsoft.Xrm.Sdk.Query;
+using Xunit;
+
+namespace DG.XrmMockupTest.Online
+{
+ ///
+ /// Unit tests verifying XrmMockup's integration with IOnlineDataService.
+ /// Uses mock IOnlineDataService to verify correct behavior without real proxy.
+ /// Only available on net8.0 (DATAVERSE_SERVICE_CLIENT).
+ ///
+ public class OnlineDataServiceUnitTests : IClassFixture
+ {
+ private readonly XrmMockupFixture _fixture;
+
+ public OnlineDataServiceUnitTests(XrmMockupFixture fixture)
+ {
+ _fixture = fixture;
+ }
+
+ private (XrmMockup365 crm, MockOnlineDataService mockService) CreateMockupWithOnlineService()
+ {
+ var mockService = new MockOnlineDataService();
+ var settings = new XrmMockupSettings
+ {
+ BasePluginTypes = _fixture.Settings.BasePluginTypes,
+ BaseCustomApiTypes = _fixture.Settings.BaseCustomApiTypes,
+ CodeActivityInstanceTypes = _fixture.Settings.CodeActivityInstanceTypes,
+ EnableProxyTypes = _fixture.Settings.EnableProxyTypes,
+ IncludeAllWorkflows = _fixture.Settings.IncludeAllWorkflows,
+ ExceptionFreeRequests = _fixture.Settings.ExceptionFreeRequests,
+ MetadataDirectoryPath = _fixture.Settings.MetadataDirectoryPath,
+ IPluginMetadata = _fixture.Settings.IPluginMetadata,
+ OnlineDataServiceFactory = () => mockService
+ };
+
+ var crm = XrmMockup365.GetInstance(settings);
+ return (crm, mockService);
+ }
+
+ [Fact]
+ public void GetDbRow_EntityNotInDb_CallsOnlineServiceRetrieve()
+ {
+ // Arrange
+ var (crm, mockService) = CreateMockupWithOnlineService();
+ var service = crm.GetAdminService();
+
+ var onlineAccountId = Guid.NewGuid();
+ var onlineAccount = new Entity(Account.EntityLogicalName, onlineAccountId)
+ {
+ ["name"] = "Online Account",
+ ["accountnumber"] = "ONLINE-001"
+ };
+ mockService.SetupEntity(onlineAccount);
+
+ // Act - Try to retrieve an account that doesn't exist locally
+ // This should trigger a call to the online service
+ var retrieved = service.Retrieve(Account.EntityLogicalName, onlineAccountId, new ColumnSet(true));
+
+ // Assert
+ Assert.Single(mockService.RetrieveCalls);
+ Assert.Equal(Account.EntityLogicalName, mockService.RetrieveCalls[0].EntityName);
+ Assert.Equal(onlineAccountId, mockService.RetrieveCalls[0].Id);
+ Assert.Equal("Online Account", retrieved.GetAttributeValue("name"));
+ }
+
+ [Fact]
+ public void GetDbRow_EntityInDb_DoesNotCallOnlineService()
+ {
+ // Arrange
+ var (crm, mockService) = CreateMockupWithOnlineService();
+ var service = crm.GetAdminService();
+
+ // Create account locally
+ var localAccount = new Account { Name = "Local Account" };
+ localAccount.Id = service.Create(localAccount);
+
+ mockService.ClearCalls(); // Clear any calls from Create
+
+ // Act - Retrieve the locally created account
+ var retrieved = service.Retrieve(Account.EntityLogicalName, localAccount.Id, new ColumnSet(true));
+
+ // Assert - No calls to online service since entity exists locally
+ Assert.Empty(mockService.RetrieveCalls);
+ Assert.Equal("Local Account", retrieved.GetAttributeValue("name"));
+ }
+
+ [Fact]
+ public void GetDbRow_RetrievedEntityAddedToLocalDb()
+ {
+ // Arrange
+ var (crm, mockService) = CreateMockupWithOnlineService();
+ var service = crm.GetAdminService();
+
+ var onlineAccountId = Guid.NewGuid();
+ var onlineAccount = new Entity(Account.EntityLogicalName, onlineAccountId)
+ {
+ ["name"] = "Online Account",
+ ["accountnumber"] = "ONLINE-002"
+ };
+ mockService.SetupEntity(onlineAccount);
+
+ // Act - First retrieve fetches from online
+ service.Retrieve(Account.EntityLogicalName, onlineAccountId, new ColumnSet(true));
+ mockService.ClearCalls();
+
+ // Second retrieve should use local cache
+ var retrieved = service.Retrieve(Account.EntityLogicalName, onlineAccountId, new ColumnSet(true));
+
+ // Assert - Second retrieve should not call online service
+ Assert.Empty(mockService.RetrieveCalls);
+ Assert.Equal("Online Account", retrieved.GetAttributeValue("name"));
+ }
+
+ [Fact]
+ public void PrefillDBWithOnlineData_CallsRetrieveMultiple()
+ {
+ // Arrange
+ var (crm, mockService) = CreateMockupWithOnlineService();
+
+ var onlineAccount1 = new Entity(Account.EntityLogicalName, Guid.NewGuid())
+ {
+ ["name"] = "Online Account 1"
+ };
+ var onlineAccount2 = new Entity(Account.EntityLogicalName, Guid.NewGuid())
+ {
+ ["name"] = "Online Account 2"
+ };
+ mockService.SetupEntities(new[] { onlineAccount1, onlineAccount2 });
+
+ var query = new QueryExpression(Account.EntityLogicalName)
+ {
+ ColumnSet = new ColumnSet(true)
+ };
+
+ // Act
+ crm.PrefillDBWithOnlineData(query);
+
+ // Assert
+ Assert.Single(mockService.RetrieveMultipleCalls);
+ Assert.Equal(Account.EntityLogicalName, mockService.RetrieveMultipleCalls[0].EntityName);
+ }
+
+ [Fact]
+ public void PrefillDBWithOnlineData_AddsEntitiesToLocalDb()
+ {
+ // Arrange
+ var (crm, mockService) = CreateMockupWithOnlineService();
+ var service = crm.GetAdminService();
+
+ var onlineAccount1 = new Entity(Account.EntityLogicalName, Guid.NewGuid())
+ {
+ ["name"] = "Online Account 1"
+ };
+ var onlineAccount2 = new Entity(Account.EntityLogicalName, Guid.NewGuid())
+ {
+ ["name"] = "Online Account 2"
+ };
+ mockService.SetupEntities(new[] { onlineAccount1, onlineAccount2 });
+
+ var query = new QueryExpression(Account.EntityLogicalName)
+ {
+ ColumnSet = new ColumnSet(true)
+ };
+
+ // Act
+ crm.PrefillDBWithOnlineData(query);
+ mockService.ClearCalls();
+
+ // Retrieve entities - should come from local DB, not online
+ var retrieved1 = service.Retrieve(Account.EntityLogicalName, onlineAccount1.Id, new ColumnSet(true));
+ var retrieved2 = service.Retrieve(Account.EntityLogicalName, onlineAccount2.Id, new ColumnSet(true));
+
+ // Assert - No online calls since entities are now local
+ Assert.Empty(mockService.RetrieveCalls);
+ Assert.Equal("Online Account 1", retrieved1.GetAttributeValue("name"));
+ Assert.Equal("Online Account 2", retrieved2.GetAttributeValue("name"));
+ }
+
+ [Fact]
+ public void PrefillDBWithOnlineData_SkipsExistingEntities()
+ {
+ // Arrange
+ var (crm, mockService) = CreateMockupWithOnlineService();
+ var service = crm.GetAdminService();
+
+ // Create a local account first
+ var localAccount = new Account { Name = "Local Account" };
+ localAccount.Id = service.Create(localAccount);
+
+ // Setup online service to return an account with the same ID but different name
+ var onlineAccount = new Entity(Account.EntityLogicalName, localAccount.Id)
+ {
+ ["name"] = "Online Account (should not overwrite)"
+ };
+ mockService.SetupEntity(onlineAccount);
+
+ var query = new QueryExpression(Account.EntityLogicalName)
+ {
+ ColumnSet = new ColumnSet(true)
+ };
+
+ // Act
+ crm.PrefillDBWithOnlineData(query);
+
+ // Retrieve the entity
+ var retrieved = service.Retrieve(Account.EntityLogicalName, localAccount.Id, new ColumnSet(true));
+
+ // Assert - Local entity should NOT be overwritten
+ Assert.Equal("Local Account", retrieved.GetAttributeValue("name"));
+ }
+
+ [Fact]
+ public void NoOnlineService_EntityNotFound_ThrowsException()
+ {
+ // Arrange - Create mockup without online service
+ var crm = XrmMockup365.GetInstance(_fixture.Settings);
+ var service = crm.GetAdminService();
+
+ var nonExistentId = Guid.NewGuid();
+
+ // Act & Assert
+ Assert.Throws(() =>
+ service.Retrieve(Account.EntityLogicalName, nonExistentId, new ColumnSet(true)));
+ }
+ }
+}
+#endif
diff --git a/tests/XrmMockup365Test/TestSettings.cs b/tests/XrmMockup365Test/TestSettings.cs
index 01e15fa5..e3f513e1 100644
--- a/tests/XrmMockup365Test/TestSettings.cs
+++ b/tests/XrmMockup365Test/TestSettings.cs
@@ -1,6 +1,5 @@
using System;
using Microsoft.Xrm.Sdk;
-using Microsoft.Xrm.Sdk.Query;
using DG.XrmFramework.BusinessDomain.ServiceContext;
using Xunit;
using Xunit.Sdk;
@@ -32,34 +31,5 @@ public void TestNoExceptionRequest()
orgAdminUIService.Execute(req);
}
}
-
- [Fact(Skip = "Using real data")]
- public void TestRealDataRetrieve()
- {
- var acc = new Account(new Guid("9155CF31-BA6A-E611-80E0-C4346BAC0E68"))
- {
- Name = "babuasd"
- };
- orgRealDataService.Update(acc);
- var retrieved = orgRealDataService.Retrieve(Account.EntityLogicalName, acc.Id, new ColumnSet(true)).ToEntity();
- Assert.Equal(acc.Name, retrieved.Name);
- Assert.Equal("12321123312", retrieved.AccountNumber);
- }
-
- [Fact(Skip = "Using real data")]
- public void TestRealDataRetrieveMultiple()
- {
- var query = new QueryExpression(Account.EntityLogicalName)
- {
- ColumnSet = new ColumnSet(true),
- PageInfo = new PagingInfo()
- {
- Count = 1000,
- PageNumber = 1
- }
- };
- var res = orgRealDataService.RetrieveMultiple(query);
- Assert.True(res.Entities.Count > 0);
- }
}
}
diff --git a/tests/XrmMockup365Test/UnitTestBase.cs b/tests/XrmMockup365Test/UnitTestBase.cs
index 3f7aeaab..00ce43a7 100644
--- a/tests/XrmMockup365Test/UnitTestBase.cs
+++ b/tests/XrmMockup365Test/UnitTestBase.cs
@@ -18,7 +18,6 @@ public abstract class ServiceWrapper
public IOrganizationServiceAsync2 orgAdminUIService { get; protected set; }
public IOrganizationServiceAsync2 orgAdminService { get; protected set; }
public IOrganizationServiceAsync2 orgGodService { get; protected set; }
- public IOrganizationServiceAsync2 orgRealDataService { get; protected set; }
public IOrganizationServiceAsync2 testUser1Service { get; protected set; }
public IOrganizationServiceAsync2 testUser2Service { get; protected set; }
@@ -28,7 +27,6 @@ public abstract class ServiceWrapper
public IOrganizationService orgAdminUIService { get; protected set; }
public IOrganizationService orgAdminService { get; protected set; }
public IOrganizationService orgGodService { get; protected set; }
- public IOrganizationService orgRealDataService { get; protected set; }
public IOrganizationService testUser1Service { get; protected set; }
public IOrganizationService testUser2Service { get; protected set; }
@@ -97,8 +95,6 @@ public UnitTestBase(XrmMockupFixture fixture)
orgAdminUIService = crm.GetAdminService(new MockupServiceSettings(true, false, MockupServiceSettings.Role.UI));
orgGodService = crm.GetAdminService(new MockupServiceSettings(false, true, MockupServiceSettings.Role.SDK));
orgAdminService = crm.GetAdminService();
- // Skip real data service - it causes online connection issues and isn't needed for most tests
- orgRealDataService = null;
//create an admin user to run our impersonating user plugins as
var adminUser = new Entity("systemuser") { Id = Guid.Parse("3b961284-cd7a-4fa3-af7e-89802e88dd5c") };
diff --git a/tests/XrmMockup365Test/UnitTestBaseNoProxyTypes.cs b/tests/XrmMockup365Test/UnitTestBaseNoProxyTypes.cs
index dcd58783..3a704e64 100644
--- a/tests/XrmMockup365Test/UnitTestBaseNoProxyTypes.cs
+++ b/tests/XrmMockup365Test/UnitTestBaseNoProxyTypes.cs
@@ -7,12 +7,9 @@ namespace DG.XrmMockupTest
{
public class UnitTestBaseNoProxyTypes : IClassFixture
{
- private static DateTime _startTime { get; set; }
-
protected IOrganizationService orgAdminUIService;
protected IOrganizationService orgAdminService;
protected IOrganizationService orgGodService;
- protected IOrganizationService orgRealDataService;
protected XrmMockup365 crm;
@@ -23,8 +20,6 @@ public UnitTestBaseNoProxyTypes(XrmMockupFixtureNoProxyTypes fixture)
orgAdminUIService = crm.GetAdminService(new MockupServiceSettings(true, false, MockupServiceSettings.Role.UI));
orgGodService = crm.GetAdminService(new MockupServiceSettings(false, true, MockupServiceSettings.Role.SDK));
orgAdminService = crm.GetAdminService();
- // Skip real data service - it causes online connection issues and isn't needed for most tests
- orgRealDataService = null;
}
public void Dispose()
@@ -33,4 +28,4 @@ public void Dispose()
// The instance will be garbage collected automatically
}
}
-}
\ No newline at end of file
+}
diff --git a/tests/XrmMockup365Test/UnitTestBaseNoReset.cs b/tests/XrmMockup365Test/UnitTestBaseNoReset.cs
index 4e223afd..d89d8094 100644
--- a/tests/XrmMockup365Test/UnitTestBaseNoReset.cs
+++ b/tests/XrmMockup365Test/UnitTestBaseNoReset.cs
@@ -9,12 +9,9 @@ namespace DG.XrmMockupTest
{
public class UnitTestBaseNoReset : IClassFixture
{
- private static DateTime _startTime { get; set; }
-
protected IOrganizationService orgAdminUIService;
protected IOrganizationService orgAdminService;
protected IOrganizationService orgGodService;
- protected IOrganizationService orgRealDataService;
protected XrmMockup365 crm;
@@ -25,8 +22,6 @@ public UnitTestBaseNoReset(XrmMockupFixture fixture)
orgAdminUIService = crm.GetAdminService(new MockupServiceSettings(true, false, MockupServiceSettings.Role.UI));
orgGodService = crm.GetAdminService(new MockupServiceSettings(false, true, MockupServiceSettings.Role.SDK));
orgAdminService = crm.GetAdminService();
- // Skip real data service - it causes online connection issues and isn't needed for most tests
- orgRealDataService = null;
//create an admin user to run our impersonating user plugins as
@@ -41,4 +36,4 @@ public UnitTestBaseNoReset(XrmMockupFixture fixture)
}
}
}
-}
\ No newline at end of file
+}
diff --git a/tests/XrmMockup365Test/XrmMockup365Test.csproj b/tests/XrmMockup365Test/XrmMockup365Test.csproj
index 9d4903e9..6ec0e892 100644
--- a/tests/XrmMockup365Test/XrmMockup365Test.csproj
+++ b/tests/XrmMockup365Test/XrmMockup365Test.csproj
@@ -43,17 +43,19 @@
-
+
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
+
diff --git a/tests/XrmMockup365Test/XrmmockupFixture.cs b/tests/XrmMockup365Test/XrmmockupFixture.cs
index 7b94cad4..322427e4 100644
--- a/tests/XrmMockup365Test/XrmmockupFixture.cs
+++ b/tests/XrmMockup365Test/XrmmockupFixture.cs
@@ -2,20 +2,17 @@
using DG.Tools.XrmMockup;
using Microsoft.Xrm.Sdk;
using XrmPluginCore;
-using Microsoft.Xrm.Sdk.Client;
using System;
using TestPluginAssembly365.Plugins.LegacyDaxif;
using TestPluginAssembly365.Plugins.ServiceBased;
public class XrmMockupFixture : IDisposable
{
- // Shared settings instances to ensure metadata cache hits
+ // Shared settings instance to ensure metadata cache hits
private static XrmMockupSettings _sharedSettings;
- private static XrmMockupSettings _sharedRealDataSettings;
private static readonly object _settingsLock = new object();
-
+
public XrmMockupSettings Settings => _sharedSettings;
- public XrmMockupSettings RealDataSettings => _sharedRealDataSettings;
public XrmMockupFixture()
{
@@ -34,31 +31,6 @@ public XrmMockupFixture()
MetadataDirectoryPath = GetMetadataPath(),
IPluginMetadata = metaPlugins
};
-
- try
- {
- _sharedRealDataSettings = new XrmMockupSettings
- {
- BasePluginTypes = _sharedSettings.BasePluginTypes,
- CodeActivityInstanceTypes = _sharedSettings.CodeActivityInstanceTypes,
- EnableProxyTypes = _sharedSettings.EnableProxyTypes,
- IncludeAllWorkflows = _sharedSettings.IncludeAllWorkflows,
- ExceptionFreeRequests = _sharedSettings.ExceptionFreeRequests,
- MetadataDirectoryPath = GetMetadataPath(),
- OnlineEnvironment = new Env
- {
- providerType = AuthenticationProviderType.OnlineFederation,
- uri = "https://exampleURL/XRMServices/2011/Organization.svc",
- username = "exampleUser",
- password = "examplePass"
- }
- };
- }
- catch
- {
- // ignore - set to null
- _sharedRealDataSettings = null;
- }
}
}
}
diff --git a/tests/XrmMockup365Test/XrmmockupFixtureNoProxyTypes.cs b/tests/XrmMockup365Test/XrmmockupFixtureNoProxyTypes.cs
index bb8b88c5..1b76b4c4 100644
--- a/tests/XrmMockup365Test/XrmmockupFixtureNoProxyTypes.cs
+++ b/tests/XrmMockup365Test/XrmmockupFixtureNoProxyTypes.cs
@@ -1,18 +1,15 @@
using DG.Some.Namespace;
using DG.Tools.XrmMockup;
using XrmPluginCore;
-using Microsoft.Xrm.Sdk.Client;
using System;
public class XrmMockupFixtureNoProxyTypes : IDisposable
{
// Shared settings instances to ensure metadata cache hits
private static XrmMockupSettings _sharedSettings;
- private static XrmMockupSettings _sharedRealDataSettings;
private static readonly object _settingsLock = new object();
public XrmMockupSettings Settings => _sharedSettings;
- public XrmMockupSettings RealDataSettings => _sharedRealDataSettings;
public XrmMockupFixtureNoProxyTypes()
{
@@ -30,31 +27,6 @@ public XrmMockupFixtureNoProxyTypes()
MetadataDirectoryPath = GetMetadataPath(),
IPluginMetadata = metaPlugins
};
-
- try
- {
- _sharedRealDataSettings = new XrmMockupSettings
- {
- BasePluginTypes = _sharedSettings.BasePluginTypes,
- CodeActivityInstanceTypes = _sharedSettings.CodeActivityInstanceTypes,
- EnableProxyTypes = _sharedSettings.EnableProxyTypes,
- IncludeAllWorkflows = _sharedSettings.IncludeAllWorkflows,
- ExceptionFreeRequests = _sharedSettings.ExceptionFreeRequests,
- MetadataDirectoryPath = GetMetadataPath(),
- OnlineEnvironment = new Env
- {
- providerType = AuthenticationProviderType.OnlineFederation,
- uri = "https://exampleURL/XRMServices/2011/Organization.svc",
- username = "exampleUser",
- password = "examplePass"
- }
- };
- }
- catch
- {
- // ignore - set to null
- _sharedRealDataSettings = null;
- }
}
}
}