My plugin is ran when a value is changed on a form.
I am trying to pass in the 'name' attribute of the target entity as a variable.
However, the name is showing as blank in my trace log.
The attribute logical name is correct and there is a value in the form.
Also, the Id is passing through OK so the target entity is working.
I tried defining 'entityName' as both var and string but neither work.
Can anyone advise what I am doing wrong below?
public void Execute(IServiceProvider serviceProvider)
{
ITracingService tracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService));
IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);
Entity entityTarget = context.InputParameters["Target"] as Entity;
Guid guid = entityTarget.Id;
var entityName = entityTarget.GetAttributeValue<string>("new_name");
tracingService.Trace("entityName = " + entityName);
}
When attribute new_name is not modified, it will not be part of the Attributes collection in the "Target" entity.
Attributes that have not been modified must be retrieved from a pre entity image.
Entity entityTarget = context.InputParameters["Target"] as Entity;
Entity originalEntity = context.PreEntityImages.Values.FirstOrDefault();
if (!entityTarget.TryGetAttributeValue<string>("new_name", out string entityName))
entityName = originalEntity.GetAttributeValue<string>("new_name");
tracingService.Trace("entityName = " + entityName);
Remember you need to register this pre image on the update plugin step and in the image attribute new_name must be selected.
Related
I am working with Dynamics CRM annotation, i developed an external application that use the organization service in order to create new annotation linked to a custom entity and linked to the user based on the user id, by set the CallerId in the organization Service and by set the field "CreatedBy" in the annotation object on create.
The problem is that the annotation is sometimes the value of "Created by" is not correct and it randomly set it by another user.
below used code:
Guid callerID = new Guid(HttpContext.Current.Request.QueryString["CallerId"].ToString());
CrmServiceClient connection = new CrmServiceClient(connectionString);
OrganizationServices = connection.OrganizationServiceProxy;
OrganizationServices.CallerId = new Guid(callerID);
.
.
.
Entity Annotation = new Entity("annotation");
Annotation.Attributes["objectid"] = new EntityReference("RelatedEntityLogical", RelatedEntity.Id);
Annotation.Attributes["objecttypecode"] = RelatedEntity.LogicalName;
.
.
.
Annotation.Attributes["createdby"] = new EntityReference("systemuser", callerID);
OrganizationServices.Create(Annotation);
Any ideas?
Thanks
Maybe try this:
Do not set the createdby attribute - i.e. remove this line:
Annotation.Attributes["createdby"] = new EntityReference("systemuser", callerID);
Set the CallerId directly on the instance of CrmServiceClient:
connection.CallerId = new Guid(callerID);
Invoke Create directly from the instance of CrmServiceClient:
connection.Create(Annotation);
This is truly one of the strangest issues I've run into.
I have a Web API which uses EF. I have an audit table which takes an ApplicationUser. I create the new object, add it to the collection and then call SaveChangesAsync(). The weird part is, I get "User name MyUserName is already taken." error.
using (var context = new ApplicationDbContext())
{
var user = context.Users.Single<ApplicationUser>(x => x.UserName == model.UserName);
var sid = context.SessionIds.FirstOrDefault(x => x.Id == model.SessionId);
var audit = new Audit
{
Data = model.Data,
User = user,
IpAddress = Helper.GetClientIp(Request),
Session = sid != null ? sid : ItsMyChance.Entities.Entities.SessionId.Create(scoreModel.UserName, scoreModel.GameId)
};
context.Audits.Add(audit);
await context.SaveChangesAsync();
}
Update
This code has been working for years. The difference is I upgrade from .NET 4.5 to .NET 4.61
Update 2
I also tried the following but still receive the same error
[ForeignKey("User")]
public string UserId { get; set; }
public ApplicationUser User { get; set; }
Update 3
Trying to track this issue down I call
var entries = context.ChangeTracker.Entries();
It returns several entries, 1 for each object, including User. User shows Added and another as Unchanged. I can't figure out how this is happening.
In addition, I added the following before making any changes but there's no effect.
context.Configuration.AutoDetectChangesEnabled = false;
Since You are adding the complete user object in Audit , so SaveChangesAsync will save a new Entry for Audit and User also and since a user with same username already exists that's why you are getting this error. What you should do is just assign just the UserId (Whatever is referral key in Audit table for User) in Audit object
var audit = new Audit
{
Data = model.Data,
UserId = user.Id,
IpAddress = Helper.GetClientIp(Request),
Session = sid != null ? sid : ItsMyChance.Entities.Entities.SessionId.Create(scoreModel.UserName, scoreModel.GameId)
};
I have this code to update the email address using Identity framework's UserManager:
UserManager<ApplicationUser> UserManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(ApplicationDbContext.Create()));
ApplicationUser user = await UserManager.FindByNameAsync(username);
IdentityResult result = null;
if (user != null)
{
user.Email="foo";
result = await UserManager.UpdateAsync(user);
}
however whenever I try to run it, it throws this error:
System.InvalidOperationException: Attaching an entity of type 'ApplicationUser' failed because another entity of the same type already has the same primary key value. This can happen when using the 'Attach' method or setting the state of an entity to 'Unchanged' or 'Modified' if any entities in the graph have conflicting key values. This may be because some entities are new and have not yet received database-generated key values. In this case use the 'Add' method or the 'Added' entity state to track the graph and then set the state of non-new entities to 'Unchanged' or 'Modified' as appropriate.
For the most part I'm just using out-of-the-box identity framework as it appears in the default MVC template in Visual Studio. No custom user stores or anything. I'm not sure what I'm doing wrong here.
Try changing the state of your entity to Modified like this:
var context = ApplicationDbContext.Create();
UserManager<ApplicationUser> UserManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(context));
ApplicationUser user = await UserManager.FindByNameAsync(username);
IdentityResult result = null;
if (user != null)
{
context.Entry(user).State = System.Data.EntityState.Modified;
user.Email="foo";
result = await UserManager.UpdateAsync(user);
}
Do you know how I could update an entity in WCF Data Services with OData syntax without the key property of the entity.
For example, an entity:
public class Product
{
[Key]
public int Id { get; set; }
public string Reference { get; set; }
}
I would like to make this request:
PUT myservice.svc/Product('REFXX')
with 'REFXXX' corresponding do the Reference property (which is unique).
Any idea?
Currently there is no way to do this - the issue is if you pass the following request to the server (PUT myservice.svc/Product('REFXX')), how will the server know that REFXX is the value for the unique property and not the key property.
If you really want to update the client based on the unique property, make sure the server exposes that unique property as key.
Thanks
Pratik
I wrote a IDispatchMessageInspector, parse the url and replace the match element in the request parameter with a correct syntax and the real key. I know that the key is not the real "Key" with a specific user agent or with the syntax Service.svc/Entity(SecondaryKey=value), which is used normally for multiple pk's.
so in the method AfterReceiveRequest the process is:
parse the url Service.svc/Entity(SecondaryKey=value)
get the key value of the entity (by building a dynamic linq expression)
change the match element of the request with Service.svc/Entity(PKValue)
public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
{
if (request.Properties.ContainsKey("UriTemplateMatchResults") && HttpContext.Current != null)
{
//get match for current request
UriTemplateMatch match = (UriTemplateMatch)request.Properties["UriTemplateMatchResults"];
Utils.ODataBasicUriParser uriParser = new Utils.ODataBasicUriParser(match.RequestUri.PathAndQuery);
//verify if this is a SecondaryKey request
if (uriParser.IsEntityQuery && uriParser.IsSecondaryKeyQuery)
{
//TODO this syntax is also used for entities with multiple pk's, test it
//get a new data context
//TODO see if this can be improved, avoid two datacontext for one request
DataContext ctx = new DataContext();
Type outType;
//get entity type name from the service name
string entityName = DataContext.GetEntityNameByServiceName(uriParser.EntityServiceName);
//get the pk for the entity
string id = ctx.GetEntityId(entityName, uriParser.EntityKey, uriParser.EntityId, out outType);
//verify if the pk has been found or cancel this to continue with standart request process
if (string.IsNullOrEmpty(id))
{
Trace.TraceWarning(string.Format("Key property not found for the the entity:{0}, with secondaryKeyName:{1} and secondaryKeyValue:{2}",
entityName, uriParser.EntityKey, uriParser.EntityId));
return System.Net.HttpStatusCode.NotFound;
}
//in odata syntax quotes are required for string values, nothing for numbers
string quote = outType.FullName == typeof(Int32).FullName || outType.FullName == typeof(Int64).FullName ? string.Empty : "'";
//build the new standart resource uri with the primary key
var newUri = new Uri(string.Format("{0}/{1}({2}{3}{2})", match.BaseUri.ToString(), uriParser.EntityServiceName, quote, id));
//create a new match to replace in the current request, with the new Uri
UriTemplateMatch newMatch = NewMatch(match, newUri);
//set request values
request.Properties["UriTemplateMatchResults"] = newMatch;
request.Headers.To = newUri;
request.Properties.Via = newUri;
}
}
return null;
}
UriTemplateMatch NewMatch(UriTemplateMatch match, Uri newUri)
{
UriTemplateMatch newMatch = new UriTemplateMatch();
newMatch.RequestUri = newUri;
newMatch.Data = match.Data;
newMatch.BaseUri = match.BaseUri;
return newMatch;
}
works for my current needs
I'm making a plugin that triggers on the create message of a custom activity SMS.
These plugin will send the actual sms using a third party sms service provider.
Therefore i need to get the mobilephone numbers for every contact in the "To" field of the SMS activity. This is a field of type: PartyList.
I'm currently using the following code:
EntityCollection Recipients;
Entity entity = (Entity) context.InputParameters["Target"];
IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);
Content = entity.GetAttributeValue<String>("subject");
Recipients = entity.GetAttributeValue<EntityCollection>("to");
for (int i = 0; i < Recipients.Entities.Count; i++)
{
Entity ent= Recipients[i];
string number = ent["MobilePhone"].ToString();
}
But this is not working, i think the ent variable contains no attributes.
I've tried coding with ActivityParty also but not luck either.
I hope someone of you can help me with this one.
Thanks!
Recipients is a list of ActivityParty, not of contacts, accounts, ... .
Therefore you have to read its PartyId
EntityReference partyId = ent.GetAttributeValue<EntityReference>("partyid");
With this information you have to look for the record which is referecend with this partyID. It could be a contact, an account, a systemuser, ...
You'll get this information trough
var partyType = partyId.LogicalName;
Then you could retrieve the record this record in order to read the number.
Here's is how I finally did it:
EntityCollection Recipients;
Entity entity = (Entity) context.InputParameters["Target"];
IOrganizationServiceFactory serviceFactory
= (IOrganizationServiceFactory)serviceProvider.GetService(
typeof(IOrganizationServiceFactory));
IOrganizationService service = serviceFactory
.CreateOrganizationService(context.UserId);
Content = entity.GetAttributeValue<String>("subject");
Recipients = entity.GetAttributeValue<EntityCollection>("to");
for (int i = 0; i < Recipients.Entities.Count; i++)
{
ActivityParty ap = Recipients[i].ToEntity<ActivityParty>();
String contactid = ap.PartyId.Id.ToString();
Contact c = (Contact) service.Retrieve(
Contact.EntityLogicalName,
ap.PartyId.Id,
new ColumnSet(new string[]{ "mobilephone" }));
String mobilephone = c.MobilePhone;
...
}