User Objects available, but no Attributes mapped

Jul 19, 2012 at 4:28 PM

Hi there

I'm trying to create a small ActiveDirectory User Manager, to specific OUs. First I tried to do the mapping the AttributeClassMap-Way which did work out well - in getting information from the AD. Updating did not work (DistinguishedName not mapped - Error, even if it's mapped). Besides this problem, it's not very dynamic, because you have to put the path to the DirectorySchema-Attribute.

 

Now i'm trying to get the whole thing mapped by using ClassMap. POCO is ready (User), UserMapping-Class derived from ClassMap<User>, which is implementing the abstract Method PerformMapping (empty actually). Config looks like this:

 

IDirectoryMapper mapper = new DirectoryMapper();
            mapper.Map(new UserMapping(pContext), pContext, null, true, null, true);

config = new LdapConfiguration();
            config.Mapper = mapper;

            // create Connection
            config.ConfigureFactory(ServerAddress +":"+ServerPort) // standardPort: 389
                .AuthenticateAs(new NetworkCredential(Username, Password, Domain));

            connection = config.ConnectionFactory.GetConnection();

            context = new DirectoryContext(connection, disposeOfConnection: true);   

 

Then in the query i try to get all Users:

IEnumerable> users = context.Query(new User(), namingContext, null).Select(x=> new List(){x.Id, x.CommonName, x.FirstName, x.LastName, x.LastChanged, x.DistinguishedName});

What am I doing wrong? I get the five user-objects but their attributes like name, first name and so on are empty!


Thanks!

 

Coordinator
Jul 22, 2012 at 11:16 PM
Edited Jul 23, 2012 at 5:30 AM

Change your DirectoryContext to one of the following:

context = config.CreateContext(); 

// OR

context = new DirectoryContext(config);

Internally the DirectoryContext will use the config to get a connection so you don't have to explicitly request a connection and pass it in.

The reason why it is not working is the configuration has the reference to the DirectoryMapper.  If the DirectoryContext is not given a config then it will create one internally (along with a new mapper).  In your case you are using query by example and I'm betting your User properties don't match exactly to the directory attributes.

Change your query to this:

var users = context.Query<User>(namingContext: namingContext).Select(x => new {x.Id, x.CommonName, x.FirstName, x.LastName, x.LastChanged, x.DistinguishedName});

Regarding AttributeClassMap, were you using the DistinguishedName attribute when mapping?  The DistinguishedName is essentially the primary key for an entry.  If you don't map it, then Add / Update will not work since it has no idea where to Add / Update the entry.  Also, you can override what is mapped when you call the Map method for all mappings (Attribute, Class, and Auto).

Let me know if this solves your problem.

Jul 23, 2012 at 1:48 PM
Edited Jul 23, 2012 at 2:44 PM

Hi, thank you very much for the reply!

I changed the DirectoryContext to context = new DirectoryContext(config) which helped me out, the Attributes get mapped now. But I still have problems performing an update on a record. By the way, I'm trying to change data on a Windows 2008 Server with Active Directory.

Here is the code how I try to update the record:

        
        public bool UpdateUserField(String Guid, String ColumnName, String NewValue, String NamingContext)
        {
            User user = GetUserByGuid(Guid,NamingContext);

            if (user != null)
            {
                Type type = user.GetType();
                PropertyInfo pi = type.GetProperty(ColumnName);
                pi.SetValue(user, NewValue, null);
                var user2 = context.Update<User>(user); // Here the exception occures
                return user2.Equals(user);
            }
            else return false;
        }

Then I get an DirectoryOperationException: The server cannot handle directory requests.

In the connection string to connect to LDAP, I'm using the domain-admin and I even started the VisualStudio using this user. So there shouldn't be any permission-issues right? Thanks!

EDIT: Just tested, DELETE does work! So what's the difference to the edit-method?

Coordinator
Jul 24, 2012 at 12:32 AM
Edited Jul 24, 2012 at 12:37 AM

It depends.  That error is kind of generic and can be caused by a few things.  Can you provide the log information for the DirectoryContext?  If possible, can you make sure you are using 3.0.1?  The Response property of the exception will have the relevant information.  I made changes to specifically capture that in 3.0.1.

Jul 24, 2012 at 1:58 PM

This is what I get from the Log of the DirectoryContext:

 

Expression: value(LinqToLdap.DirectoryQuery`1[AdAdmin.LinqToLDAP.User]).FirstOrDefault(x => (x.Id == new Guid(value(AdAdmin.LinqToLDAPWrapper.Wrapper+<>c__DisplayClass0).Guid)))
Search Request >
Filter: (&(objectCategory=person)(objectClass=user)(objectguid=\17\02\37\c3\80\f7\2a\42\a5\4b\04\fd\ae\95\9c\de))
Attributes: distinguishedname, badpwdcount, directreports, Title, PostalCode, cn, WhenCreated, givenname, objectguid, l, usnchanged, c, whenchanged, objectsid, EmployeeId, telephonenumber, Street, Comment, Name, sn, Mail
Naming Context: OU=MyOU,DC=MyDomainName,DC=local
Scope: Subtree
Types Only: False
Controls: 
Page Size: 1
Page Cookie Length: 0
Page Control Is Critical: True
Page Control OID: 1.2.840.113556.1.4.319


Modify Request >
Distinguished Name: CN=MyTest TestName,OU=MyOU,DC=MyDomainName,DC=local
Attributes: { Replace badpwdcount: 0 }, { Replace directreports:  }, { Replace Title:  }, { Replace PostalCode:  }, { Replace cn: Test Jonas }, { Replace WhenCreated: 20120723150535.0Z }, { Replace givenname: Test }, { Replace objectguid: \17\02\37\c3\80\f7\2a\42\a5\4b\04\fd\ae\95\9c\de }, { Replace l:  }, { Replace usnchanged: 257867 }, { Replace c:  }, { Replace whenchanged: 20120723150550.0Z }, { Replace objectsid: \01\05\00\00\00\00\00\05\15\00\00\00\02\42\ab\19\8e\01\9f\5c\1b\9b\68\a0\8d\04\00\00 }, { Replace EmployeeId: 0 }, { Replace telephonenumber:  }, { Replace Street:  }, { Replace Comment:  }, { Replace Name: MyTest TestName}, { Replace sn: MyTest2}, { Replace Mail: test@mail.com }
Request ID: 
Controls: 

An error occurred while trying to update 'CN=Test TestName,OU=MyOU,DC=MyDomainName,DC=local'.  See inner exception for more details
Response=[ ErrorMessage: 0000209A: SvcErr: DSID-031A1021, problem 5003 (WILL_NOT_PERFORM), data 0
, MatchedDN: , ResultCode: UnwillingToPerform, RequestId: , Controls: , Referrals:  ]         Message=The server cannot handle directory requests.    Data=...        InnerException={ }      TargetSite={ }  StackTrace=   at System.DirectoryServices.Protocols.LdapConnection.ConstructResponse(Int32 messageId, LdapOperation operation, ResultAll resultType, TimeSpan requestTimeOut, Boolean exceptionOnTimeOut)
   at System.DirectoryServices.Protocols.LdapConnection.SendRequest(DirectoryRequest request, TimeSpan requestTimeout)
   at System.DirectoryServices.Protocols.LdapConnection.SendRequest(DirectoryRequest request)
   at LinqToLdap.DirectoryContext.Update[T](T entry, String distinguishedName, DirectoryControl[] controls)   HelpLink=null   Source=System.DirectoryServices.Protocols

 

Coordinator
Jul 26, 2012 at 1:55 AM

Ah, the issue is that your are updating properties that either the directory manages or cannot be updated the way you expect.

  • BadPwdCount, WhenCreated, ObjectGuid, UsnChanged, WhenChanged, and ObjectSid are managed by the directory.
  • DirectReports can only be updated by setting the "manager" attribute of an entry (at least that is the case in AD LDS).
  • Cn can only be modified in a Move or Rename operation since it is part of the DistinguishedName.  You can read more about that here and here.

Mark these attributes as ReadOnly or StoreGenerated in your mapping to exclude them from modification requests.

Jul 26, 2012 at 2:50 PM

OK, thank you very much, now it looks way better, I'm able to edit the object. But Adding still doesn't work. Maybe you got me another hint?

this is how I Create one the Object and Save it to the context:

public Boolean AddUser(String username, String LastName, String FirstName, String DisplayName, String eMail, String Company)
{
    string distinguishedName = "CN="+DisplayName+","+context;
    User user = new User() { FirstName = FirstName, LastName = LastName, DisplayName = DisplayName, Mail = eMail, DistinguishedName = distinguishedName, Company=Company };

    wrapper.AddUser(user, context);

    return true;
}

public void AddUser(User addUser)
{
    User returnUser = context.Add<User>(addUser);
}

Thank you very much, I really appreceate your help!

 

Coordinator
Jul 26, 2012 at 11:18 PM

Nothing stands out from your code.  Can you show me the log?

Jul 27, 2012 at 3:03 PM

I get "An object class violation occurred" (DirectoryOperationException).

Here is the log:

 

Expression: value(LinqToLdap.DirectoryQuery`1[AdAdmin.LinqToLDAP.User]).Select(x => new List`1() {Void Add(System.Object)(Convert(x.Id)), Void Add(System.Object)(x.DisplayName), Void Add(System.Object)(x.FirstName), Void Add(System.Object)(x.LastName), Void Add(System.Object)(x.Company), Void Add(System.Object)(x.Mail), Void Add(System.Object)(x.UserPrincipalName)})
Search Request >
Filter: (&(objectCategory=person)(objectClass=user))
Attributes: objectguid, DisplayName, givenname, sn, Company, Mail, UserPrincipalName
Naming Context: OU=Test,OU=MyOU,DC=MyCompany,DC=local
Scope: Subtree
Types Only: False
Controls:
Page Size: 500
Page Cookie Length: 0
Page Control Is Critical: True
Page Control OID: 1.2.840.113556.1.4.319

and

Add Request >
Distinguished Name: CN=,OU=Test,OU=myOu,DC=MyCompany,DC=local
Attributes: { objectClass: person }, { objectClass: user }, { givenname:  }, { sn:  }, { Mail:  }, { DisplayName:  }, { Company:  }
Request ID:
Controls:

An error occurred while trying to add 'CN=,OU=Test,OU=MyOU,DC=MyCompany,DC=local'.  See inner exception for more details.
Response=[ ErrorMessage: 00000057: LdapErr: DSID-0C090C3E, comment: Error in attribute conversion operation, data 0, v1db1, MatchedDN: , ResultCode: InvalidAttributeSyntax, RequestId: , Controls: , Referrals:  ]     Message=The syntax is invalid.  Data=...        InnerException={ }      TargetSite={ }  StackTrace=   at System.DirectoryServices.Protocols.LdapConnection.ConstructResponse(Int32 messageId, LdapOperation operation, ResultAll resultType, TimeSpan requestTimeOut, Boolean exceptionOnTimeOut)
   at System.DirectoryServices.Protocols.LdapConnection.SendRequest(DirectoryRequest request, TimeSpan requestTimeout)
   at System.DirectoryServices.Protocols.LdapConnection.SendRequest(DirectoryRequest request)
   at LinqToLdap.DirectoryContext.Add[T](T entry, String distinguishedName, DirectoryControl[] controls)      HelpLink=null   Source=System.DirectoryServices.Protocols

Coordinator
Aug 1, 2012 at 3:25 AM

This is a bug.  The first objectClass in the AddRequest should be objectCategory.  I'll create a ticket for it.

Coordinator
Aug 1, 2012 at 3:27 AM
This discussion has been copied to a work item. Click here to go to the work item and continue the discussion.