Deleting duplicate Names from NamesManager using C# Interop.Excel - c#

I am facing some problem with deleting Names from NAmesManager in C# code.
As per my requirement, I need to delete all the Invalid names from the Names Manager. I am currently checking for the value of the Name and deleting it if the value is "#REF!". This is the code I am using
foreach (Name RangeName in namesManager2)
{
if(RangeName.Value.Contains("#REF!"))
{
RangeName.Delete();
}
}
The code works fine, However There are some strange situations where there exists 2 names with same Name but the scope is different.
Consider cell 1 is named "TESTNAME" with scope of "Workbook" and cell 2 is also named "TESTNAME" with scope "Sheet1". The name referring to Cell2 has a valid value.
So when I am looping if the name with "#REF!" value is encountered the above code is removing both the names. I want to retain the name with valid value but delete only the invalid Name.
Can someone suggest how to achieve this ?

There are bugs with duplicated Local/global names.
Accessing a global name whilst the active sheet has an identically named name local to that sheet, will change the properties of the local name and NOT the global name, even if the name is fully qualified with the workbook name.
So to bypass this you have to:
- detect if the Name is a duplicate
- if so switch to a worksheet that is not the parent of the local name (of course there may be multiple local names, one on each sheet so the only really safe way is to add another temporary worksheet and switch to that)
- then access whichever name you want either in the Workbook Name collection or the worksheet name collection.
This is the technique used by the freebie Name Manager Addin developed by JK Pieterse and myself:Name manager Download which has a lot more function than the built-in Name Manager

I recommend you use unique names for Name Ranges to avoid this sort of problem.
var activeBook = (Workbook)currentInstance.ActiveWorkbook;
Range rnArea = activeSheet.Range["A1:A1"];
activeBook.Names.Add("TESTNAME", rnArea);
rnArea = activeSheet.Range["B1:B1"];
activeSheet.Names.Add("TESTNAME", rnArea);
List<Name> existingNamedRangeList1 = XlHelper.GetNamedRanges(currentInstance.ActiveWorkbook);
foreach (Name RangeName in existingNamedRangeList1)
{
if (RangeName.Value.Contains("#REF!"))
{
RangeName.Delete();
}
}

Unfortunately, it looks like it assumes the Name will be unique, and as such any Deletes done will not have expected behaviour. One way to get around this is to generate a unique name when going through all the RangeNames, then after you are done deleting, reset them back to their original names.
var rangeNameHolder = new Dictionary<string, string>();
var rangeNames = activeBook.Names;
int counter = 0;
foreach (Name rangeName in rangeNames)
{
var oldName = rangeName.Name;
/*
* This hack here is done because when you grab the name, it includes the prepended scope.
* However, when you set the name, it prepends the scope yet again!
* So when you grab it the first time you need to remove the scope so that it doesnt get
* prepended twice
*/
oldName =oldName.Substring(oldName.LastIndexOf('!')+1);
var newName = string.Format("{0}{1}", oldName, counter++);
rangeName.Name = newName;
if (rangeName.Value.Contains("#REF!"))
{
rangeName.Delete();
continue;
}
rangeNameHolder.Add(rangeName.Name,oldName);
}
//Reset names back to original
foreach (Name rangeName in rangeNames)
{
if (rangeNameHolder.ContainsKey(rangeName.Name))
rangeName.Name = rangeNameHolder[rangeName.Name];
}

Related

How to let a user add new objects in a list?

In my Console App the user can currently add a new object by pressing the "+" key, but how can I let him later on change a specific object in the list? For example.
The list is of boats. I want each boat to continously get a specific name, i.e boat1, boat2, boat3, etc. And if a user adds another boat it will get then name boat4, and so on, so that he later in the application can change the speed of a specific boat by calling its name.
Boats boat1 = new Boats();
boat1.Speed = 15;
Boats boat2 = new Boats();
boat2.Speed = 26;
List<Boats> boatList = new List<Boats>();
boatList.Add(boat1);
boatList.Add(boat2);
And in the Boats.cs I have this method:
public static void NewBoat()
{
var newBoat = new Boats();
Console.WriteLine("\nIf you want to create a new boat, press +");
if (Console.ReadKey().KeyChar == '+')
{
var newBoats = new Boats();
newBoat.AddBoatList(newBoats);
}
else
{
Console.WriteLine("\nInvalid Input.");
}
boat1 and boat2 are just variable names, not the name of the object. If you add them to the list the list doesn't know anything about the names. If you want to have the name in the list you should have a String with the name as part of the Boats class to which you assign the name. Later you can search for the name in the list.
To generate the variables it's better to do it in the way of your second part in Boats.cs: not for each boat an extra variable, just one variable which process the new before you assign the data to it and add it to the list.
You need to save the names of the boats in the list and then you can create a function to retrieve the boat name and then you can change it

c# reference to string in FIM

I'm writing a rule extension for Microsoft Identity Manager (FIM / MIM) and have a problem.
What I want:
I have an attribute "Manager" which is references to user manager. I need to look for this attribute and populate another attribute in MS AD with account name of manager.
I have a such code which must be working, but I get error
System.InvalidOperationException: Unable to access attribute manager. Reference values not accessible on MV objects.
at Microsoft.MetadirectoryServices.Impl.AttributeImpl.get_Value()
at Mms_ManagementAgent_HRExt.MAExtensionObject.Microsoft.MetadirectoryServices.IMASynchronization.MapAttributesForImport(String FlowRuleName, CSEntry csentry, MVEntry mventry) in C:......HRExt.cs:line 213
at this point
mvMGRemployeeID = mventry["ManagerID"].Value.ToString().ToLower();
Code is below:
string mvMGRemployeeID; //temp string that holds the supervisor code
MVEntry[] mgrSearch; //Collection of MV Etriers used to perform the search forMV object based on the manager employeeID
if (mventry["ManagerID"].IsPresent)
{
mvMGRemployeeID = mventry["MAnagerID"].Value.ToString().ToLower();
mgrSearch= Utils.FindMVEntries("employeeID", mvMGRemployeeID, 1); //Is there an object with employeeID = ManagerID
if (mgrDNSearch.Length == 1)//if we get only one return (which we should)
{
if (mgrDNSearch[0]["accountName"].IsPresent) //get the DN of the returned object
{
csentry["manager"].Value = mgrDNSearch[0]["accountName"].Value.ToString();
}
}
}
break;
Make sure you added MAnagerID in dependencies.
and simply you can do:
mvMGRemployeeID = "" + csentry["MAnagerID"].ReferenceValue;
Thank you

Copy a text field data into a look field type

I am new in coding world :) it would be helpful if somebody can help me with my below query.
We have created two custom entity called Sector and Sub sector which are having an 1:N relationship with Account. Since they are relation fields hence they are lookup type and populated on the Account form.
On another part, we have InsideView( 3rd party tool) integrated with our Contact and account form. We have mapped certain fields from Inside view with CRM fields to update the data from the inside view when it is being synced however Inside view does not support Lookup type field hence we cannot map lookup field type data.
We discovered this barrier recently when we tried to map our custom entity (sector and Subsector) with inside view. Since we cannot map lookup field type we thought to have two text field instead and map it with Inside view. Once data is synced these two text fields will get filled out with the sector and subsector name.
Now, we want to copy information from text fields to the lookup field (custom fields Sector and Sub sector)
thanks in advance for your help :)
Bhavesh
I am not understanding your requirement but you can fill a lookup field with some text.
Use following code:
function setLookupField() {
var context = Xrm.Page.context;
var UserID = context.getUserId();// your id
var UserName=context.getUserName();//your text
var lookupData = new Array();
var lookupItem = new Object()`enter code here`;
//Set the GUID
lookupItem.id = UserID;
//Set the name
lookupItem.name = UserName;
lookupItem.entityType = "systemuser";// entity name of lookup
lookupData[0] = lookupItem;
//If existing value is empty, then set new value
var existingValue = Xrm.Page.getAttribute("new_usercreatedby").getValue();
if (existingValue === null) {
Xrm.Page.getAttribute("new_usercreatedby").setValue([{
id: UserID,
name: UserName,
entityType: "systemuser"
}]);
} else {
return;
}
}

Streamwriter if statement not working

I am currently working on a school project involving a large number of students where I have to insert a new student alphabetically and do a few other calculations. I am having trouble getting it so that it only adds the new student once. I have an if statement but it doesn't appear to be working properly.
`//this adds the new student
StreamWriter changeFile = new StreamWriter("Students.txt", true);
string newStudent = "(LIST (LIST 'Malachi 'Constant 'A ) '8128675309 'iwishihadjessesgirl#mail.usi.edu 4.0 )";
// this is where I am getting stumped
if (File.Exists(newStudent))
{
changeFile.Close();
}
else
{
changeFile.WriteLine(newStudent);
changeFile.Close();
}`
Whenever I run the code like this it will just add the new student every time I debug the program. How can I make it only add him one time?
File.Exists determines if the file at the given path exists (which, for the record, you should still be doing before trying to read/write to the file). You're trying to find out if the given line of text exists within a given file. That's a very different task.
You'll need to read through the lines in the file and compare them to your given text.
if(!File.ReadLines(filepath).Contains(newStudent))
{
//TODO: Append student to the file
}
File.Exists(string path) returns a bool that determines if a file exists at the specified path.
http://msdn.microsoft.com/en-us/library/system.io.file.exists(v=vs.110).aspx
string newStudent is not a file path, so it will always return false.
I think what you want is something like this: (this is by memory so it likely won't compile as is)
var file = File.Open("students.txt");
var fileContents = file.ReadToEnd();
if (!fileContents.Contains(newStudent))
{
file.WriteLine(newStudent);
}
file.Close();
First read the Existing file data into String variable and then check the given student data is available or not in the received file.if the given student data is not found then write the new student data into file otherwise,if already present then close the opened steream.
String StudentInfo = System.IO.File.ReadAllText("Students.txt");
StreamWriter changeFile = new StreamWriter("Students.txt", true);
string newStudent = "(LIST (LIST 'Malachi 'Constant 'A ) '8128675309 'iwishihadjessesgirl#mail.usi.edu 4.0 )";
// this is where I am getting stumped
if (StudentInfo.Contains(newStudent))
{
changeFile.Close();
}
else
{
changeFile.WriteLine(newStudent);
changeFile.Close();
}

Retrieve Lotus Notes contact information using contact ID in c#

Im developing a tool that needs to access to the names.nsf database inside IBM Lotus Notes, and, using the lotus contact ID (Employee ID) (this id will be provided by the user), retrieve the full information of the person (Name, Position, Phone #....)
I found an example at Codeproject.com (http://www.codeproject.com/Articles/18517/Lotus-Notes-Integration-with-Microsoft-NET-Platfor), however it takes around 10 minutes to get the information the way the example does it (the database has more or less 5000 entries), so I'm searching for a faster way of doing it (if I actually use Lotus notes for this it takes about a second!).
Is there a way to accomplish this task without having the user waiting for minutes?
Thought that maybe you can help me out with this one.
The sample you are using goes through the view using
NotesViewEntry viewEntry = notesViewCollection.GetNthEntry( rowCount );
This is (one of) the worst methods to use as it goes for every iteration from the top of the view and iterates through all docs until it reached the nth document.
There are two options:
1) Optimize this code by using
NotesViewEntry viewEntry = notesViewCollection.GetFirstEntry();
and at the end
viewEntry = notesViewCollection.GetNextEntry(viewEntry);
2) (in my humble opinion the better way): Change the code:
- you need a view with the first column sorted by your key => contact ID (Employee ID)
- You can the access the ViewEntry by a code like
LotusNotesView.GetEntryByKey( EmployeeID, true);
If you are lucky the names.nsf is full text indexed. If it's not you could try to ask if it could be full text indexed. When it's indexed you can get the person document quicly like this:
LotusNotesView.FTSearch("[EmployeeID]=1234567", 1);
NotesDocument docPerson = LotusNotesView.GetFirstDocument();
The use of GetNthEntry certainly causes some performance issues. I've taken the relevant code from that site and rewrote it to use the GetFirst/GetNext pattern, which is recommended for all view processing in Lotus Notes.
Note this hasn't been tested, of course. The point is to get the first entry in your collection, check that it is an object, and then process it. At the end of the loop, get the next entry and repeat until you hit null.
NotesViewEntryCollection notesViewCollection = LotusNotesView.AllEntries;
NotesViewEntry viewEntry = notesViewCollection.GetFirstEntry();
while (viewEntry != null)
{
//Get the first document of particular entry.
NotesDocument document = viewEntry.Document;
object documentItems = document.Items;
Array itemArray1 = (System.Array)documentItems;
for( int itemCount=0 ; itemCount< itemArray1.Length; itemCount++ )
{
NotesItem notesItem =
(Domino.NotesItem)itemArray1.GetValue( itemCount );
//compare field value with specific value entered by user
if( notesItem.Text !=null )
{
if( (notesItem.Text.ToUpper()).StartsWith( fieldValue ))
{
Contact contact = new Contact();
for( int icount=0 ; icount< itemArray1.Length; icount++ )
{
NotesItem searchedNotesItem =
(Domino.NotesItem)itemArray1.GetValue( icount );
string FieldName = searchedNotesItem.Name.ToString();
//For FirstName
if( searchedNotesItem.Name == "FirstName" )
contact.FirstName= searchedNotesItem.Text;
//For LastName
if( searchedNotesItem.Name == "LastName" )
contact.LastName = searchedNotesItem.Text;
//For Office Phone Number
if( searchedNotesItem.Name == "OfficePhoneNumber" )
contact.OfficePhoneNumber = searchedNotesItem.Text;
if( searchedNotesItem.Name == "InternetAddress" )
contact.EmailId = searchedNotesItem.Text;
}//end for
contactsList.Add( contact );
break;
}//End if
}
}
//Get the nth entry of the selected view according to the iteration.
NotesViewEntry viewEntry = notesViewCollection.GetNextEntry(viewEntry);
}
Why are you asking the user to provide his Employee ID? You should ask him to provide his Notes username (either FullName or ShortName), or his email address. Any of those can be looked up very quickly in the $Users view in names.nsf, giving you fast access to the document containing all the data that you need.
Note: I'm aware that some companies actually enter their Employee ID into the ShortName field in names.nsf. If that's the case for your organization, then what you should be doing is opening a NotesView object using the NotesDatabase.getView() method, and then use the NotesView.getDocumentByKey() method to get the document for the user. E.g., something like this:
NotesView usersView = namesDb.getView("$Users");
NotesDocument userDoc = usersView.getDocumentByKey(employeeId);
Then just read the data that you want, using userDoc.getItemValue() for each information field that you are interested in. You should only do a loop through the entire userdoc.Items array if you are really trying to capture everything, including a bunch of internal-use values.

Categories

Resources