Test Driven Development for Dynamics 365 Customizations
Xrm-Framework is a Dynamics 365 customization framework that facilitate complete test driven development of CRM plugins. What’s more; You don’t even need CRM to develop plugins anymore. You can mock the entire CRM platform using the built in JSON framework.
Best documentaion is to download the source from the GitHub repo and studying the samples present
Start with installing the latest version of the package from the package manager console in Visual Studio
Install-Package Qubit.Xrm.Framework
This will install quite a bit of dependencies along with the main package.
After installation you can now start creating your first plugin. Before we do that, let us look at the different types of plugins you can create:
These are plugins that follow a pipeline pattern for CRM entities. What this means is that each message and stage in the plugin’s pipeline is considered a pipeline event. You will need two things to execute a pipeline plugin. An derivation of EntityPipelineService abstract class and the plugin itself. Everything else is automatically taken care of.
public interface ISampleEntityPipelineService : IEntityPipelineService
{ }
public class SampleEntityPipelineService : EntityPipelineService, ISampleEntityPipelineService
{
private readonly IOrganizationService _organizationService;
private readonly ISettingsProvider _settingsProvider;
public SampleEntityPipelineService(IPluginExecutionContextAccessor executionContextAccessor, ILogger logger,
IOrganizationService organizationService, ISettingsProvider settingsProvider) :
base(executionContextAccessor, logger)
{
_organizationService = organizationService;
_settingsProvider = settingsProvider;
}
protected override void OnCreated()
{
Entity account = ExecutionContextAccessor.Target.GetEntity(new ColumnSet());
account["qubit_autonumber"] = "12345";
using (WebClient client = new WebClient())
{
string url = _settingsProvider.Get("abnservice");
account["qubit_abn"] = client.DownloadString($"{url.TrimEnd('/')}/validate");
}
_organizationService.Update(account);
}
}
[Messages(Messages.Create, Messages.Update)]
[TargetEntityLogicalName("account")]
public sealed class SampleEntityPipelinePlugin : EntityPipelinePlugin<ISampleEntityPipelineService>
{
public SampleEntityPipelinePlugin()
{ }
public SampleEntityPipelinePlugin(IKernel fakeServices, Action<IKernel> setupMockServices)
: base(fakeServices, setupMockServices)
{ }
}
The advantage of using a pipeline plugin is that you now just have to implement the pipeline services and its various overrides to achieve your customizations in a more readable code base. This is because this exposes the plugins pipeline stage where the code will be executed, rather than relying on plugin registration information.
Standard plugins are the tradational plugins that we all have loved to embrace in CRM.
[Messages(Messages.Create, Messages.Update)]
[TargetEntityLogicalName("account")]
public sealed class SamplePlugin : Qubit.Xrm.Framework.Plugin
{
public SamplePlugin()
{ }
public SamplePlugin(IKernel fakeServices, Action<IKernel> setupMockServices)
: base(fakeServices, setupMockServices)
{ }
public override void Execute(IKernel services)
{
IOrganizationService organizationService = services.Get<IOrganizationService>();
}
}
There is nothing special about this implementation except all the service are now neatly registerd into the ninject container.
You can register your own services through an override of the configure method.
public interface ISomeService
{
void DoSomethingSpectacular();
}
public class SomeService : ISomeService
{
public void DoSomethingSpectacular()
{
//
}
}
[Messages(Messages.Create, Messages.Update)]
[TargetEntityLogicalName("account")]
public sealed class SamplePlugin : Qubit.Xrm.Framework.Plugin
{
public SamplePlugin()
{ }
public SamplePlugin(IKernel fakeServices, Action<IKernel> setupMockServices)
: base(fakeServices, setupMockServices)
{ }
public override void Execute(IKernel services)
{
IOrganizationService organizationService = services.Get<IOrganizationService>();
}
public override void Configure(IKernel services)
{
services.Bind<ISomeService>().To<SomeService>().InTransientScope();
}
}
All services can be accessed through ninject’s container (IKernel)
public override void Execute(IKernel services)
{
IOrganizationService organizationService = services.Get<IOrganizationService>();
}
The plugin execution context accessor gives you access to the following members:
The settings provider is a simple application settings store that provides configuration settings to various parts of the framework. It could be as simple as a hard coded key/value store or as complex as a SQL server based store.
A simple custom settings provider is shown below
public class CustomSettingsProvider : DefaultSettingsProvider
{
private Dictionary<string, string> _store;
public CustomSettingsProvider(IOrganizationService organizationService, ICache cache)
: base(organizationService, cache)
{
_store = new Dictionary<string, string>
{
{ "Logging", "{\"SourceName\": \"XrmFrameworkTests\",\"Sink\": \"Console\" }" }
};
}
public override string Get(string key)
{
return _store[key];
}
}
Another example where you can fully customize the provider
public class CustomSettingsProvider : ISettingsProvider
{
private Dictionary<string, string> _store;
public CustomSettingsProvider()
{
_store = new Dictionary<string, string>
{
{ "Logging", "{\"SourceName\": \"XrmFrameworkTests\",\"Sink\": \"Console\" }" }
};
}
public string Get(string key)
{
return _store[key];
}
public T Get<T>(string key) where T : class
{
string jsonString = Get(key);
return JsonConvert.DeserializeObject<T>(jsonString);
}
}
If you don’t already have a configuration infrastrucutre setup, you can use the built in settings provider (DefaultSettingsProvider). To use this, you will also have to install the Qubit Settings Provider managed solution in your CRM system.
To use your custom settings provider use it as a type parameter to the Plugin abstract class
[Messages(Messages.Create, Messages.Update)]
[TargetEntityLogicalName("account")]
public sealed class SamplePlugin : Qubit.Xrm.Framework.Plugin<CustomSettingsProvider>
{
public SamplePlugin()
{ }
public SamplePlugin(IKernel fakeServices, Action<IKernel> setupMockServices) : base(fakeServices, setupMockServices)
{ }
public override void Execute(IKernel services)
{
IOrganizationService organizationService = services.Get<IOrganizationService>();
}
public override void Configure(IKernel services)
{
services.Bind<ISomeService>().To<SomeService>().InTransientScope();
}
}
Derive from Qubit.Xrm.Framework.Plugin and the default settings provider will automatically be used
[Messages(Messages.Create, Messages.Update)]
[TargetEntityLogicalName("account")]
public sealed class SamplePlugin : Qubit.Xrm.Framework.Plugin
{
public SamplePlugin()
{ }
public SamplePlugin(IKernel fakeServices, Action<IKernel> setupMockServices) : base(fakeServices, setupMockServices)
{ }
public override void Execute(IKernel services)
{
IOrganizationService organizationService = services.Get<IOrganizationService>();
}
public override void Configure(IKernel services)
{
services.Bind<ISomeService>().To<SomeService>().InTransientScope();
}
}
A step by step series of examples that tell you how to get a development env running
Say what the step will be
Give the example
And repeat
until finished
End with an example of getting some data out of the system or using it for a little demo
Explain how to run the automated tests for this system
Explain what these tests test and why
Give an example
Add additional notes about how to deploy this on a live system
Please read CONTRIBUTING.md for details on our code of conduct, and the process for submitting pull requests to us.
We use SemVer for versioning. For the versions available, see the tags on this repository.
See also the list of contributors who participated in this project.
This project is licensed under the Simple Public License 2.0 - see the LICENSE.md file for details