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
Related
I have a viewmodel to update data (from API, not view) with params like below:
public string Name { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
i just want to update "name", so my param like below:
{
"name": "my name"
}
its name changed become "my name" but its email and phone become null. how to avoid params changed to be null if they don't exist in input form?
Thanks...
You'll have to adjust whatever does the update so that it understands "null means do not set a value, rather than set the value to null"
For example if you're running a db update query you could:
UPDATE person
SET
name = COALESCE(#name, name),
email = COALESCE(#email, email),
phone = COALESCE(#phone, phone)
WHERE
id = #id
Now if any value is supplied as null the update will set the column to the same value it is currently (ie no-op)
If you're adjusting a c# object you can take a similar approach:
var p = db.FindPerson(viewmodel.PersonId);
p.Name = viewmodel.Name ?? p.Name;
...
As I see, you have a problem or misunderstanding in your software design.
If this API endpoint is meant to just update the name field, then you should not have the other fields in your ViewModel. However, if you update the other fields in some cases, then you should pass their values as well.
So maybe you need to call a Get endpoint first to get the all data you need in your client "web page for example" and then allow this client to resend the full JSON, not just the name.
Or you may just need an endpoint that just takes the name.
Another solution, in case you're using EF, is to ignore the null fields when updating the EF entity.
e.g.
...
var entity = dbContext.Employees.FirstOrDefault(e => e.Id == 3);
entity.Name = request.Name;
await dbContext.SaveChangesAsync();
...
Using EF Core
We are trying to obtain all information of an assessment, which includes its groups and all assigned users. See the
Database Diagram
What is working in following order;
HttpPost (api/Assessment/aID/groups) of an empty group to an assessment
HttpPost (api/Group/gID/users) of users to an existing group
What we are trying to accomplish (code referenced is a different example, yet same principle);
HttpPost (api/Assessment/aID/groups) where a group already contains a list of users. When trying to accomplish this, a possible object cycle was detected which is not supported.
This piece of code is currently throwing a NullReference on Address
-------------------------------------------------------------------
Group groupToCreate = new Group { Name = dto.Name, Description = dto.Description };
foreach (var u in dto.Users)
{
groupToCreate.AddUser(new User
{
Name = u.Name,
Email = u.Email,
Address = new Address
{
Country = u.Address.Country,
City = u.Address.City,
PostalCode = u.Address.PostalCode,
Street = u.Address.Street,
HouseNr = u.Address.HouseNr,
BusNr = u.Address.BusNr
}
});
}
_groupRepository.Add(groupToCreate);
_groupRepository.SaveChanges();
return groupToCreate;
HttpGet (api/Assessment) which displays its assigned groups and linked users.
This seems to be working
------------------------
groupList = _groups.Select(g => new GroupDTO
{
Name = g.Name,
Description = g.Description,
Users = g.GroupUsers.Select(u => new UserDTO
{
Name = u.User.Name,
Email = u.User.Email,
Address = new AddressDTO
{
Country = u.User.Address.Country,
City = u.User.Address.City,
PostalCode = u.User.Address.PostalCode,
Street = u.User.Address.Street,
HouseNr = u.User.Address.HouseNr,
BusNr = u.User.Address.BusNr
}
}).ToList()
}).ToList();
References:
User
Group
Assessment
AssessmentRepo
Hard to tell with the details you're providing, but I'm guessing this is due to Having two-way navigation properties? Are you using EF here?
For example, if your User has a Navigation property allowing access to the user's Group, but a Group has a collection of User objects, then each of those users would themselves have the Group expressed within them... then when trying to express this it could easily get stuck in a cycle, e.g. a user would look like:
{
"Name":"user name",
"Group":{
"Name":"group1",
"Users":[
{
"Name":"user name",
"Group":{
"Name":"group1",
"Users":{
....
}
}
}
]
}
}
.. because a User has a Group, and the Group has a list of User objects, and each one of those has a Group... etc.
This is the sort of issue that comes from mixing your Data layer and DTO objects. Change your system so the objects returned by your REST methods are new objects designed for the requirements of the API/front-end. These objects may look very similar to your DB models (at least initially) but they should not be the same objects.
Create entirely new objects which don't have any logic or navigation properties, and exist only to pass information back to API consumers. For example, a simple class to give a list of user groups and the users in those groups may be defined as:
public class UserDto
{
public string UserName { get; set; }
public IEnumerable<string> Groups { get; set; }
}
public class UserListDto
{
public IEnumerable<UserDto> Users { get; set; }
}
And then your controller action could do something like:
var users = userService.GetAllUsers();
var result = new UserListDto {
Users = users.Select(u => new UserDto{
UserName = u.Name,
Groups = u.Groups.Select(g => g.Name)
}
};
return Ok(result);
..So the thing being serialised for the response doesn't have any complicated relationships to negotiate, and more importantly a change to how you are internally storing and working with the data won't affect the external contract of your API - API consumers can continue to see exactly the same information but how you store and compile this can change drastically.
It is tempting to think "The Data I need to return is basically the same as how I store it internally, so just re-use these classes" but that's not a great idea & will only ever give problems in the long run.
To avoid having to (re-)write a lot of code to 'convert' one object into another, I'd recommend looking into something like AutoMapper as this can make that fairly easily re-usable & allow you to put all this 'Translation' stuff into one place.
I have tried to create a opportunity with suite talk API. while updating the entity field value it returns error because it needs internal id value of the field but it is not feasible to address the internal id.
ReflectionExtensions.SetPropertyValue(NS_OPPURTUNITY, map.Dst_Fld_Name, new RecordRef()
{
internalId = "2551",
type = RecordType.customer,
typeSpecified = true
});
i want to get rid of that static id to reference the entity.
As far as I know, you need the internal ID to reference any object through web services. You can however find the internal ID by first searching for the item you need to reference.
You can use a CustomerSearch to find the internal ID of your customer:
CustomerSearch custSearch = new CustomerSearch();
SearchStringField name = new SearchStringField();
name.SearchValue = "firstName";
name.operatorSpecified = true;
name.#operator = SearchStringFieldOperator.#is;
CustomerSearchBasic custBasic = new CustomerSearchBasic();
custBasic.firstName= customerEntityID;
custSearch.basic = custBasic;
// Search for the customer entity
SearchResult res = _service.search(custSearch);
//Get the internal ID of the customer
string internalID = ((Customer) (res.recordList[0])).internalId;
You can search for the customer using other fields besides 'firstName' as well. Check which other fields are available on the CustomerSearchBasic object here: CustomerSearchBasic
You can update the entity field on basis of external ID also but for this you need to remember certain things
1. You need to set externalID during creation of any record.
2. External ID is unique around the system.
3. Some records don't support external ID such as Custom List.
InventoryItem inventory = new InventoryItem();
inventory.externalId = "abc";
inventory.displayname = "Hello";
setPreferences();
WriteResponse writeRes = _service.update(inventory );
I have a list view control that I want to add accounts to. The first column of the list view is for an account's username, and the second column is for the account's password.
I want avoid adding duplicate rows of login credentials. What I mean by that is that I don't care if a username appears in the list view control more than once. Just as long as each instance of the username has a different password. The same rule applies to passwords. I don't care if multiple accounts have the same password.
I just want to avoid duplicate ROWS.
The following code is something I have tried with no success:
private void AddAccounts()
{
List<string> usernames = new List<string>();
usernames.Add("Margaret Parker");
usernames.Add("Steven Stewart");
usernames.Add("Heather Powell");
usernames.Add("Denise Simmons");
usernames.Add("Ronald Moore");
List<string> passwords = new List<string>();
passwords.Add("mExEvHb3");
passwords.Add("muFLdtHu");
passwords.Add("GrcSNCyY");
passwords.Add("S8qenUZY");
passwords.Add("PVAzFYyu");
// usernames.Count will always be the same as passwords.Count
// so it doesn't matter which property I use.
for (int i = 0; i < usernames.Count; i++)
{
ListViewItem lvi = new ListViewItem(usernames[i]);
lvi.SubItems.Add(passwords[i]);
if (!accountsListView.Items.Contains(lvi))
{
// It is unsafe to call a control from a thread other
// than the one that created the control without using
// the Invoke method.
Invoke((MethodInvoker)delegate { accountsListView.Items.Add(lvi); });
}
}
}
When AddAccounts() is called, it will add the following items to accountsListView:
/*
# Margaret Parker mExEvHb3
# Steven Stewart muFLdtHu
# Heather Powell GrcSNCyY
# Denise Simmons S8qenUZY
# Ronald Moore PVAzFYyu
*/
It will not take into consideration if an account has already been added to the list view control. Which is why I'm coming to you folks for help.
How can I avoid adding duplicate rows to a list view control?
Thank you for taking the time to read my question.
I want to ALLOW multiple instances of the same username OR password:
/*
# JamesEdwards ---- LZsDVQ7A ---- different password (GOOD!)
# GeraldLopez ---- LZsDVQ7A
# JamesEdwards ---- 7cbrPRzt ---- different password (GOOD!)
*/
I want to avoid EXACT duplicates:
/*
# PhillipAnderson ---- 4ZN5TKfM ---- exact duplicate (BAD!)
# NicholasPowell ---- 4ZN5TKfM
# PhillipAnderson ---- 4ZN5TKfM ---- exact duplicate (BAD!)
*/
If an account's password ever gets changed, then I want to be able to add that account again to my list view control with the new password. I want to keep the old account information in my list view control as well. That is why I want to allow duplicate usernames in my list view control.
You are creating a new instance of ListViewItem and validating existence in the collection, which performs reference check and returns false all the time.
instead of this accountsListView.Items.Contains(lvi)
do this
ListViewItem item = accountsListView.FindItemWithText(userNames[i]);
if (item == null)
{
// logic goes here.
}
or
bool found = false;
foreach(ListViewItem lv in accountsListView.Items)
{
if(lv.Text ==userNames[i])
{
found = true;
break;
}
}
if(!found)
{
// logic goes here.
}
Make a note ListView also provides FindItemWithText to validate existence of an item.
There are few things that you need to change in your code. You do not have to use 2 lists for this purpose. One list should be enough. You can use available features in C# to do this kind of things more efficiently.
So as the first step; create a class and inherit it from IEqualityComparer. We have to override Equals and GetHashCode methods to make it possible to be used in Linq etc.
public class UserAccount : IEqualityComparer<UserAccount>
{
public string UserName { get; set; }
public string Password { get; set; }
public bool Equals(UserAccount x, UserAccount y)
{
return (x.UserName == y.UserName && x.Password == y.Password);
}
public int GetHashCode(UserAccount obj)
{
return obj.UserName.GetHashCode() + obj.Password.GetHashCode();
}
}
Then use the above created class to create a List of user accounts.
var userAccounts = new List<UserAccount>
{
new UserAccount() {UserName = "PhillipAnderson", Password = "4ZN5TKfM"},
new UserAccount() {UserName = "NicholasPowell", Password = "4ZN5TKfM"},
new UserAccount() {UserName = "PhillipAnderson", Password = "4ZN5TKfM"}
};
Now you can use Distinct method to filter out duplicate user accounts. you will notice uniqueUserAccounts has only 2 user accounts after applying Distinct.
var uniqueUserAccounts = userAccounts.Distinct(new UserAccount()).ToList();
foreach (var uniqueUserAccount in uniqueUserAccounts)
{
//your code to add user accounts to the list view
}
I am struggling with how I do the following in LightSwitch. I am 'intercepting' the entity during the partial void tblStaffExtendeds_Updating(tblStaffExtended entity) and then pulling some data from our local ldap and trying to set one of the properties of the entity to a specific value, like so:
partial void tblStaffExtendeds_Updating(tblStaffExtended entity)
{
string ldapPath = #"LDAP://DC=myLDAP,DC=myLDAP";
string user = entity.GPEmployeeID.ToString();
string[] props = {
ActiveDirectoryInfo.strings.DISPLAYNAME, ActiveDirectoryInfo.strings.EMAIL,
ActiveDirectoryInfo.strings.LOGONALIAS, ActiveDirectoryInfo.strings.PHONE,
ActiveDirectoryInfo.strings.OFFICE, ActiveDirectoryInfo.strings.TITLE,
ActiveDirectoryInfo.strings.GPEMPLOYEEID
};
var propResults = ActiveDirectoryInfo.UserPropertySearchByGPEmpID(user, ldapPath, props);
entity.tblAdminStaffType.StaffType = propResults[ActiveDirectoryInfo.strings.TITLE];
entity.WorkEmail = propResults[ActiveDirectoryInfo.strings.EMAIL];
entity.UserID = propResults[ActiveDirectoryInfo.strings.LOGONALIAS];
entity.WorkPhone = propResults[ActiveDirectoryInfo.strings.PHONE];
}
Now for fields like WorkEmail and WorkPhone this works fine as those properties are just strings and that is what I am getting from LDAP.
However I do I go about setting the StaffType which is a reference to an Admin Table entry? LDAP returns a string which matches the description on the Admin Table but on the Entity I would need to set it to the correct ID, I am assuming.
Is there someway to do this, short of creating "Look Up" methods to find the ID from the Admin Table by matching the Description to my String from LDAP?
The solution should look something like this:
string title = propResults[ActiveDirectoryInfo.strings.TITLE];
var qryAdminStaffType = from st in DataWorkspace.ApplicationData.StaffTypes
where st.Title == title
select st;
entity.tblAdminStaffType.StaffType = qryAdminStaffType.FirstOrDefault();
In this example, I'm assuming that your data source is called ApplicationData (the LS default name), that the StaffTypes table holds your staff types, and that you are matching the Title attribute. Note that if there is no match to the title, FirstOrDefalt() will return null.