• Common System.DirectoryServices Patterns


    Common Patterns in System.DirectoryServices

    Searching the Directory:

    1. Create a DirectoryEntry that represents your SearchRoot.  Your searches will be rooted to this location and will have the same permissions as the bound SearchRoot.  Failure to specify a SearchRoot (bad practice) means you will attempt to search the entire current domain (as specified by RootDSE defaultNamingContext) and can be problematic for ASP.NET applications.
    2. Create your LDAP query.
    3. Optionally specify PropertiesToLoad collection - this will be more efficient if specified.  However, it will return all available, non-constructed attributes if left null (Nothing in VB.NET).
    4. For v1.1 S.DS, use the DirectorySearcher.FindAll() for all searches.  The DirectorySearcher.FindOne() has a memory leak in certain situations - .NET 2.0 is unaffected and safe to use.
    Sample For Retrieving Only 1 Result:

    DirectoryEntry searchRoot = new DirectoryEntry(
        "LDAP://server/OU=People,DC=domain,DC=com", //searches will be rooted under this OU
        "domain\\user", //we will use these credentials
        "password",
        AuthenticationTypes.Secure
        );

    using (searchRoot) //we are responsible to Dispose this!
    {
        DirectorySearcher ds = new DirectorySearcher(
            searchRoot,
            "(sn=Smith)", //here is our query
            new string[] {"sn"} //optionally specify attributes to load
            );

        ds.SizeLimit = 1;

        SearchResult sr = null;
       
        using (SearchResultCollection src = ds.FindAll())
        {
            if (src.Count > 0)
                sr = src[0];
        }

        if (sr != null)
        {
            //now use your SearchResult
        }
    }


    Sample For Retrieving Multiple Results:

    DirectoryEntry searchRoot = new DirectoryEntry(
        "LDAP://server/OU=People,DC=domain,DC=com", //searches will be rooted under this OU
        "domain\\user", //we will use these credentials
        "password",
        AuthenticationTypes.Secure
        );

    using (searchRoot) //we are responsible to Dispose this!
    {
        DirectorySearcher ds = new DirectorySearcher(
            searchRoot,
            "(sn=Smith)", //here is our query
            new string[] {"sn"} //optionally specify attributes to load
            );

        ds.PageSize = 1000; //enable paging for large queries

        using (SearchResultCollection src = ds.FindAll())
        {
            foreach (SearchResult sr in src)
            {
                //use the SearchResult here
            }
        }
    }


    Notice the common pattern here and how all DirectoryEntry and SearchResultCollection classes are Disposed.  Failure to do so can leak memory.

    Reading Attributes:

    One of the most common issues that people run into is getting an error trying to read the attribute when it does not exist.  It is important to understand there is no concept of null attributes in Active Directory - the attribute either exists on the object or it doesn't - it is never null.  This however can be confusing because trying to read a non-existant attribute culminates in a null reference exception in .NET.

    To protect against this, use this pattern:

    DirectoryEntry entry = new DirectoryEntry(...);
    using (entry)
    {
        if (entry.Properties.Contains("property"))
        {
            //now safe to access "property"
            //cast as appropriate to string, byte[], int, etc...
            //object o = entry.Properties["property"].Value;
        }
    }


    This same pattern is appropriate for the SearchResult as well:

    SearchResult result;
    //... fill the result

    if (result.Properties.Contains("property"))
    {
        //now safe to access "property"
        //cast as appropriate to string, byte[], int, etc...
        //object o = result.Properties["property"][0];
    }

    Depending on the attribute, the PropertyValueCollection (or ResultPropertyValueCollection) will return either a single value or multiple values.  To actually get a value from the DirectoryEntry, we need to cast the 'object' to whatever type we are expecting.  Thus, we get something like:

    DirectoryEntry entry = new DirectoryEntry(...);
    using (entry)
    {
        if (entry.Properties.Contains("
    sn"))
        {
            string lastName = entry.Properties["sn"][0].ToString();
        }
    }


    In this example, I cast the 'sn' attribute to the lastname.  It makes 2 assumptions: 1.) The value can be interpreted as a string, and 2.) I am only interested in a single valued attribute.

    The pattern changes slightly if we are looking to read the values from a multi-valued attribute:

    DirectoryEntry entry = new DirectoryEntry(...);
    using (entry)
    {
        if (entry.Properties.Contains("
    mail"))
        {
            foreach (object o in entry.Properties["mail"])
            {
                //iterate through each value in the 'mail' attribute as a string
                Response.Output.Write("mail: {0}", o.ToString());
            }
        }
    }


    Notice that I am iterating through all the objects and casting to string in this case.  If the attribute was something else, it might be appropriate to cast to a byte[] array or an integer, etc.  It just depends on what it is, but the casting from object to whatever type you need is up to you.

    Binding to a Data Control:

    In order to bind to things like a dropdown list or a datagrid, we need to perform the search first and manually create a datasource to use.  Since AD is hierarchical and datacontrols are not, we generally need to think about how we will model the data when rows/columns won't make any sense.

    Here is a sample function that will search Active Directory given a filter for searching and the attributes to retrieve.  I called it 'FindUsers' for lack of a better name about 5 years ago and decided not to change it to confuse people.  In reality, it can be used to find anything, not just users.  It returns a DataSet and optionally caches it for faster lookups next time.

    public DataSet FindUsers(string sFilter, string[] columns, string path, bool useCached)
    {
        //try to retrieve from cache first
        HttpContext context = HttpContext.Current;
        DataSet userDS = (DataSet)context.Cache[sFilter];
               
        if((userDS == null) || (!useCached))
        {
            //setup the searching entries
            DirectoryEntry deParent = new DirectoryEntry(path);
            //deParent.Username = Config.Settings.UserName;
            //deParent.Password = Config.Settings.Password;
            deParent.AuthenticationType = AuthenticationTypes.Secure;
               
            DirectorySearcher ds = new DirectorySearcher(
                deParent,
                sFilter,
                columns,
                SearchScope.Subtree
                );

            ds.PageSize = 1000;
               
            using(deParent)
            {
                //setup the dataset that will store the results
                userDS = new DataSet("userDS");
                DataTable dt = userDS.Tables.Add("users");
                DataRow dr;
           
                //add each parameter as a column
                foreach(string prop in columns)
                {
                    dt.Columns.Add(prop, typeof(string));
                }

                using (SearchResultCollection src = ds.FindAll())
                {
                    foreach(SearchResult sr in src)
                    {
                        dr = dt.NewRow();
                        foreach(string prop in columns)
                        {
                            if(sr.Properties.Contains(prop))
                            {
                                dr[prop] = sr.Properties[prop][0];
                            }
                        }
                        dt.Rows.Add(dr);
                    }
                }
            }
            //cache it for later, with sliding 3 minute window
            context.Cache.Insert(sFilter, userDS, null, DateTime.MaxValue, TimeSpan.FromSeconds(180));
        }
        return userDS;
    }


    Now, just place a datagrid (or dropdown) on your page, and with a few lines of code, you have a searcher:

    //sample use
    string qry = String.Format("(&(objectCategory=person)(givenName={0}*))", txtFirstName.Text);
    string[] columns = new string[]{"givenName", "sn", "cn", "sAMAccountName", "telephoneNumber", "l"}
    string ldapPath = "LDAP://dc=mydomain";

    DataSet ds = FindUsers(qry, columns, ldapPath, true);
    DataGrid1.DataSource = ds;
    DataGrid1.DataBind();

  • 相关阅读:
    Arc Catalog重建索引时报错:ORA02298: 无法验证 (SDE.A18_FK1) 未找到父项关键字 (A18_FK1)
    网站复杂信息自动录入处理
    httpModules remove does not work in a folder or virtual directory
    事件触发型ActiveX放置在网页中的部分思考
    Javascript中文字符串处理额外注意事项
    指定web.config让httphandler处理某目录及子目录下所有文件
    windows命令行里取得年月日时分秒的办法
    手工删除打印任务
    数据绑定表达与javascript字符串连用
    【kserve】kserve安装记录
  • 原文地址:https://www.cnblogs.com/tommyli/p/708345.html
Copyright © 2020-2023  润新知