I am trying to make a simple plugin for MS Dynamics CRM 4.0 where send data of a salesorder in a SOAP message on the update of the order.
The strange thing is that I get this error every other time i try to save /(execute the plugin).
So when I update (any field) of a salesorder and then save I get the error:
The given key was not present in the dictionary.
When I save again right away after that(without even changing anything in between the two saves) it executes correctly and gives me all data I want. It is really every time the same thing: first save: error, second save: execute correctly.
Any ideas what this could be?
This is the first part of my code; where it actually gets the dataset of the salesorder in this case:
public class CompleteOrderPlugin : IPlugin
{
public void Execute(IPluginExecutionContext context)
{
DynamicEntity entity = null;
if (context.InputParameters.Properties.Contains(ParameterName.Target) &&
context.InputParameters.Properties[ParameterName.Target] is DynamicEntity)
{
entity = (DynamicEntity)context.InputParameters[ParameterName.Target];
if (entity.Name != EntityName.salesorder.ToString()) { return; }
}
else
{
return;
}
The rest is where I use values from attributes to fill my own variables.
I fixed this by first making a Post Image of the salesorder in the plugin regsitration tool and then using the values in the Post Image instead of the ones comming directly from the salesorder. This I did because on a update you get only the values that actually changed.
Related
When we pass our DbContext an object whose values have not changed, and try to perform an Update we get a 500 internal server error.
A user may open a dialog box to edit a record, change a value, change it back and then send the record to the database. Also we provide a Backup and Restore function and when the records are restored, some of them will not have changed since the backup was performed.
I was under the impression that a PUT would delete and re-create the record so I didn't feel there would be a problem.
For example, having checked that the Activity exists my ActivityController is as follows:
var activityEntityFromRepo = _activityRepository.GetActivity(id);
// Map(source object (Dto), destination object (Entity))
_mapper.Map(activityForUpdateDto, activityEntityFromRepo);
_activityRepository.UpdateActivity(activityEntityFromRepo);
// Save the updated Activity entity, added to the DbContext, to the SQL database.
if (await _activityRepository.SaveChangesAsync())
{
var activityFromRepo = _activityRepository.GetActivity(id);
if (activityFromRepo == null)
{
return NotFound("Updated Activity could not be found");
}
var activity = _mapper.Map<ActivityDto>(activityFromRepo);
return Ok(activity);
}
else
{
// The save failed.
var message = $"Could not update Activity {id} in the database.";
_logger.LogWarning(message);
throw new Exception(message);
};
My ActivityRepository is as follows:
public void UpdateActivity(Activity activity)
{
_context.Activities.Update(activity);
}
If any of the fields have changed then we don't get the error. Do I have to check every record for equality before the PUT? It seems unnecessary.
Perhaps I have missed something obvious. Any suggestions very welcome.
There is a lot of code missing here.
In your code you call your SaveChangesAsync (not the EF SaveChangesAsync).
Probably (but there is not the code to be sure) your SaveChangesAsync is something that returns false if there is an exception (and is not a good pattern because you "loose" the exception info) or if DbSet.SaveChangesAsync returns 0.
I think (but there is a lot of missing code) that this is your case. If you don't make any changes, SaveChangesAsync returns 0.
EDIT
The System.Exception is raised by your code (last line). EF never throws System.Exception.
Due to a strange behavior in my application, i am forced to reload the designer before calling WorkflowInvoker.Invoke on it.
wd.Flush();
SaveXamlFile(currentXamlPath, wd.Text);
I just flush the content, and write the wd.Text to a file.
//cleanup the previous designer
if (wd != null)
{
wd.ModelChanged -= new EventHandler(Designer_ModelChanged);
}
//designer
wd = new WorkflowDesigner();
designerArea.Child = wd.View;
this.DebuggerService = this.wd.DebugManagerView;
//property grid
propertiesArea.Child = wd.PropertyInspectorView;
//event handler
wd.ModelChanged += new EventHandler(Designer_ModelChanged);
//error service
wd.Context.Services.Publish<IValidationErrorService>(errorService);
wd.Context.Items.Subscribe<Selection>(OnItemSelected);
I then recreate a new instance of the WorkflowDesigner and load the previously saved file.
wd.Load(currentXamlPath);
I call WorkflowInvoker.Invoke and inside my custom activity which derives from CodeActivity i am taking it's name:
OK, fine until now, i have a 1.2 Id there.
I want to update some of the fields of this Activity via its ModelItem in order to display them in the GUI right away.
IEnumerable<ModelItem> activityCollection = currentWorkflow.Find(currentWorkflow.Root, typeof(Activity));
But here comes the issue:
I can't find that my Activity id there. Is now transformed from 1.2 to 2. Why is this happening?
I've tried to send a this reference from my Activity Execute method and searched it by ref but all i get is nulls.
ModelItem temp = activityCollection.FirstOrDefault((m) => (m.GetCurrentValue() == a));
I am sure i am missing something here, but i can't figure out what is it.
I found a workaround on this :
On my custom activities i am adding a Guid property and I override CacheMetadata:
public Guid unique { get; set; }
protected override void CacheMetadata(CodeActivityMetadata metadata)
{
if (unique.ToString() == "00000000-0000-0000-0000-000000000000")
unique = Guid.NewGuid();
}
When i drag the activity on the designer, the unique id is generated. I make sure that this portion of code is called only once.
Why is that?
Because after a call like this,
IEnumerable<ModelItem> activityCollection = currentWorkflow.Find(currentWorkflow.Root, typeof(Activity));
each model in the activity collection contains that property ( unique of type Guid ) with the value of the first assignment made in CacheMetadata. I can't explain this behavior, i've just taken it into consideration.
Who calls again that CacheMetadata ? something like this :
Activity root = ActivityXamlServices.Load(currentXamlPath);
WorkflowInspectionServices.CacheMetadata(root);
And so, the Guid is changed and its utility is gone.
This way, i am able to get the ModelItem for my custom activity and update some of its properties which are immediately displayed in the GUI.
I am not sure I am even explaining this right since I don't know the correct terms. I am also not sure if I am using EF 4.0 or 4.1.
I have an Entity model generated from my DB. My DB has a one-to-many relationship between a Survey and an Answer. My data layer gets a Survey object from the model (with an Answers sub-collection) and sends it over a Web Service to the front-end, where it is updated (both the survey data and the associated answers may get updated). I gather this is called a 'detached' object from the Entity model?
Anyway, after the editing, it is sent back to the data layer to get saved, and here is the problem. I tired to do the following 3 options, based on answers to other question here at SO:
public bool updateSurvey(Survey surv)
{
Survey target = entity.Surveys.FirstOrDefault(p => p.id == surv.id);
if (target == null)
return false;
//first try - exception at commented line
target.ownerID = surv.ownerID;
target.question = surv.question;
target.title = surv.title;
//target.Answers = surv.Answers;
//second try - replace commented line above with this - different exception
target.Answers.Clear();
foreach (var item in surv.Answers)
{
target.Answers.Add(item);
}
//third try - replace whole above block with this line
entity.Surveys.ApplyCurrentValues(surv);
try
{
entity.SaveChanges();lse;
}
catch (Exception)
{
return false;
}
return true;
}
My third try above finally didn't throw an exception, but the updated answers were not saved (I don't know about the other properties of surv, since in this case I didn't change them).
So, bottom line - how do I save this Survey and all attached Answers? Do I have to manually loop through the answers and save each one? What about atomicity in this case?
I have a VSIX and a associated MEF DLL using IModelConversionExtension Class as per the documentation, and a pkgdef file setting up .foo as an extension to invoke the EF Designer.
[PartCreationPolicy(CreationPolicy.Shared)]
[Export(typeof(IModelConversionExtension))]
[ModelFileExtension(".foo")]
public class MyConversionCallback : IModelConversionExtension
{
public void OnAfterFileLoaded(ModelConversionExtensionContext context)
{
//How does this get called?
return;
}
public void OnBeforeFileSaved(ModelConversionExtensionContext context)
{
//How does this get called?
return;
}
}
[$RootKey$\Editors\{c99aea30-8e36-4515-b76f-496f5a48a6aa}\Extensions]
"foo"=dword:00000032
[$RootKey$\Projects]
[$RootKey$\Projects\{F184B08F-C81C-45F6-A57F-5ABD9991F28F}]
[$RootKey$\Projects\{F184B08F-C81C-45F6-A57F-5ABD9991F28F}\RelatedFiles]
[$RootKey$\Projects\{F184B08F-C81C-45F6-A57F-5ABD9991F28F}\RelatedFiles\.foo]
".diagram"=dword:00000002
[$RootKey$\Projects]
[$RootKey$\Projects\{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}]
[$RootKey$\Projects\{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\RelatedFiles]
[$RootKey$\Projects\{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\RelatedFiles\.foo]
".diagram"=dword:00000002
I can get both the similar Transform and Generation MEF Classes to work fine.
And my Model1.foo does invoke the EF Designer, but
1. OnAfterFileLoaded and OnBeforeFileSaved never fire, and
2. I get an error message when I try to save Model1.foo, which says to see errors in the Error List but there are none.
What am not doing to get this to work.
Thanks
OnAfterFileLoaded is supposed to be invoked if you load a file whose extension is different than edmx and the IEntityDesignerConversionData.FileExtension returns a value that matches your extension. OnBeforeFileSaved works the opposite way - on save. However - I looked at code in this area today and concluded that it actually cannot work. I filed a work item for this: https://entityframework.codeplex.com/workitem/1371
Does anyone know for an example of GWT's CellTable using RequestFactory and that table is being edited? I would like to list objects in a table (each row is one object and each column is one property), be able to easily add new objects and edit. I know for Google's DynaTableRf example, but that one doesn't edit.
I searched Google and stackoverflow but wasn't able to find one. I got a bit confused with RF's context and than people also mentioned some "driver".
To demonstrate where I currently arrived, I attach code for one column:
// Create name column.
Column<PersonProxy, String> nameColumn = new Column<PersonProxy, String>(
new EditTextCell()) {
#Override
public String getValue(PersonProxy person) {
String ret = person.getName();
return ret != null ? ret : "";
}
};
nameColumn.setFieldUpdater(new FieldUpdater<PersonProxy, String>() {
#Override
public void update(int index, PersonProxy object, String value) {
PersonRequest req = FaceOrgFactory.getInstance().requestFactory().personRequest();
PersonProxy eObject = req.edit(object);
eObject.setName(value);
req.persist().using(eObject).fire();
}
});
and my code for data provider:
AsyncDataProvider<PersonProxy> personDataProvider = new AsyncDataProvider<PersonProxy>() {
#Override
protected void onRangeChanged(HasData<PersonProxy> display) {
final Range range = display.getVisibleRange();
fetch(range.getStart());
}
};
personDataProvider.addDataDisplay(personTable);
...
private void fetch(final int start) {
lastFetch = start;
requestFactory.personRequest().getPeople(start, numRows).fire(new Receiver<List<PersonProxy>>() {
#Override
public void onSuccess(List<PersonProxy> response) {
if (lastFetch != start){
return;
}
int responses = response.size();
if (start >= (personTable.getRowCount()-numRows)){
PersonProxy newP = requestFactory.personRequest().create(PersonProxy.class);
response.add(newP);
responses++;
}
personTable.setRowData(start, response);
personPager.setPageStart(start);
}
});
requestFactory.personRequest().countPersons().fire(new Receiver<Integer>() {
#Override
public void onSuccess(Integer response) {
personTable.setRowCount(response+1, true);
}
});
}
I try to insert last object a new empty object. And when user would fill it, I'd insert new one after it. But the code is not working. I says that user is "attempting" to edit a object previously edited by another RequestContext.
Dilemmas:
* am I creating too many context'es?
* how to properly insert new object into celltable, created on the client side?
* on fieldUpdater when I get an editable object - should I insert it back to table or forget about it?
Thanks for any help.
am I creating too many context'es?
Yes.
You should have one context per HTTP request (per fire()), and a context that is not fire()d is useless (only do that if you/the user change your/his mind and don't want to, e.g., save your/his changes).
You actually have only one context to remove here (see below).
Note that your approach of saving on each field change can lead to "race conditions", because a proxy can be edit()ed by at most one context at a time, and it remains attached to a context until the server responds (and once a context is fired, the proxy is frozen –read-only– also until the server responds).
(this is not true in all cases: when onConstraintViolation is called, the context and its proxies are unfrozen so you can "fix" the constraint violations and fire the context again; this should be safe because validation is done on the server-side before any service method is called).
how to properly insert new object into celltable, created on the client side?
Your code looks OK, except that you should create your proxy in the same context as the one you'll use to persist it.
on fieldUpdater when I get an editable object - should I insert it back to table or forget about it?
I'm not 100% certain but I think you should refresh the table (something like setRowData(index, Collections.singletonList(object)))
BTW, the driver people mention is probably the RequestFactoryEditorDriver from the Editor framework. It won't help you here (quite the contrary actually).