How to clone a record (server side) in Dynamics crm 2016? - dynamics-crm

I am using crm 2016 and I need to clone a record using plugin, after googling I found out that I need to use Microsoft.Xrm.Client that hold the clone() function - which is not in 2016 SDK because of MS reorganization.This lib is in 2015 SDK.
My questions are :
1. If I'll take that lib from CRM 2015 and use it in 2016 will it be supported?
2. If it's not supported what are my options to clone a record in server side?

The method Clone() in the Microsoft.Xrm.Client only creates a copy of the Entity object in memory. It does not create a copy in the database of CRM. When you need to create a copy in the database just instantiate a new Entity object and pass it to the Create() method of the IOrganizationService interface.
When you really need a deep clone as described on MSDN you could consider writing one yourself. In most scenarios you will only need to copy the objects in the attributes collection. Of those objects only the reference types EntityReference, OptionSetValue and Money wiil need your special attention.
I would not advise to use deprecated libraries.

We use the following helper method to clone an entity - this is an updated version from the original which correctly clones the reference types, and excludes the system attributes
public static Entity CloneEntitySandbox(Entity entityToClone)
{
var newEntity = new Entity(entityToClone.LogicalName);
var systemAttributes = new List<string>();
systemAttributes.Add("createdon");
systemAttributes.Add("createdby");
systemAttributes.Add("modifiedon");
systemAttributes.Add("modifiedby");
systemAttributes.Add("owninguser");
systemAttributes.Add("owningbusinessunit");
foreach (var attribute in entityToClone.Attributes
.Where(x => x.Key != entityToClone.LogicalName + "id")
.Where(x => !systemAttributes.Contains(x.Key)))
{
switch (attribute.Value.GetType().Name)
{
case "Money":
var m = attribute.Value as Money;
newEntity[attribute.Key] = new Money(m.Value);
break;
case "EntityReference":
var er = attribute.Value as EntityReference;
newEntity[attribute.Key] = new EntityReference(er.LogicalName, er.Id);
break;
case "OptionSetValue":
var os = attribute.Value as OptionSetValue;
newEntity[attribute.Key] = new OptionSetValue(os.Value);
break;
default:
newEntity[attribute.Key] = attribute.Value;
break;
}
}
return newEntity;
}
Note that this does not perform the save to create the cloned entity in the CRM database, that's up to you.

Microsoft announcement says:
We also removed Microsoft.Xrm.Client from the CRM 2016 (8.x) SDK client because it was not compliant with the OAuth changes, and replaced it with Microsoft.Xrm.Tooling.Connector. You can use the current Microsoft Dynamics 365 Software Development Kit (SDK) to access Microsoft Dynamics CRM back to version 6.x for both auth and messaging.
Dynamics 365 SDK Backwards Compatibility
You can still use Microsoft.Xrm.Client.dll in your project from older SDK, this maybe supported for a while.
But I would recommend to go with custom Action, taking parent record as EntityReference Input parameter, Retrieve the parent record data + needed related entities & manually create (clone) child record + related entities records in Action.
You can Execute/invoke this Action from client/server side, wherever you want.

Related

how to use services before app build in .net core 6.0

I have earlier achieved this .net 3.1. But it couldn't be possible with .Net 6 because of startup.cs removed.
I have registered a few services,
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var appSettings = builder.Configuration.GetSection("AppSettings").Get<AppSettings>();
builder.Services.AddScoped<IEncryption, Encryption>();
//Here I need to get the IEncryption Service, and call the method in this service to encrypt/decrypt the connection string to pass to DBContext Service.
builder.Services.AddDbContext<CatalogDbContext>(options => options.UseNpgsql(
appSettings.ConnectionString));
var app = builder.Build();
Earlier in .NET 3.1, I used BuildServicProvider() to get the Encryption service, and call the methods in that service to do the required logic then got the proper connection string I wanted that would be passed to the DBContext service on the next line.
Now, .NET 6/7 is forced to use the services only after app = builder.Build(); so, I can't register the DBCOntext after the build() method.
How can I solve this case? Any recommended approach to do this in .NET 6/7?
You still can useStartup.cs in .net 6
var builder = WebApplication.CreateBuilder(args);
var startup = new Startup(builder.Configuration);
startup.ConfigureServices(builder.Services); // calling ConfigureServices method
var app = builder.Build();
startup.Configure(app, builder.Environment); // calling Configure method
And then you can use ConfigureServices and Configure methods to register your services before building.
You didn't need to use BuildServiceProvider in .NET Core 3.1 either. AddDbContext has an overload that provides access to an IServiceProvider instance :
builder.Services.AddDbContext<CatalogDbContext>((services,options) =>{
var myOwnDecrypter=services.GetRequiredService<IMyOwnDecrypter>();
var cns=myOwnDecrypter.Decrypt(appSettings.ConnectionString,key);
options.UseNpgsql(cns);
});
or, if you use the ASP.NET Core Data Protection package :
builder.Services.AddDataProtection();
...
builder.Services.AddDbContext<CatalogDbContext>((services,options) =>{
var protector = services.GetDataProtector("Contoso.Example.v2");
var cns=protector.Unprotect(appSettings.ConnectionString);
options.UseNpgsql(cns);
});
or, if IConfiguration.GetConnectionString is used :
builder.Services.AddDataProtection();
...
builder.Services.AddDbContext<CatalogDbContext>((services,options) =>{
var conn_string=services.GetService<IConfiguration>()
.GetConnectionString("MyConnectionString");
var protector = services.GetDataProtector("Contoso.Example.v2");
var cns=protector.Unprotect(conn_string);
options.UseNpgsql(cns);
});
That said, it's the configuration provider's job to decrypt encrypted settings, not the service/context's. ASP.NET Core's configuration allows using multiple different configuration sources in the same host, not just a single settings file. There's nothing special about appsettings.json. That's just the default settings file name.
You can add another settings file with sensitive contents with AddJsonSettings. That file could use the file system's encryption, eg NTFS Encryption, to ensure it's only readable by the web app account
You can read settings from a key management service, like Hashicorp, Azure Key Vault, Amazon Key Management etc.
You can create your own provider that decrypts its input. The answers to this SO questino show how to do this and one of them inherits from JsonConfigurationProvider directly.
Important Caveat: In general, my suggestion below is a bad practice
Do not call BuildServiceProvider
Why is bad? Calling BuildServiceProvider from application code results in more than one copy of singleton services being created which might result in incorrect application behavior.
Justification: I think it is safe to call BuildServiceProvider as long as you haven't registered any singletons before calling it. Admittedly not ideal, but it should work.
You can still callBuildServiceProvider() in .Net6:
builder.Services.AddScoped<IEncryption, Encryption>();
// create service provider
var provider = builder.Services.BuildServiceProvider();
var encryption = scope.ServiceProvider.GetService<IEncryptionService>();
// use service here
or alternatively
builder.Services.AddScoped<IEncryption, Encryption>();
var provider = builder.Services.BuildServiceProvider();
using (var scope = provider.CreateScope()) {
var encryption = scope.ServiceProvider.GetService<IEncryptionService>();
// use service here
}
Alternative:
You can still use the classic startup structure in .Net6/7. We upgraded our .Net3.1 projects to .Net6 without having to rewrite/restructure the Startup()

Connections entity - Audit menu missing

We have migrated a CRM 2011 successfully to Dynamics 365, Version 9.
We have determined that the audit history for connections is not present in the UI.
Is it because the connection form is still in CRM 2011 style (out-of-the-box)? If so, where do you find the connection audit history?
Auditing is enabled in CRM, on the connection entity and also some fields are configured for auditing.
I would assume this is expected behavior & working as per design in various CRM versions.
Connection entity Form editor showing left navigation with "Audit History" menu.
Actual Connection form rendering is not showing the left navigation. I remember this was the case with CRM 2011 form also.
If you want to show the "Audit history" explicitly, you can always go for some unsupported solution like discussed here.
Keep an iframe & point the url something like this: (Obviously you have to script to parameterize it)
https://devcrm.crm.dynamics.com/userdefined/areas.aspx?formid=52078acf-77c4-4e18-86b8-ebffcafc0698&inlineEdit=1&navItemName=Audit History&oId={3877A32E-EA20-E911-A952-000D3A34E213}&oType=3234&pagemode=iframe&rof=true&security=852023&tabSet=areaAudit&theme=Outlook15White
Code from the above UG discussion link:
function buildIframeSource() {
var url = window.parent.Xrm.Page.context.getClientUrl();
var recordId = window.parent.Xrm.Page.data.entity.getId().replace("{", "").replace("}", "");
var element = document.getElementById("areaAuditFrame");
element.src = url + "/userdefined/areas.aspx?formid=52078acf-77c4-4e18-86b8-ebffcafc0698&inlineEdit=1&navItemName=Audit%20History&oId=%7b" + recordId + "%7d&oType=10270&pagemode=iframe&rof=true&security=852023&tabSet=areaAudit&theme=Outlook15White";
};

Azure Functions, Entity Framework and Oracle DB - basic POC fails

I'm having a lot of trouble getting a basic proof-of-concept working, in which I am accessing an Oracle DB (11g) through Azure Functions via Entity Framework (6.2).
Prerequisites:
ODT For Visual Studio 2017 is installed, as well as Azure Functions CLI/Core Tools. Everything mentioned below is done entirely via Visual Studio 2017, not through Azure portal.
Take 1:
Created a new project with the Azure Functions template.
Installed NuGet packages EntityFramework (6.2.0), Oracle.ManagedDataAccess (12.2.1100) and Oracle.ManagedDataAccess.EntityFramework (12.2.1100). Note: When installing NuGet packages in projects using the Azure Functions template, the packages are added under Dependencies -> NuGet, rather than under References.
Added ADO.NET Entity Data Model to project.
Problem: After setting my connection string, choosing Entity Framework 6.x is unavailable, with the following error message:
An Entity Framework database provider compatible with the latest
version of Entity Framework could not be found for your data
connection. If you have already installed a compatible provider,
ensure you have rebuilt your project before performing this action.
Otherwise, exit this wizard, install a comaptible provider, and
rebuild your project befre performing this action.
As the simplest of workarounds, I have tried to just go ahead with EF5, but it throws an exception while creating the DB model (after selecting the objects to include in model, including some stored procedures).
Take 2:
Created project and installed NuGet packages as above.
Created class library project to facilitate the Oracle interactions.
Installed the same NuGet packages as above in the class library project.
Added ADO.NET Entity Data Model to class library project and added some database objects to the database model. Also added custom constructor to the model for specific connection string, because managing connection strings in Azure Functions was a seperate set of headaches that I'll deal with later.
Added a simple wrapper method to the class library project that calls a stored procedure from the database model:
public static string NameByEmpNo(int empNo)
{
string result;
MyEntities entities = new MyEntities("metadata=res://*/MyEntities.csdl|res://*/MyEntities.ssdl|res://*/MyEntities.msl;provider=Oracle.ManagedDataAccess.Client;provider connection string='DATA SOURCE=127.0.0.1:1521/ORCL;PASSWORD=tiger;USER ID=SCOTT'");
ObjectParameter name = new ObjectParameter("o_empname", typeof(string));
entities.GET_EMP_NAME_PROC(empNo, name);
result = (string)name.Value;
return result;
}
Added reference to the class library in the Azure Functions project.
Added function that calls NameByEmpNo:
[FunctionName("GetNameByEmpNo")]
public static async Task<HttpResponseMessage> GetNameByEmpNo([HttpTrigger(AuthorizationLevel.Function, "get", Route = null)]HttpRequestMessage req, TraceWriter log)
{
int empNo = Int32.Parse(req.GetQueryNameValuePairs()
.FirstOrDefault(q => string.Compare(q.Key, "empno", true) == 0)
.Value);
string empName = ScottAccess.NameByEmpNo(empNo);
return req.CreateResponse(HttpStatusCode.OK, "Employee name: " + empName);
}
Problem: At runtime, calling the function fails with this error
message:
Exception while executing function: GetNameByEmpNo -> The ADO.NET
provider with invariant name 'Oracle.ManagedDataAccess.Client' is
either not registered in the machine or application config file, or
could not be loaded. See the inner exception for details. -> Unable to
find the requested .Net Framework Data Provider. It may not be
installed.
Bonus info: My class library works perfectly when called through a console application. Also, my Azure Functions app works perfectly when calling functions that do not use my class library...
I am stumped. Has anyone got experience with getting this combination of techs working together and can offer some insight into where I'm going wrong / provide steps to get a basic connection working?
Entity Framework within Azure Functions defaults the providers to System.Data.SqlClient so SQL connections will work without any configuration changes, but that means you have to do something special for Oracle connections. The problem seems to come from the config values that the Oracle.ManagedDataAccess.Client library assumes are available within the App.Config or Web.Config file in the project, which are inserted whenever you install the Oracle.ManagedDataAcess.EntityFramework Nuget package. Azure Functions don't have config files, and I wasn't able to find any way to specify the Oracle provider in the settings json files.
I found a solution in this post
It suggests bypassing this mechanism and creating a DbConfiguration for Oracle, then using DbConfigurationType to tell the DbContext which configuration you're using.
public class OracleDbConfiguration : DbConfiguration
{
public OracleDbConfiguration()
{
SetDefaultConnectionFactory(new OracleConnectionFactory());
SetProviderServices("Oracle.ManagedDataAccess.Client", EFOracleProviderServices.Instance);
SetProviderFactory("Oracle.ManagedDataAccess.Client", new OracleClientFactory());
}
}
[DbConfigurationType(typeof(OracleDbConfiguration))]
public partial class MyEntities : IGISContext
{
//Expose Connection String Constructor
public MyEntities(string connectionString, int commandTimeoutInSeconds = 30) : base(connectionString)
{
this.Database.CommandTimeout = commandTimeoutInSeconds;
}
}
Note: I used EF 6 Database First to generate my EDMX; MyEntities here is a partial class for providing a constructor that takes in a connection string.
The oracle connection wil use the specified DbConfiguration class, and any SQL database connections will continue to work using the defaults.
My solution is using the Nuget Packages:
EntityFramework 6.2.0
Oracle.ManagedDataAccess 12.2.1100
Oracle.ManagedDataAccess.EntityFramework 12.2.1100

MSCRM Addcustom filter

I am using MS CRM 2013 and created a custom filter on ownerid. It works ok but just that it returns both team and user. I just want to have user. If I provide the entityname as below:
Xrm.Page.getControl("ownerid")
.addCustomFilter(thisfetch, "systemuser");
Then it filters on user and returns filtered users and all teams.
How can I fix it?
thisfetch is my filter criteria.
Please help.
Instructing the Lookup dialog to only allow SystemUser is currently not supported.
There are unsupported workarounds but as pointed out in the linked article they can easily break due to changes in the DOM.
plain js
function setToFieldFilter()
{
document.getElementById("to_i").setAttribute("lookuptypenames", "systemuser:8:User");
document.getElementById("to_i").setAttribute("lookuptypes", "8");
}
function onLoad()
{
Xrm.Page.getControl("ownerid").addPreSearch(setToFieldFilter);
}
jquery
$("#to_i").attr("lookuptypenames", "systemuser:8:User");
$("#to_i").attr("lookuptypes", "8");
Sources
CRM 2013 : Change default entity for a Lookup field
Filter PartyList entities Lookup using Jscript in Microsoft Dynamics CRM 2011

Error 0152: No Entity Framework provider found for 'Oracle.DataAccess.Client' ADO.NET provider

I have Windows 7 x64, Visual Studio 2012 with the 'ODAC 11.2 Release 5 and Oracle Developer Tools for Visual Studio (11.2.0.3.20)'.
Then I connected to Oracle db, create .edmx model. But when I'm trying to use EF to operate some data
var ent = new Entities();
var res = ent.table1.ToList();
Error happends
Schema specified is not valid. Errors:
Model.ssdl(2,2) : error 0152: No Entity Framework provider found for
'Oracle.DataAccess.Client' ADO.NET provider. Make sure the provider is registered in
the 'entityFramework' section of the application config file. See
http://go.microsoft.com/fwlink/?LinkId=260882 for more information.
I checked my AppConfig, all sections are ok. Also I installed ODAC1120320Xcopy_x64 (and added a environment variables too). But nothing changes.
If I do it in this way, all works fine
EntityConnection econn = new EntityConnection("name=Entities");
econn.Open();
EntityCommand ecmd = econn.CreateCommand();
ecmd.CommandText = "SELECT e.NAME FROM Entities.table1 as e";
EntityDataReader ereader = ecmd.ExecuteReader(CommandBehavior.SequentialAccess);
while (ereader.Read()) { ... }
Have you any ideas? Will be very grateful for any help.

Resources