MS Dynamics CE CRM 365 - Pre Operation Plugin - Given key was not present in the dictionary - dynamics-crm

After few years doing some other stuff I'm back to CRM business. I'm already questioning my life choices. I don't understand what is wrong here. I'm trying to create a simple plugin that will run on Incident / Case creation. It will look if description field contains a valid url and if yes then it should update first url that has been found to another field. Here's the plugin execution method.
public void Execute(IServiceProvider serviceProvider)
{
ITracingService tracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService));
IPluginExecutionContext pluginExecutionContext = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
if (pluginExecutionContext.InputParameters.Contains("Target") && pluginExecutionContext.InputParameters["Target"] is Entity)
{
Entity targetEntity = (Entity)pluginExecutionContext.InputParameters["Target"];
if (targetEntity.LogicalName != Incident.EntityLogicalName)
{
return;
}
IOrganizationServiceFactory orgServiceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
IOrganizationService orgService = orgServiceFactory.CreateOrganizationService(pluginExecutionContext.UserId);
try
{
string desc = (string)targetEntity.Attributes["description"];
string pattern = #"\b(?:https?://|www\.)[^ \f\n\r\t\v\]]+\b";
MatchCollection collection = Regex.Matches(desc, pattern);
if (collection.Count > 0)
{
throw new Exception(collection[0].Value);
}
}
catch (Exception ex)
{
tracingService.Trace("Error in CaseUrlPlugin {0}", ex.ToString());
throw ex;
}
}
}
The problem is that when I'm creating a new case (description field filled with text and url) and hit save I get "Given key was not present in the dictionary" exception as if description field is not there. When I hit ok to that error window and hit save again then description field is founded and my code throws an exception with that link.
So why is not the description field present in the first time? I don't like the idea doing this post operation because that would require another sql transaction right (to save incident again)?

Aah ffs. The problem was that when I was trying to save for the first time I used CTRL + S while my focus was still on description field because that's the last field I was filling. Now it seems that UI doesn't register that field to be filled if focus on that field and hitting CTRL + S. Hitting save icon works because that would get focus off the description field. And of course in my case hitting ok for that error window also unfocus the field thus second save works.............
Well, at least I figured it out right after posting question here. Been trying to solve this way too long.

Related

Can't update Parse Object

I've created an object previously that I'm now trying to update in an "edit" screen
The id of the object is correct (as it correctly queries earlier in the activity to update the text labels. This should save when a button is clicked.
ParseQuery<ParseObject> query = ParseQuery.getQuery("Product");
Intent i = getIntent();
String queryString = i.getStringExtra("id");
query.getInBackground(queryString, new GetCallback<ParseObject>() {
public void done(ParseObject editProduct, ParseException e) {
if (e == null) {
editProduct.put("productName", ProductName.getText().toString());
editProduct.put("ISDN", ISDN_text.getText().toString());
editProduct.put("expiry", expiry_date.getText().toString());
editProduct.put("type", spinnercategory.getSelectedItem().toString());
editProduct.put("quantity", quantity.getText().toString());
editProduct.put("username", "Admin");
editProduct.put("shoppingList", true);
editProduct.put("mainList", false);
editProduct.saveInBackground();
}
}
});
This is lifted from the Parse Android developers guide, but doesn't appear to be working. Any suggestions
Figured it out. Sort of.
editProduct.put("expiry", expiry_date.getText().toString()); is the line that's breaking it. I'm trying to pass a string to what is defined as a date at the backend, which apparently cancels the whole save operation.
Commented this out in my solution for now until I can fix the issue with the date

windows phone c# check for valid url and replace foreach item in list

I am getting a list of objects in Windows Phone, and show them in a listbox with databinding.
some image urls are not valid, so after every object is added in the list, i run the following code to check and replace, if not valid
private void CheckLinkUrl(Person p)
{
Uri filePath = new Uri(p.img_url);
string correct = p.img_url;
HttpWebRequest fileRequest = HttpWebRequest.CreateHttp(filePath);
fileRequest.Method = "HEAD";
fileRequest.BeginGetResponse(result =>
{
HttpWebRequest resultInfo = (HttpWebRequest)result.AsyncState;
HttpWebResponse response;
try
{
response = (HttpWebResponse)resultInfo.EndGetResponse(result);
}
catch (Exception e)
{
p.img_url = "http://somethingelse.com/image.jpg";
}
}, fileRequest);
}
the problem is that it is very slow, it takes sometimes 2 minutes+ to load every image (although the UI remains responsive, and everything else is displayed immediately in the listbox, apart from the images)
am I doing something wrong? can i get it to run faster?
EDIT:
I tried using the imagefailed event and replace the link, no improvement at the speed of loading the pics
What I have done to avoid this problem in my application is, I have loaded the items with a default Image, The image source is binded to a property in my result item of type ImageSource. By default it returns the default image. After processing or download completion the imagesource value changes to the new Image triggering the NotifyPropertyChanged event and hence it is automatically reflected on the UI. I hope it helps you.

How to save a record and immediately use its GUID

I'm executing some javascript from a ribbon button and what I want to do is save the record that I am creating and then immediately use its GUID for some code a bit further on. Each time I try it the GUID is coming back null even though I'm requesting it after the record has been saved. If I try the button again after I've saved it then it works, but not as I'm saving it.
Is there a way to do this?
function RibbonButton_AddProduct()
{
//Save the Record
Xrm.Page.data.entity.save();
LoadProductCreate();
}
function LoadProductCreate()
{
var serverUrl;
var errorMessage = "Context to retrieve the Server URL is not available.";
if (typeof GetGlobalContext != "undefined"){
serverUrl = GetGlobalContext().getServerUrl();
} else {
if (typeof Xrm != "undefined"){
serverUrl = Xrm.Page.context.getServerUrl();
} else {
alert(errorMessage);
return;
}
}
if (serverUrl.match(/\/$/)){
serverUrl = serverUrl.substring(0, serverUrl.length - 1);
}
var recordId = Xrm.Page.data.entity.getId();
alert(recordId);
var url = serverUrl + "/main.aspx?etc=10030&extraqs=%3f_CreateFromId%3d%"+recordId
+"%257d%26_CreateFromType%3d10029%26etc%3d10030%26"
+"pagemode%3diframe%26preloadcache%3d1345465354543&pagetype=entityrecord";
window.open(url);
}
Here’s a different approach to solving this problem.
What you are trying to do is ‘working against the system’ - you are effectively making two save buttons. In the rest of Crm when the Id is required for a ribbon button the record must first be saved. E.g. you can’t use the dialog or workflow buttons on an unsaved record, you also can’t 'add new/existing' to an unsaved record.
So my solution would be to disable the button on unsaved forms, force the user to save the record manually and then allow them to use your button - this is the way Crm is meant to be used, and is the way the rest of Crm will work.
You should not work against the system, you should work with it, you have a product to customise and extend – not change.
If this doesn’t meet your requirement I would suggest uses Greg’s suggestion (1) of having flags, though it sounds a bit messy - but then this is a requirement that inherently is.
You could try one of two things:
Add a hidden boolean attribute to your form(e.g. "new_launchProductCreate"), set it in code prior to save and then read it onLoad.
Instead of setting the value prior to create (and therefore potentially commiting it to the database), you could create a plugin registered against the "Create" step of your record that injects a boolean value into the Entity.Attributes collection as the record is returned to the user. This would prevent the value persisting into the database and running every time your form loads.
You can instead use AJAX to reset the value as you launch your onLoad code so that it doesn't trigger on every form load
Assign the record guid manually, use AJAX to save your record, pop your new window using th enew guid and then reload your original form (so that the form is no longer in an "unsaved" state).
At the risk of being proven wrong as I cannot verify this right away... you will need to save and then reload the page.
The value stored in Xrm.Page.data.entity.getId() is set when the page is loaded/initialised and hence won't be updated when you access it after you have called Save().
It is also why it does work when you reload the page.
Perhaps you could call save and then reload the window adding a querystring variable of your own, to indicate that this event has just occurred?
e.g.
function DoSomething() {
//do your stuff
Xrm.Page.data.entity.save();
//something like - sure someone can do better!
window.location = window.location.href + '&foo=bar';
}
and then register something like this onFormLoad
function OnLoad() {
var queryStringParms = Xrm.Page.context.getQueryStringParameters();
//test to see if your query string param exists here
for (var i in queryStringParams) {
//if you find query string, do extra processing here
}
}

SelectedItem must always be set to a valid value. Windows Phone Local Database

I am using the local database example taht Microsoft created.
I can add items to the list, and delete them. But I now want to select the items and get the text of the item and use that in the next page.
This is the select changed event:
private void allToDoItemsListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
NavigationService.Navigate(new Uri("/LiveTimes.xaml?selectedItem=" + allToDoItemsListBox.SelectedIndex, UriKind.Relative));
// string urlWIthData = string.Format("/LiveTimes.xaml?name={0}", " ");
// this.NavigationService.Navigate(new Uri(urlWIthData, UriKind.Relative));
}
Then this is the on page load on the other page.
string selectedIndex = "";
if (NavigationContext.QueryString.TryGetValue("selectedItem", out selectedIndex))
{
int index = int.Parse(selectedIndex);
DataContext = App.ViewModel.HomeToDoItems[index];
}
Then when i use this, the error is on the DataContext line.
Whats the solution?
There is no problem in the above code that you have shown, The actual problem may be in the way how you defined your ViewModel and HomeToDoItems . It helps us to solve your problem, if you can show some of that code.
Also before setting your data to DataContext, try the following steps:
First, make sure you are getting the valid selectedIndex.
var tempData = App.ViewModel.HomeToDoItems[index];
DataContext = tempData;
And then insert a break point at tempData to check whether you are getting the expected data.
This answer may not solve your problem, but guide you in identifying the actual problem.

Can I switch use of 'entities.SingleOrDefault' ==> 'entities.Find' without hazards?

In my WCF service's business logic, most of the places when I need to locate an entity, I use this syntax:
public void UpdateUser(Guid userId, String notes)
{
using (ProjEntities entities = new ProjEntities())
{
User currUser = entities.SingleOrDefault(us => us.Id == userId);
if (currUser == null)
throw new Exception("User with ID " + userId + " was not found");
}
}
I have recentely discovered that the DbContext has the Find method, and I understand I can now do this:
public void UpdateUser(Guid userId, String notes)
{
using (ProjEntities entities = new ProjEntities())
{
User currUser = entities.Find(userId);
if (currUser == null)
throw new Exception("User with ID " + userId + " was not found");
}
}
Note : the 'userId' property is the primary key for the table.
I read that when using Find method entity framework checks first to see if the entity is already in the local memory, and if so - brings it from there. Otherwise - a trip is made to the database (vs. SingleOrDefault which always makes a trip to the database).
I was wondering if I now will convert all my uses of SingleOrDefault to Find is there any potential of danger?
Is there a chance I could get some old data that has not been updated if I use Find and it fetches the data from memory instead of the database?
What happens if I have the user in memory, and someone changed the user in the database - won't it be a problem if I always use now this 'memory' replica instead of always fetching the latest updated one from the database?
Is there a chance I could get some old data that has not been updated
if I use Find and it fetches the data from memory instead of the
database?
I think you have sort of answered your own question here. Yes, there is a chance that using Find you could end up having an entity returned that is out of sync with your database because your context has a local copy.
There isn't much more anyone can tell you without knowing more about your specific application; do you keep a context alive for a long time or do you open it, do your updates and close it? obviously, the longer you keep your context around the more susceptible you are to retrieving an up to date entity.
I can think of two strategies for dealing with this. The first is outlined above; open your context, do what you need and then dispose of it:
using (var ctx = new MyContext())
{
var entity = ctx.EntitySet.Find(123);
// Do something with your entity here...
ctx.SaveChanges();
}
Secondly, you could retrieve the DbEntityEntry for your entity and use the GetDatabaseValues method to update it with the values from the database. Something like this:
var entity = ctx.EntitySet.Find(123);
// This could be a cached version so ensure it is up to date.
var entry = ctx.Entry(entity);
entry.OriginalValues.SetValues(entry.GetDatabaseValues());

Resources