What is the difference between IOrganizationService and OrganizationServiceProxy in Dynamics CRM?
Is it related to accessing services in Crm context and outside Crm Context?
Simplest answer is IOrganizationService is an interface whereas OrganizationServiceProxy is a class which implements the IOrganizationService interface. This means any properties/methods on IOrganizationService are by default also available via OrganizationServiceProxy.
If running in the context of a plugin or custom workflow activity it will give you access to an IOrganizationService that you can use to interrogate CRM.
If you are writing something external, such as a windows service or standalone application, then you generally use the class OrganizationServiceProxy to set up a connection to the CRM web service. You can obviously assign this to an IOrganizationService later (e.g. dependency injection / for unit testing purposes). Or if you prefer there is no reason why you can continue to use the OrganizationServiceProxy.
The IOrganisationService is used within plugins and custom workflow activities and derived from the execution context.
While the OrganisationServiceProxy is mainly used for code running outside the Dynamics CRM application.
If you're using the sdk assemblies (specifically microsoft.xrm.sdk.dll), then you'll be using an implementation of IOrganizationService, and the call time will be identical. The main purpose of OrganizationServiceProxy is to provide options for establishing a connection to CRM in code that runs outside of CRM .The OrganizationServiceProxy class implements the IOrganizationService and provides an authenticated WCF channel to the organization service
OrganizationService is a higher level class that provides richer client side functionality and actually uses OrganizationServiceProxy inside it.
The Microsoft.Xrm.Client assembly that holds this higher level API cannot be used in Plugins etc but is intended for rich clients and ASP.NET.
It's worth noting that the Microsoft.Xrm.Client assembly has been removed from the CRM2016 SDK. For 2016 projects you might consider using the XRM Toolking assemblies.
See msdn.microsoft.com/.../dn689057.aspx
It has similar functionality that you mention around the connection manager -msdn.microsoft.com/.../mt608573.aspx
OrganizationServiceProxy is implementation of IOrganizationService. It is similar to case as List is implementation of interface IList. If to speak about why on Earth Microsoft provides both interface and implementation besides testing, I can propose one interesting case which happened in my life. I was in need of re-reading information from dynamics crm. In my case OrganizationServiceProxy got expired faster, then information was received from CRM. In order to fix it I've created following facade:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
namespace OrganizationService
{
public delegate IOrganizationService CreateOrganizationServiceFunc(string organizationServiceUrl, string userName, string password, TimeSpan? timeout = null, bool useSSL = false);
public class OrganizationServiceFacade : IOrganizationService
{
private IOrganizationService _serviceInternal { get; set; }
private CreateOrganizationServiceFunc _creator;
Func<IOrganizationService> _orgServiceFactory;
public OrganizationServiceFacade(Func<IOrganizationService> orgServiceFactory)
{
_orgServiceFactory = orgServiceFactory;
CreateService();
}
private void CreateService()
{
_serviceInternal = _orgServiceFactory();
}
public void Associate(string entityName, Guid entityId, Relationship relationship, EntityReferenceCollection relatedEntities)
{
try
{
_serviceInternal.Associate(entityName, entityId, relationship, relatedEntities);
}
catch (System.ServiceModel.Security.MessageSecurityException mex)
{
CreateService();
_serviceInternal.Associate(entityName, entityId, relationship, relatedEntities);
}
}
public Guid Create(Entity entity)
{
Guid result;
try
{
result = _serviceInternal.Create(entity);
}
catch (System.ServiceModel.Security.MessageSecurityException mex)
{
CreateService();
result = _serviceInternal.Create(entity);
}
return result;
}
public void Delete(string entityName, Guid id)
{
try
{
_serviceInternal.Delete(entityName, id);
}
catch (System.ServiceModel.Security.MessageSecurityException mex)
{
CreateService();
_serviceInternal.Delete(entityName, id);
}
}
public void Disassociate(string entityName, Guid entityId, Relationship relationship, EntityReferenceCollection relatedEntities)
{
try
{
_serviceInternal.Disassociate(entityName, entityId, relationship, relatedEntities);
}
catch (System.ServiceModel.Security.MessageSecurityException mex)
{
CreateService();
_serviceInternal.Disassociate(entityName, entityId, relationship, relatedEntities);
}
}
public OrganizationResponse Execute(OrganizationRequest request)
{
try
{
return _serviceInternal.Execute(request);
}
catch (System.ServiceModel.Security.MessageSecurityException mex)
{
CreateService();
return _serviceInternal.Execute(request);
}
}
public Entity Retrieve(string entityName, Guid id, ColumnSet columnSet)
{
try
{
return _serviceInternal.Retrieve(entityName, id, columnSet);
}
catch (System.ServiceModel.Security.MessageSecurityException mex)
{
CreateService();
return _serviceInternal.Retrieve(entityName, id, columnSet);
}
}
public EntityCollection RetrieveMultiple(QueryBase query)
{
try
{
return _serviceInternal.RetrieveMultiple(query);
}
catch (System.ServiceModel.Security.MessageSecurityException mex)
{
CreateService();
return _serviceInternal.RetrieveMultiple(query);
}
}
public void Update(Entity entity)
{
try
{
_serviceInternal.Update(entity);
}
catch (System.ServiceModel.Security.MessageSecurityException mex)
{
CreateService();
_serviceInternal.Update(entity);
}
}
}
}
and then I've got simple reconnect mechanism:
Trace.TraceInformation("Creation of CRM connection");
for (var maxTry = 0; maxTry < reconectionTries; maxTry++)
{
try
{
var service = new OrganizationServiceFacade(() =>
XrmServiceCreator.CreateOrganizationService("organizationServiceUrl",
"username",
"userpassword",
DefaultTimeout));
var response = (WhoAmIResponse) service.Execute(new WhoAmIRequest());
if (response.Results.Count == 0)
{
throw new InvalidDataException($"CRM returned no data in response. Number of retries is: {maxTry}, user name: {ConfigSettings.Default.RunAsUser}, Default timeout = {DefaultTimeout}");
}
return service;
}
catch (Exception e)
{
Trace.TraceError("Exception: {0}", e);
}
}
method XrmServiceCreator.CreateOrganizationService returns instance of IOrganizationService, and behind curtains it creates instance of OrganizationServiceProxy
Related
I am creating a custom workflow in Microsoft Dynamics CRM to automatically update a field when a record is saved.
A developer on a forum provided the following source code; but he is not responding to my questions.
public class SalesRepActivity2 : WorkFlowActivityBase
{
[Input("Sales Rep Name")]
public InArgument<string> SalesRepName { get; set; }
[Output("Sales Rep")]
[ReferenceTarget("systemuser")]
public OutArgument<EntityReference> SalesRep { get; set; }
[Output("IsSuccess")]
public OutArgument<bool> IsSuccess { get; set; }
[Output("Message")]
public OutArgument<string> Message { get; set; }
protected override void Execute(
CodeActivityContext activityContext,
IWorkflowContext workflowContext,
IOrganizationService CrmService,
ITracingService trace)
{
try
{
string salesRepName = SalesRepName.Get(activityContext);
if (string.IsNullOrWhiteSpace(salesRepName))
{
IsSuccess.Set(activityContext, false);
Message.Set(activityContext, "Sales Rep Name not provided");
}
var QEsystemuser = new QueryExpression("systemuser");
QEsystemuser.ColumnSet.AddColumns("salesrepname");
QEsystemuser.Criteria.AddCondition("salesrepname", ConditionOperator.Equal, salesRepName);
var results = CrmService.RetrieveMultiple(QEsystemuser);
if (results == null || !results.Entities.Any())
{
IsSuccess.Set(activityContext, false);
Message.Set(activityContext, "User with " + salesRepName + " not found");
return;
}
if (results.Entities.Count > 1)
{
IsSuccess.Set(activityContext, false);
Message.Set(activityContext, "Multiple users found with same name : " + salesRepName);
return;
}
IsSuccess.Set(activityContext, true);
SalesRep.Set(activityContext, results.Entities.Single().ToEntityReference());
}
catch (Exception ex)
{
IsSuccess.Set(activityContext, false);
Message.Set(activityContext, "An error occurred trying to find user : " + ex.Message);
}
}
I am trying to get the code to compile on my machine.
I installed the following NuGet packages, which resolved most of the errors:
Microsoft.Xrm.Sdk.Workflow.2015
Microsoft.Xrm.Sdk.2015
But my project cannot resolve the WorkFlowActivityBase class.
Is there a reference I should set or a NuGet package I should install to resolve this?
Thank you.
WorkFlowActivityBase is a custom base class that implements the CodeActivity (System.Activities) anyone can write, it's not an official Dynamics class. Yon can find dozen of classes lie that in the web.
Basically, you should use the CodeActivity. here as an example:
https://learn.microsoft.com/en-us/powerapps/developer/common-data-service/workflow/sample-create-custom-workflow-activity
The code you posted is not a good place for you to start because it makes heavy use of proprietary objects. Ziv has provided good information about extending the CodeActivity class but I do not recommend starting there. Instead read about how to develop custom workflow activities, and write a workflow using the base Microsoft classes so that you understand how they work:
https://learn.microsoft.com/en-us/powerapps/developer/common-data-service/workflow/workflow-extensions
Once you have some experience with creating custom workflows, and you understand the limitations of the base objects, then go back and implement something more fancy.
Here in an exaple from the documentation:
namespace Microsoft.Crm.Sdk.Samples
{
public sealed class SimpleSdkActivity : CodeActivity
{
protected override void Execute(CodeActivityContext executionContext)
{
//Create the tracing service
ITracingService tracingService = executionContext.GetExtension<ITracingService>();
//Create the context
IWorkflowContext context = executionContext.GetExtension<IWorkflowContext>();
IOrganizationServiceFactory serviceFactory = executionContext.GetExtension<IOrganizationServiceFactory>();
IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);
...
var systemUsers = service.RetrieveMultiple(QEsystemuser)
}
}
}
we have an mvc 5 application with individual user authentication we also have a xamarin forms application. we need to be able to use the same login details thats created on the web application when we log in via the xamarin application we are creating. we have successfully been able to create web api controllers using existing models in the web application and read/write the data in the xamarin application. but the problem is that we are not able to provide the same authentication we have(username and password with role assigned to the user) to the xamarin application. how can we make an api controller that reads from our existing database..please note our application is hosted on azure with a sql database.
basically we want to provide a login to our web application via the mobile app.
You need to take a look at Adrian Halls book - chapter 2 covers custom authentication which is what you need.
https://adrianhall.github.io/develop-mobile-apps-with-csharp-and-azure/chapter2/custom/
The key points are setting the mobile app to use authentication in the Azure portal but don't set any of the authentication providers (this makes it custom)
You then need to implement your own custom authentication controller to handle the authentication call back like this example taken from Adrian's book;
using System;
using System.IdentityModel.Tokens;
using System.Linq;
using System.Security.Claims;
using System.Web.Http;
using Microsoft.Azure.Mobile.Server.Login;
using Newtonsoft.Json;
namespace AWPBackend.Controllers
{
[Route(".auth/login/custom")]
public class CustomAuthController : ApiController
{
private MobileServiceContext db;
private string signingKey, audience, issuer;
public CustomAuthController()
{
db = new MobileServiceContext();
signingKey = Environment.GetEnvironmentVariable("WEBSITE_AUTH_SIGNING_KEY");
var website = Environment.GetEnvironmentVariable("WEBSITE_HOSTNAME");
audience = $"https://{website}/";
issuer = $"https://{website}/";
}
[HttpPost]
public IHttpActionResult Post([FromBody] User body)
{
if (body == null || body.Username == null || body.Password == null ||
body.Username.Length == 0 || body.Password.Length == 0)
{
return BadRequest(); ;
}
if (!IsValidUser(body))
{
return Unauthorized();
}
var claims = new Claim[]
{
new Claim(JwtRegisteredClaimNames.Sub, body.Username)
};
JwtSecurityToken token = AppServiceLoginHandler.CreateToken(
claims, signingKey, audience, issuer, TimeSpan.FromDays(30));
return Ok(new LoginResult()
{
AuthenticationToken = token.RawData,
User = new LoginResultUser { UserId = body.Username }
});
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
db.Dispose();
}
base.Dispose(disposing);
}
private bool IsValidUser(User user)
{
return db.Users.Count(u => u.Username.Equals(user.Username) && u.Password.Equals(user.Password)) > 0;
}
}
public class LoginResult
{
[JsonProperty(PropertyName = "authenticationToken")]
public string AuthenticationToken { get; set; }
[JsonProperty(PropertyName = "user")]
public LoginResultUser User { get; set; }
}
public class LoginResultUser
{
[JsonProperty(PropertyName = "userId")]
public string UserId { get; set; }
}
The actual custom authentication takes place in the IsValidUser function and should link to your existing internal authentication method (do not use the example here, this is for demonstration only)
Custom authentication has to use a client side flow which also meets your requirements.
Something very strange is happening in production, and it only happens in production. I have a Web API running and in one of the APIs, there is a repository created in the constructor and used in the functions. This is how the flow of a request works:
HTTP request comes in
MVC API controller decides which "worker" class to instantiate and creates it using Activator.CreateInstance
API controller calls worker.OnExecute inside of a Task.Run() and returns the http response
Worker calls _engine.Execute
Each worker instantiates another "engine" class that has all of the logic.
The engine in case constructs 3 repositories created using a UnitOfWork that is created per engine instance, like so:
public class MyWorker : Worker
{
private readonly MyEngine _engine;
public MyWorker()
{
_engine = new MyEngine();
}
protected override WorkerResult OnExecute(JObject data, CancellationToken cta)
{
return new WorkerResult(HttpStatusCode.OK, _engine.Execute(data));
}
}
public class MyEngine : EngineBase
{
private BaseRepository<Order> OrderRepo { get; set; }
private BaseRepository<OrderItem> OrderItemRepo { get; set; }
public MyEngine()
{
OrderRepo = new BaseRepository<Order>(MyUnitOfWork);
OrderItemRepo = new BaseRepository<OrderItem>(MyUnitOfWork);
}
public string Execute(JObject data)
{
return IsOrderValid(data).ToString();
}
public bool IsOrderValid(JObject data)
{
var orderId = data.Value<int>("OrderId");
// Without this line it crashes. With this line it crashes
//OrderRepo = new BaseRepository<Order>(InternationalWork);
// This is where it crashes
Order order = OrderRepo.First(x => x.OrderID == orderId);
// more code
}
}
public class EngineBase : UnitOfWorker, IDisposable
{
private UnitOfWork _myUnitOfWork;
public EngineBase() { }
public UnitOfWork MyUnitOfWork
{
get
{
return _myUnitOfWork ?? (_myUnitOfWork = new UnitOfWork(new DbContextAdapter(new MyDbContext())));
}
}
}
This is the actual stack trace:
The operation cannot be completed because the DbContext has been disposed.
StackTrace1
at System.Data.Entity.Internal.LazyInternalContext.InitializeContext()
at System.Data.Entity.Internal.LazyInternalContext.get_ObjectContext()
at System.Data.Entity.Internal.Linq.InternalSet`1.CreateObjectQuery(Boolean asNoTracking, Nullable`1 streaming, IDbExecutionStrategy executionStrategy)
at System.Data.Entity.Internal.Linq.InternalSet`1.InitializeUnderlyingTypes(EntitySetTypePair pair)
at System.Data.Entity.Internal.Linq.InternalSet`1.get_InternalContext()
at System.Data.Entity.Infrastructure.DbQuery`1.System.Linq.IQueryable.get_Provider()
at System.Linq.Queryable.FirstOrDefault[TSource](IQueryable`1 source, Expression`1 predicate)
The stack trace shows "FirstOrDefault" because OrderRepo.First internally calls DbSet.FirstOrDefault, like so:
public virtual T First(Expression<Func<T, bool>> query)
{
return _dbSet.FirstOrDefault(query);
}
I'm stumped because each worker is created per http request. Each DBContext is created per engine instance so I don't know how it could be disposed when it was just created in the constructor. And this only happens on the production web server where I presume it's being called more. Any tips would be greatly appreciated.
I am working on a asp.net mvc5 project and I want to implement chatroom with signalR So I got Microsoft.Aspnet.SignalR from nuget and I used a SignalR Hub class for hub and now i want to override OnDisconnected() method .but I get error
'ChatHub.OnDisconnected()': no suitable method found to override
I dont know how to solve this problem please help me
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Microsoft.AspNet.SignalR;
using System.Collections.Concurrent;
using System.Threading.Tasks;
namespace WebApplication3.Hubs
{
public class ChatHub : Hub
{
public void Hello()
{
Clients.All.hello();
}
static ConcurrentDictionary<string, string> dic = new ConcurrentDictionary<string, string>();
public void Send(string name, string message)
{
Clients.All.broadcastMessage(name, message);
}
public void SendToSpecific(string name, string message, string to)
{
// Call the broadcastMessage method to update clients.
Clients.Caller.broadcastMessage(name, message);
Clients.Client(dic[to]).broadcastMessage(name, message);
}
public void Notify(string name, string id)
{
if (dic.ContainsKey(name))
{
Clients.Caller.differentName();
}
else
{
dic.TryAdd(name, id);
foreach (KeyValuePair<String, String> entry in dic)
{
Clients.Caller.online(entry.Key);
}
Clients.Others.enters(name);
}
}
public override Task OnDisconnected()
{
var name = dic.FirstOrDefault(x => x.Value == Context.ConnectionId.ToString());
string s;
dic.TryRemove(name.Key, out s);
return Clients.All.disconnected(name.Key);
}
}
}
For SignalR 2.1.0+, you need to use OnDisconected(bool stopCalled).
// Microsoft.AspNet.SignalR.Hub
// Summary:
// Called when a connection disconnects from this hub gracefully or due to a timeout.
//
// Parameters:
// stopCalled:
// true, if stop was called on the client closing the connection gracefully; false,
// if the connection has been lost for longer than the Microsoft.AspNet.SignalR.Configuration.IConfigurationManager.DisconnectTimeout.
// Timeouts can be caused by clients reconnecting to another SignalR server in scaleout.
//
// Returns:
// A System.Threading.Tasks.Task
public virtual Task OnDisconnected(bool stopCalled);
My project has a need for realtime user interaction and I think SignalR will solve my need. I'm technically on a SharePoint 2007 project, although I'm exclusively in application pages and thus barely use SharePoint at all. Regardless, I'm stuck in a 2.0 framework app pool in IIS.
My first approach was to try to create a 4.0 application as a sub-site. Unfortunately, that failed miserably. That approach works in a non-SharePoint world, but it appears that SharePoint has hijacked too much of the request pipeline for this approach to work for me.
So now I'm going down the path of creating a separate IIS Site that's 4.0 and using IIS rewrite rules to fake my app into thinking a particular subdirectory (/realtime/) is local and not a separate site so that I don't have to deal with cross domain request issues. The problem is I can't get IIS rewrite rules to rewrite to another http host (e.g. http://www.mySharepoint.com/_layouts/MySite/realtime/Hello.aspx to http://realtime.mySharePoint.com/Hello.aspx).
Any help with approach #1 or approach #2 or any alternative ideas would be greatly appreciated.
Here is what I did... Web App with signalR .net4.0, then your SharePoint Web App .net 2.
Add this to the global.asax in your Signalr project
RouteTable.Routes.MapHttpHandlerRoute("spproxy","spproxy/{*operation}", new SharePointRProxyHandler());
If you want to raise an event from SharePoint you can do a http POST to this new route URL for example
http://localhost:38262/spproxy
It will pass any posted data onto the httphandler below, that will then broadcast it to your clients.
Here is the code for MapHttpHandlerRoute
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace System.Web.Routing
{
public class HttpHandlerRoute : IRouteHandler
{
private String _virtualPath = null;
private IHttpHandler _handler = null;
public HttpHandlerRoute(String virtualPath)
{
_virtualPath = virtualPath;
}
public HttpHandlerRoute(IHttpHandler handler)
{
_handler = handler;
}
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
IHttpHandler result;
if (_handler == null)
{
result = (IHttpHandler)System.Web.Compilation.BuildManager.CreateInstanceFromVirtualPath(_virtualPath, typeof(IHttpHandler));
}
else
{
result = _handler;
}
return result;
}
}
public static class RoutingExtensions
{
public static void MapHttpHandlerRoute(this RouteCollection routes, string routeName, string routeUrl, string physicalFile, RouteValueDictionary defaults = null, RouteValueDictionary constraints = null)
{
var route = new Route(routeUrl, defaults, constraints, new HttpHandlerRoute(physicalFile));
RouteTable.Routes.Add(routeName, route);
}
public static void MapHttpHandlerRoute(this RouteCollection routes, string routeName, string routeUrl, IHttpHandler handler, RouteValueDictionary defaults = null, RouteValueDictionary constraints = null)
{
var route = new Route(routeUrl, defaults, constraints, new HttpHandlerRoute(handler));
RouteTable.Routes.Add(routeName, route);
}
}
}
Or you could just post directly to a httphandler and get the handler to do a connection.Broadcast
namespace SharePointRProxy
{
/// <summary>
/// Summary description for SharePointRProxyHandler
/// </summary>
public class SharePointRProxyHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "text/plain";
IConnectionManager connectonManager = AspNetHost.DependencyResolver.Resolve<IConnectionManager>();
IConnection connection = connectonManager.GetConnection<MyConnection>();
object payload = null; //Add payload here 'context.Request.Params["data"] ?'
JavaScriptSerializer jss = new JavaScriptSerializer();
var payloadJSON = jss.Serialize(payload);
connection.Broadcast(payloadJSON);
}
public bool IsReusable
{
get
{
return false;
}
}
}
}
You could also use either an event handler calling a .net 4.0 web service or an http handler to grab requests from SharePoint and pass them over to a .net 4.0 application running your signalr code.
You can see an example of using an http handler here: http://spmatt.wordpress.com/2012/04/12/harnessing-signalr-in-sharepoint/