I have looked around a lot of MS Dynamics CRM blogs and SO questions and tried all the solutions but they haven't worked.
The problem I am facing is as follows: I am trying to loop through an excel file with company names and company type. I then find company with matching names in CRM and set values for some custom fields depending on the company type in excel. When I do this though code for a single company it works fine however when I try to run a loop for a large number of companies I am constantly getting:
SecLib::CrmCheckPrivilege failed. Returned hr = -2147220943 on UserId: xxxxxx and PrivilegeType: Read.
The user in question has all privilleges and is an admin user with Read/Write CAL. Also like I pointed out I am able to do the updates for a single record. But when run as a loop that very same record throws an error.
I have been unable to find a solution for this so any help would be much appreciated. I am using MS Dynamics CRM online.
Here's my code:
while (!parser.EndOfData)
{
counter++;
if (counter >= 20)
{
try
{
counter = 0;
context.SaveChanges();
}
catch (Exception exc)
{
while (exc != null)
{
System.Diagnostics.Debug.WriteLine("ERROR: " + exc.Message);
exc = exc.InnerException;
}
continue;
}
}
//Processing row
string[] fields = parser.ReadFields();
foreach (string field in fields)
{
//TODO: Process field
company.Add(fields[0]);
category.Add(fields[1]);
var currAccs = context.CreateQuery<Account>().Where(x => x.Name.Contains(fields[0])).ToList();
if (currAccs != null)
{
foreach (var currAcc in currAccs)
{
System.Diagnostics.Debug.WriteLine("Processing: " + currAcc.Name);
currAcc.cir_MediaOwner = false;
currAcc.cir_Agency = false;
currAcc.cir_AdvertiserBrand = false;
if (fields[1].Contains("MO"))
{
currAcc.cir_MediaOwner = true;
}
if (fields[1].Contains("A"))
{
currAcc.cir_Agency = true;
}
if (fields[1].Contains("B"))
{
currAcc.cir_AdvertiserBrand = true;
}
try
{
context.UpdateObject(currAcc);
}
catch (Exception exc)
{
while (exc != null)
{
System.Diagnostics.Debug.WriteLine("ERROR: " + exc.Message);
exc = exc.InnerException;
}
continue;
}
}
}
break;
}
}
It is possible that you have either a Workflow or Plugin Step related to account and create/update message, impersonated with an User without the proper privileges.
For workflows, these could have the option "execute as: the owner of the workflow" (link)
For plug-in steps, these have an option "Run in User's Context" that can be set to a fix user (link)
Related
I wrote a program using CSOM to upload documents to SharePoint and insert metadata to the properties. once a while(like every 3 months) the SharePoint server gets busy or we reset IIS or any other communication problem that it may have, we get "The operation has timed out" error on clientContext.ExecuteQuery(). To resolve the issue I wrote an extension method for ExecuteQuery to try every 10 seconds for 5 times to connect to the server and execute the query. My code works in the Dev and QA environment without any problem but in Prod, when it fails the first time with timeout error, in the second attempt, it only uploads the document but it doesn't update the properties and all the properties are empty in the library. It doesn't return any error as result of ExecteQuery() but It seems from the two requests in the batch witch are uploading the file and updating the properties, it just does uploading and I don't know what happens to the properties. It kinda removes that from the batch in the second attempt!
I used both upload methods docs.RootFolder.Files.Add and File.SaveBinaryDirect in different parts of my code but I copy just one of them here so you can see what I have in my code.
I appreciate your help.
public static void ExecuteSharePointQuery(ClientContext context)
{
int cnt = 0;
bool isExecute = false;
while (cnt < 5)
{
try
{
context.ExecuteQuery();
isExecute = true;
break;
}
catch (Exception ex)
{
cnt++;
Logger.Error(string.Format("Communication attempt with SharePoint failed. Attempt {0}", cnt));
Logger.Error(ex.Message);
Thread.Sleep(10000);
if (cnt == 5 && isExecute == false)
{
Logger.Error(string.Format("Couldn't execute the query in SharePoint."));
Logger.Error(ex.Message);
throw;
}
}
}
}
public static void UploadSPFileWithProperties(string siteURL, string listTitle, FieldMapper item)
{
Logger.Info(string.Format("Uploading to SharePoint: {0}", item.pdfPath));
using (ClientContext clientContext = new ClientContext(siteURL))
{
using (FileStream fs = new FileStream(item.pdfPath, FileMode.Open))
{
try
{
FileCreationInformation fileCreationInformation = new FileCreationInformation();
fileCreationInformation.ContentStream = fs;
fileCreationInformation.Url = Path.GetFileName(item.pdfPath);
fileCreationInformation.Overwrite = true;
List docs = clientContext.Web.Lists.GetByTitle(listTitle);
Microsoft.SharePoint.Client.File uploadFile = docs.RootFolder.Files.Add(fileCreationInformation);
uploadFile.CheckOut();
//Update the metadata
ListItem listItem = uploadFile.ListItemAllFields;
//Set field values on item
foreach (List<string> list in item.fieldMappings)
{
if (list[FieldMapper.SP_VALUE_INDEX] != null)
{
TrySet(ref listItem, list[FieldMapper.SP_FIELD_NAME_INDEX], (FieldType)Enum.Parse(typeof(FieldType), list[FieldMapper.SP_TYPE_INDEX]), list[FieldMapper.SP_VALUE_INDEX]);
}
}
listItem.Update();
uploadFile.CheckIn(string.Empty, CheckinType.OverwriteCheckIn);
SharePointUtilities.ExecuteSharePointQuery(clientContext);
}
catch (Exception ex)
{
}
}
}
}
There's too many possible reasons for me to really comment on a solution, especially considering it's only on the prod environment.
What I can say is that it's probably easiest to keep a reference to the last uploaded file. If your code fails then check if the last file has been uploaded correctly.
Side note: I'm not sure if this is relevant but if it's a large file you want to upload it in slices.
I am currently working on an application for the company I work for. Part of this application deals with discrepancy reporting and sending emails out when a new discrepancy number has been created. First, it is important to realize that I am redeveloping this application from VB.NET to C#. In the old application the developer chose to read an XML file for a few email addresses. I've read to use alternate options unless the XML file is full of information. This is not intended to be an opinionated question and sorry if this sounds like one. However, I am looking for the correct way of doing this. Should these email addresses be kept in a database table for easy adding/deleting or is there a more standardized way to do this? Please find the current code below.
public void PrepareEmail(string subject, string message)
{
if (MessageBox.Show(#"Are you sure you want to save and send Discrepancy Report: " + tbxDRNumber.Text + #"?\n Click YES to save\n Click NO to cancel", #"Confirm", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
{
SendEmail(subject, message);
}
}
public Array AddEmail()
{
string[] dRemail = { "", "", "" };
if (File.Exists(#"\\fs01\Applications\EMS-Manager\DREmailAddresses.xml"))
{
XmlReader emailDocument = new XmlTextReader(#"\\fs01\Applications\EMS-Manager\DREmailAddresses.xml");
while (emailDocument.Read())
{
var type = emailDocument.NodeType;
switch (type)
{
case XmlNodeType.Element:
if (emailDocument.Name == "DRCreatedAddEmail")
{
dRemail[0] = emailDocument.ReadInnerXml();
}
if (emailDocument.Name == "DRActionNeededAddEmail")
{
dRemail[1] = emailDocument.ReadInnerXml();
}
if (emailDocument.Name == "DRPendingAddEmail")
{
dRemail[2] = emailDocument.ReadInnerXml();
}
else
{
MessageBox.Show(#"The file: 'DREmailAddresses.xml' was not found at: \\fs01\Applications\EMS-Manager");
}
break;
}
}
}
return dRemail;
}
public void SendEmail(string subjectText, string bodyText)
{
string[] email = (string[])AddEmail();
//object oOutlook = default(Microsoft.Office.Interop.Outlook.Application);
var oMessage = default(MailItem);
Activator.CreateInstance(Type.GetTypeFromProgID("Outlook.Application"));
if (subjectText == "New Discrepancy Created. DR" + tbxDRNumber.Text + " ")
{
oMessage.To = email[0];
oMessage.Subject = subjectText;
oMessage.HTMLBody = bodyText;
try
{
oMessage.Send();
}
catch (System.Exception e)
{
MessageBox.Show(#"Send Failed with error: " + e);
throw;
}
}
else if (subjectText == tbxDRNumber.Text + " - Action Needed")
{
oMessage.To = email[1];
oMessage.Subject = subjectText;
oMessage.HTMLBody = bodyText;
try
{
oMessage.Send();
}
catch (System.Exception e)
{
MessageBox.Show(#"Send Failed with error: " + e);
throw;
}
}
else if (subjectText == tbxDRNumber.Text + "DR Pending Approval")
{
oMessage.To = email[2];
oMessage.Subject = subjectText;
oMessage.HTMLBody = bodyText;
try
{
oMessage.Send();
}
catch (System.Exception e)
{
MessageBox.Show(#"Send Failed with error: " + e);
throw;
}
}
}
There isn't necessarily anything wrong with flat file configuration files. It really depends on your use case. How often do the emails in the file change? How many entries are there? Do users of the application edit the list? Are there any complex rules where different addresses will get sent different emails?
If this just a simple mailing list that doesn't change often it's probably fine to use the xml file. You could also just create an email distribution list to send to instead, like ApplicationDiscrepancyReport. That way you can just manage the recipients in active directory. If you stick with the XML email addresses, at the very least I would try to get rid of the hardcoded path to the xml file located on your file share.
If the email addresses change a lot, and are changed by users of the application, I would recommend moving this to a SQL database. If the recipients list is changing frequently you may want to add tracking of when these addresses get edited as well.
Using Sitecore 6.6.current I'm trying to programmatically access a user's profile.
I'm hoping that I can find any users who have been marked as admins, but no longer have an account, so that I can then remove their admin flag.
As a test I'm running this against my own account, as follows:
var profiles = ProfileManager.FindProfilesByUserName(
ProfileAuthenticationOption.Authenticated,
#"domain\userName"
);
Unfortunately when I do this I get the following error:
System.ArgumentException : Item has already been added. Key in dictionary: 'domain\userName' Key being added: 'domain\userName'
While I understand the error, I'm not sure how I can resolve the issue, since it's using the standard System.Web.Profile call.
What am I doing wrong?
This didn't resolve my issue, but it does appear that Sitecore was saving these active directory accounts without the domain.
Discovered by looping through the users and dumping user names:
var allUsers = Membership.GetAllUsers();
if (allUsers.Count > 0)
{
actionResults.InnerHtml += allUsers.Count + "<br />";
try
{
var count = 0;
foreach (var user in allUsers)
{
actionResults.InnerHtml += user.ToString() + "<br />";
count++;
if (count > 50)
{
break;
}
}
}
catch (Exception ex)
{
Elmah.ErrorSignal.FromCurrentContext().Raise(ex);
}
}
I cannot seem to be able to find that a certain user is a member of a DeployUsersProduction group. Here's what I have so far:
[OperationBehavior(Impersonation = ImpersonationOption.Required)]
public Modes GetDeployMode()
{
bool isProd = false;
WindowsIdentity windowsIdentity = WindowsIdentity.GetCurrent();
if (windowsIdentity == null || windowsIdentity.Groups == null) { return Modes.DUS; }
foreach (IdentityReference identityReference in windowsIdentity.Groups)
{
try
{
var reference = identityReference;
string group = reference.Translate(typeof (NTAccount)).Value.Trim();
if (!String.Equals(group, "DeployUsersProduction", StringComparison.OrdinalIgnoreCase)) { continue; }
isProd = true;
break;
}
catch (Exception ex)
{
// Silent catch due to the [Some or all identity references could not be translated]
// error that sometimes occurs while trying to map an identity.
}
}
return isProd ? Modes.Prod : Modes.DUS;
}
I've got all the config, spn, db, perms, etc correct as far as I can tell. I just have one user that should be returning Modes.Prod and it's not.
The answer wasn't that my approach was wrong, it was the fact that I needed to prefix my group that I was searching for with its domain:
if (!String.Equals(group, #"DOMAIN\DeployUsersProd", StringComparison.OrdinalIgnoreCase)) { continue; }
Special thanks to #DJ KRAZE for the links that led me to writing my own Console app that outputted the groups so I could figure this out!
I am using p4.net API to generate some reports from the metadata.
In one of the reports, I need to generate then number of the changes lines for each changeset report.
As a reporting tool, I am using MS SQL Reporting services 2008, and I have written a custom dll that uses p4.net API to calculate the number of changed lines. it works on the local without any problem. However, when I run the code on the server, it calculates let's say first %20 part then starts throwing Unable to connect to the Perforce Server!
Unable to connect to Perforce! exception.
I try same credentials on the local, it works.. I use commandline with same credentials on the server, it works.
Could anyone help me with that please, if encountered before?
Here is the code I use. If needed
public static class PerforceLib
{
public static P4Connection p4conn = null;
private static void CheckConn()
{
try
{
if (p4conn == null)
{
p4conn = new P4Connection();
p4conn.Port = "address";
p4conn.User = "user";
p4conn.Password = "pwd*";
p4conn.Connect();
p4conn.Login("pwd");
}
else if (p4conn != null)
{
if(!p4conn.IsValidConnection(true, false))
{
Log("Check CONN : Connection is not valid, reconnecting");
p4conn.Login("pwd*");
}
}
}
catch (Exception ex )
{
Log(ex.Message);
}
}
public static int DiffByChangeSetNumber(string ChangeSetNumber)
{
try
{
CheckConn();
P4Record set = p4conn.Run("describe", "-s",ChangeSetNumber)[0];
string[] files = set.ArrayFields["depotFile"].ToArray<string>();
string[] revs = set.ArrayFields["rev"].ToArray<string>();
string[] actions = set.ArrayFields["action"].ToArray<string>();
int totalChanges = 0;
List<P4File> lstFiles = new List<P4File>();
for (int i = 0; i < files.Count(); i++)
{
if (actions[i].ToString() == "edit")
lstFiles.Add(new P4File() { DepotFile = files[i].ToString(), Revision = revs[i].ToString(), Action = actions[i].ToString() });
}
foreach (var item in lstFiles)
{
if (item.Revision != "1")
{
string firstfile = string.Format("{0}#{1}", item.DepotFile, (int.Parse(item.Revision) - 1).ToString());
string secondfile = string.Format("{0}#{1}", item.DepotFile, item.Revision);
P4UnParsedRecordSet rec = p4conn.RunUnParsed("diff2", "-ds", firstfile, secondfile);
if (rec.Messages.Count() > 1)
{
totalChanges = PerforceUtil.GetDiffResults(rec.Messages[1].ToString(), item.DepotFile);
}
}
}
GC.SuppressFinalize(lstFiles);
Log(string.Format("{0} / {1}", ChangeSetNumber,totalChanges.ToString() + Environment.NewLine));
return totalChanges;
}
catch (Exception ex)
{
Log(ex.Message + Environment.NewLine);
return -1;
}
}
}
your help will be appreciated
Many thanks
I have solved this issue. we identified that the code is circling through the ephemeral port range in around two minutes. once it reaches the maximum ephemeral port, it was trying to use same port again. Due to each perforce command creates a new socket, available ports were running out after it processed about 1000 changesets.
I have set the ReservedPorts value of HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters default(1433,143) that gave me larger range of ephemeral port.
and also implemented singleton pattern for P4Conn which helped as I dont close the connection. I only check the validity of the connection, and login if the connection is not valid.
Please let me know if any of you guys needs any help regarding this