Error "The given key was not present in the dictionary" When Creating New Custom Entity on Update Event - plugins

On CRM 2013 on-premise, I'm trying to write a plugin that triggers when an update is made to a field on Quote. The plugin then creates a new custom entity "new_contract".
My plugin is successfully triggered when the update to that field is made. However I keep getting an error message "The given key was not present in the dictionary" when trying to create the new custom entity.
I'm using a "PostImage" in this code. I confirm that it's registered using the same name in Plugin Registration.
Here is the code
var targetEntity = context.GetParameterCollection<Entity>
(context.InputParameters, "Target");
if (targetEntity == null)
{throw new InvalidPluginExecutionException(OperationStatus.Failed,
"Target Entity cannot be null")}
var postImage = context.PostEntityImages["PostImage"];
if (postImage == null)
{throw new InvalidPluginExecutionException(OperationStatus.Failed,
"Post Image is required");}
var quote = context.GenerateCompositeEntity(targetEntity, postImage);
//throw new InvalidPluginExecutionException(OperationStatus.Failed, "Update is captured");
//Guid QuoteId = (Guid)quote.Attributes["quoteid"];
var serviceFactory = (IOrganizationServiceFactory)serviceProvider
.GetService(typeof(IOrganizationServiceFactory));
var service = serviceFactory.CreateOrganizationService(context.UserId);
var contractEntity = new Entity();
contractEntity = new Entity("new_contract");
if (quote.Attributes.Contains("portfolio"))
{
var quotePortfolio = (EntityReference)quote.Attributes["new_portfolio];
contractEntity[Schema.new_contract.PortfolioName] =
new EntityReference(quotePortfolio.LogicalName, quotePortfolio.Id);
}
if (quote.Attributes.Contains(Schema.Quote.QuoteName))
{
var quoteName = (string)quote.Attributes["name"];
contractEntity[Schema.new_contract.contractName] = quoteName;
}
var contractId = service.Create(contractEntity);

I think context does not contain "PostImage" attribute.You should check context to see whether it contains the attribute before getting the data.

Looking at this line in your post above:
var service = serviceFactory.CreateOrganizationService(context.UserId);
I am deducing that the type of your context variable is LocalPluginContext (since this contains the UserId value) which does not expose the images (as another answer states).
To access the images, you need to get to the Plugin Execution Context:
IPluginExecutionContext pluginContext = context.PluginExecutionContext;
Entity postImage = null;
if (pluginContext.PostEntityImages != null && pluginContext.PostEntityImages.Contains("PostImage))
{
postImage = pluginContext.PostEntityImages["PostImage"];
}

In the below code segment, you are checking for the attribute "portfolio" and using "new_portfolio". Can you correct that and let us know whether that worked.
if (quote.Attributes.Contains("portfolio"))
{
var quotePortfolio = (EntityReference)quote.Attributes["new_portfolio];
contractEntity[Schema.new_contract.PortfolioName] = new EntityReference(quotePortfolio.LogicalName, quotePortfolio.Id);
}

First, you don't say what line is throwing the exception. Put in the VS debugger and find the line that is throwing the exception.
I did see that you are trying to read from a dictionary here without first checking if the dictionary contains the key, that can be the source of this exception.
var postImage = context.PostEntityImages["PostImage"];
if (postImage == null)
throw new InvalidPluginExecutionException(OperationStatus.Failed,
"Post Image is required");
Try this:
if(!context.PostEntityImages.Contains("PostImage") ||
context.PostEntityImages["PostImage"] == null)
InvalidPluginExecutionException(OperationStatus.Failed, "Post Image is required");
var postImage = context.PostEntityImages["PostImage"];
Although, I don't think that a PostEntityImage Value will ever be null, if it passes the Contains test you don't really need the null check.

Related

Entity Framework 6: is it possible to update specific object property without getting the whole object?

I have an object with several really large string properties. In addition, it has a simple timestamp property.
What I trying to achieve is to update only timestamp property without getting the whole huge object to the server.
Eventually, I would like to use EF and to do in the most performant way something equivalent to this:
update [...]
set [...] = [...]
where [...]
Using the following, you can update a single column:
var yourEntity = new YourEntity() { Id = id, DateProp = dateTime };
using (var db = new MyEfContextName())
{
db.YourEntities.Attach(yourEntity);
db.Entry(yourEntity).Property(x => x.DateProp).IsModified = true;
db.SaveChanges();
}
OK, I managed to handle this. The solution is the same as proposed by Seany84, with the only addition of disabling validation, in order to overcome issue with required fields. Basically, I had to add the following line just before 'SaveChanges():
db.Configuration.ValidateOnSaveEnabled = false;
So, the complete solution is:
var yourEntity = new YourEntity() { Id = id, DateProp = dateTime };
using (var db = new MyEfContextName())
{
db.YourEntities.Attach(yourEntity);
db.Entry(yourEntity).Property(x => x.DateProp).IsModified = true;
db.Configuration.ValidateOnSaveEnabled = false;
db.SaveChanges();
}

MVC - Updating multiple records to database using EF

I am new to MVC. I am getting error while updating multiple records to database using EF.
I want to create a group and add members in it.
I created to list
boxes. I have loaded all members in left box.
First time, I created the Group and added members from first box to second box, and saved
the form. All data saved successfully.
While I come to edit the group, I want to add/remove some members from the group, I got
error.
I used the following code for edit mode.
if (!string.IsNullOrEmpty(groupView.GroupName) && groupView.MemberIDs.Count > 0)
{
Groups group = new Groups();
List<GroupMembers> existingMembers = null;
if (groupView.GroupID > 0)
{
group.GroupID = groupView.GroupID;
//db.Entry(group).State = EntityState.Modified;
existingMembers = db.GroupMembers.ToList();
}
else
{
group.GroupID = groupView.GroupID;
group.GroupName = groupView.GroupName;
group.IsActive = true;
db.Groups.Add(group);
}
GroupMembers groupMembers, member;
foreach (short memberId in groupView.MemberIDs)
{
groupMembers = new GroupMembers();
if (groupView.GroupID > 0)
{
//Check whether the Member already exists in the group
member = existingMembers.Where(gm => gm.GroupID == groupView.GroupID && gm.MemberID == memberId).FirstOrDefault();
if (member != null)
{
groupMembers.GroupMemberID = member.GroupMemberID;
db.Entry(groupMembers).State = EntityState.Modified;
}
else
groupMembers.GroupMemberID = 0;
}
groupMembers.GroupID = group.GroupID;
groupMembers.MemberID = memberId;
groupMembers.IsActive = Convert.ToBoolean(groupView.IsActive);
if (groupMembers.GroupMemberID == 0)
db.GroupMembers.Add(groupMembers);
}
db.SaveChanges();
At runtime I received the following exception:
An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key
If I removed the following code from the above coding, duplicate data inserted in DB.
db.Entry(group).State = EntityState.Modified;
I am new to MVC, EF, as per my knowledge I wrote the above coding. I searched and checked some questions with the error information, but I am not able to understand clearly. So I put my coding. Please guide me to resolve this issue.
Thanks in advance.

How to force the Plugin on Post-Operation to Submit?

I have a plugin in post-operation witch need to create a folder on sharepoint via webservice, to do that, my plugin calls a webservice to execute a FechXML to get the info from the entity, but the problem is that entity still not exist, and it give me Null.
How do i force the plugin to submit/save the data to my FechXml to work?
PLUGIN CODE:
try
{
Entity entity;
if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)
{
entity = (Entity)context.InputParameters["Target"];
if (entity.LogicalName != "fcg_processos")
{
throw new InvalidPluginExecutionException("Ocorreu um erro no PlugIn Create Folder.");
}
}
else
{
throw new InvalidPluginExecutionException("Ocorreu um erro no PlugIn Create Folder.");
}
processosid = (Guid)((Entity)context.InputParameters["Target"])["fcg_processosid"];
string processoid2 = processosid.ToString();
PluginSharepointProcessos.ServiceReference.PrxActivityResult result = log.CreateFolderSP("Processo", processoid2);
string resultado = result.xmlContent;
if (result.retCode > 0)
{
throw new InvalidPluginExecutionException("Ocorreu um erro na criação do Folder do Processo.");
}
WEBSERVICE CODE:
{
//WEBSERVICE TO CALL XML FROM ENTITY
PrxActivityResult Processo = ProcessoFetch2("", "", guid);
string stxml;
stxml = Processo.XmlContent;
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(stxml);
XmlNodeList nodeList = xmlDoc.SelectNodes("resultset/result");
List<string[]> lista = new List<string[]>();
string[] strs = new string[7];
if (nodeList.Count != 0)//verificar o numero de registos
{
foreach (XmlNode xmlnode in nodeList)
{
if (xmlnode.SelectSingleNode("//fcg_numero") != null)
strs[2] = xmlnode.SelectSingleNode("//fcg_numero").InnerText;
else
strs[2] = "";
if (xmlnode.SelectSingleNode("//Concurso.fcg_numero") != null)
strs[3] = xmlnode.SelectSingleNode("//Concurso.fcg_numero").InnerText;
else
strs[3] = "";
}
}
IwsspClient FmwSharepoint = new IwsspClient();
PrxActivityResult folderresult = new PrxActivityResult();
List<ws.fcg.sipp.svc.ServiceReferenceSharePoint.PareChave> arrayfields = new List<ws.fcg.sipp.svc.ServiceReferenceSharePoint.PareChave>();
ws.fcg.sipp.svc.ServiceReferenceSharePoint.PareChave nprocesso = new ws.fcg.sipp.svc.ServiceReferenceSharePoint.PareChave();
nprocesso.Key = "FCG_Numero_Processo";
nprocesso.value = strs[2];
arrayfields.Add(nprocesso);
ws.fcg.sipp.svc.ServiceReferenceSharePoint.PareChave npconcurso = new ws.fcg.sipp.svc.ServiceReferenceSharePoint.PareChave();
npconcurso.Key = "FCG_Numero_Concurso";
npconcurso.value = strs[3];
arrayfields.Add(npconcurso);
ws.fcg.sipp.svc.ServiceReferenceSharePoint.PareChave npguid = new ws.fcg.sipp.svc.ServiceReferenceSharePoint.PareChave();
npguid.Key = "FCG_Guid_CRM";
npguid.value = guid;
arrayfields.Add(npguid);
folderresult = FmwSharepoint.CreateFolder("http://localhost/folder", "Processos", strs[2], arrayfields.ToArray());
res = folderresult;
}
When a plugin runs on the Post-Operation, it is still within the database transaction, and it hasn't actually been committed to the database. Any calls done with the service reference passed in as a part of the Plugin Context will be executed within the context on the database transaction and you will be able to retrieve the newly created/updated values. If you create a brand new OrganizationServiceProxy (Which I'm guessing is what you're doing), it will execute outside of the database transaction, and will not see the newly created / updated values.
As #AndyMeyers suggests in his comment (which really should be an answer IMHO), grabbing the data from the plugin context either via a pre/post image or the target is ideal since it eliminates another database call. If you're having to lookup records that may have been created by another plugin that fired earlier, you'll need to use the IOrganizationService that is included in the plugin context.
I had no option and I used this code to run webservice based on image and forget the FecthXml, as mentioned, i get all info from the Image on the post operation and send back to the WebService. Thanks, here is the code:
entity = (Entity)context.InputParameters["Target"];
concursid = (Guid)entity.Attributes["fcg_concursid"];
guid = concursid.ToString();
string npconcurs = (string)entity.Attributes["fcg_numer"];
nconcurs= npconcurs;
EntityReference nprograma = (EntityReference)entity.Attributes["fcg_unidadeorganica"];
program = nprogram.Name;
if (entity.LogicalName != "fcg_concurs")

Does entity framework compare assigned values with original to determine IsModified flag?

If I load entity object and then assign one of properties to the same value as it had before, does framework detect changes or it would set IsModified flag to true anyway ?
This is how generated code for field Name looks like:
OnNameChanging(value);
ReportPropertyChanging("Name");
_Name = StructuralObject.SetValidValue(value);
ReportPropertyChanged("Name");
OnNameChanged();
I don't know which of those events set IsModified flag for that field and for the whole entity.
It looks like things are different now (EF6). I was researching this to see if I needed to use an if statement when setting property values to see if the "new value" is different. I tested with the following and the entity is not marked as modified:
var things = dbContext.Things.AsQueryable();
var thing = things.First();
string name = thing.Name;
thing.Name = name;
var entry = dbContext.Entry(thing);
var state = entry.State;
int count = dbContext.ChangeTracker.Entries().Count(e => e.State == EntityState.Modified);
var modified = entry.Property(x => x.Name).IsModified;
Your context only keeps track if your data got modified, not if it's different.
You can do a check like this:
private void CheckIfDifferent(DbEntityEntry entry)
{
if (entry.State != EntityState.Modified)
return;
if (entry.OriginalValues.PropertyNames.Any(propertyName => !entry.OriginalValues[propertyName].Equals(entry.CurrentValues[propertyName])))
return;
(this.dbContext as IObjectContextAdapter).ObjectContext.ObjectStateManager.GetObjectStateEntry(entry.Entity).ChangeState(EntityState.Unchanged);
}
source:https://stackoverflow.com/a/13515869/1339087

Yet again Entity Framework and FK problems

I have an entity with two fk's. I've been trying to insert a record to the database without success. This are the approaches I've used:
valuePaymentBetToAdd.BetType = db.BetTypes.First(betType => betType.Id == valuePaymentBetToAdd.BetType.Id);
valuePaymentBetToAdd.Lottery = db.Lotteries.First(lotto => lotto.Id == valuePaymentBetToAdd.Lottery.Id);
In this case the second object gets assigned but when calling the SaveChanges method I get an error saying that the properties of the Lottery object were null.
valuePaymentBetToAdd.BetTypeReference.EntityKey = new EntityKey(db.DefaultContainerName + ".BetType", "Id", valuePaymentBetToAdd.BetType.Id);
valuePaymentBetToAdd.LotteryReference.EntityKey = new EntityKey(db.DefaultContainerName + ".Lottery", "Id", valuePaymentBetToAdd.Lottery.Id);
In this case I get another weird error. When the object is being added to the collection.
The object could not be added or attached because its EntityReference has an EntityKey property value that does not match the EntityKey for this object.
Am I missing something in this case?
Try setting the EntityReference like this:
valuePaymentBetToAdd.BetTypeReference.EntityKey = b.BetTypes.First(betType => betType.Id == valuePaymentBetToAdd.BetType.Id).EntityKey;
It works for me
How about creating a stub object for BetType and Lottery where you set only the Id property, and then attach those to their respective EntitySets, and then setting these objects on you Bet object, and save - something like:
Lottery lottery = new Lottery() { Id = valuePaymentBetToAdd.Lottery.Id };
BetType betType = new BetType() { Id = valuePaymentBetToAdd.BetType.Id };
MyContext.AttachTo("Lottery", lottery);
MyContext.AttachTo("BetType", betType);
valuePaymentBetToAdd.Lottery = lottery;
valuePaymentBetToAdd.BetType = betType;
MyContext.AddToBet(valuePaymentBetToAdd);
MyContext.SaveChanges();