Simpleton
Simpleton

Reputation: 797

Optimizing WMI query with Linq

First, I should start out by admitting that I really don't know a lot about using WMI or LINQ for that matter. So I'm sure it wont surprise anyone that I'm having such a difficult time with it!

Trouble is that I'm trying to query or return several values from the same WMI object. Normally this wouldn't be to bad but I'm finding it difficult to rap my head around this due to a need to only return elements matching a specific set of data. The issue being, that I must first query WMI for a specific element or value and then restart the query, filtering by said element or value to return the required data set.

I'm sure that I could just loop through all the elements in the WMI object and append them to a collection. Then loop through the collection till I get what I'm looking for but then I still have the issue of filtering out all of the extra unnecessary elements.

So I guess my question is, whats the best way to do advanced searches of a WMI object and how can I optimize the process so it doesn't take so long to retrieve the information I'm looking for?

During my many searches, I found several references to people using LINQ but my limited exposure to LINQ means that I'm not really sure how to use it in this instance so any help or insights would be greatly appreciated.

Here's what I've come up with so far:

        ManagementObjectSearcher searcher = new ManagementObjectSearcher
            ("SELECT * FROM Win32_NetworkAdapterConfiguration where IPEnabled=true");
        IEnumerable<ManagementObject> objects = searcher.Get().Cast<ManagementObject>();
        string description = (from o in objects orderby o["IPConnectionMetric"] 
                              select o["Description"].ToString()).FirstOrDefault();

        _NICINDEX = (from o in objects orderby o["IPConnectionMetric"]
                     select o["Index"].ToString()).FirstOrDefault();

        _MACADDRESS = (from o in objects orderby o["IPConnectionMetric"]
                       select o["MACAddress"].ToString()).FirstOrDefault();

        _IPADDRESS = (from o in objects orderby o["IPConnectionMetric"]
                      select o["IPAddress"].ToString()).FirstOrDefault();

        _IPV6ADDRESS = (from o in objects orderby o["IPConnectionMetric"]
                        select o["IPAddress"].ToString()).FirstOrDefault();

        _SUBNETMASK = (from o in objects orderby o["IPConnectionMetric"]
                       select o["IPSubnet"].ToString()).FirstOrDefault();

        _GATEWAY = (from o in objects orderby o["IPConnectionMetric"]
                    select o["DefaultIPGateway"].ToString()).FirstOrDefault();

        _DNSSERVER = (from o in objects orderby o["IPConnectionMetric"]
                      select o["DNSServerSearchOrder"].ToString()).FirstOrDefault();

        _DNSSECSVR = (from o in objects orderby o["IPConnectionMetric"]
                      select o["DNSServerSearchOrder"].ToString()).FirstOrDefault();

        return description;

Upvotes: 2

Views: 5976

Answers (2)

Jeff Mercado
Jeff Mercado

Reputation: 134611

It would be easier to create a WMI query to get the data you need, convert it to a LINQ to Objects query, then iterate through the results, grabbing the bits of data you want.

var queryStr =
    "SELECT * " +
    "FROM Win32_NetworkAdapterConfiguration " +
    "WHERE IPEnabled = true";
using (var searcher = new ManagementObjectSearcher(queryStr))
using (var managementQuery = searcher.Get())
{
    // convert to LINQ to Objects query
    var query =
        from ManagementObject mo in managementQuery
        orderby Convert.ToUInt32(mo["IPConnectionMetric"])
        select new
        {
            Description          = Convert.ToString(mo["Description"]),
            Index                = Convert.ToString(mo["Index"]),
            MACAddress           = Convert.ToString(mo["MACAddress"]),
            IPAddress            = String.Join(", ", (string[])mo["IPAddress"] ?? new string[0]),
            IPSubnet             = String.Join(", ", (string[])mo["IPSubnet"] ?? new string[0]),
            DefaultIPGateway     = String.Join(", ", (string[])mo["DefaultIPGateway"] ?? new string[0]),
            DNSServerSearchOrder = String.Join(", ", (string[])mo["DNSServerSearchOrder"] ?? new string[0]),
        };

    // grab the fields
    String description = null;
    foreach (var item in query)
    {
        description  = description  ?? item.Description;
        _NICINDEX    = _NICINDEX    ?? item.Index;
        _MACADDRESS  = _MACADDRESS  ?? item.MACAddress;
        _IPADDRESS   = _IPADDRESS   ?? item.IPAddress;
        _IPV6ADDRESS = _IPV6ADDRESS ?? item.IPAddress;
        _SUBNETMASK  = _SUBNETMASK  ?? item.IPSubnet;
        _GATEWAY     = _GATEWAY     ?? item.DefaultIPGateway;
        _DNSSERVER   = _DNSSERVER   ?? item.DNSServerSearchOrder;
        _DNSSECSVR   = _DNSSECSVR   ?? item.DNSServerSearchOrder;
        // check if all fields are set so we can stop
        if (new[] { description, _NICINDEX, _MACADDRESS, _IPADDRESS, _IPV6ADDRESS, _SUBNETMASK, _GATEWAY, _DNSSERVER, _DNSSECSVR }.All(x => x != null))
            break;
    }
}

Though it's unclear to me why you would want to get the first of each of the fields individually. I would expect you would want the fields of the first adapter as a whole. So in the end, I think in the end it should just be:

var first = query.FirstOrDefault();
if (first != null)
{
    description  = first.Description;
    _NICINDEX    = first.Index;
    _MACADDRESS  = first.MACAddress;
    _IPADDRESS   = first.IPAddress;
    _IPV6ADDRESS = first.IPAddress;
    _SUBNETMASK  = first.IPSubnet;
    _GATEWAY     = first.DefaultIPGateway;
    _DNSSERVER   = first.DNSServerSearchOrder;
    _DNSSECSVR   = first.DNSServerSearchOrder;
}

Upvotes: 1

Servy
Servy

Reputation: 203847

So I'm not familiar with your particular DB provider, but your primary problem is that you're iterating the IEnumerable 10 times, which means 10 different queries to the database. You most certainly don't need to do that, and it's really killing your performance. You're also sorting the returned data 10 different times; you should only do it once.

Here's a refactor of your code to fetch the item once, and then get the 10 different values out of it:

ManagementObjectSearcher searcher = new ManagementObjectSearcher
            ("SELECT * FROM Win32_NetworkAdapterConfiguration where IPEnabled=true");
ManagementObject managementObject = searcher.Get()
    .Cast<ManagementObject>()
    .OrderBy(obj => obj["IPConnectionMetric"])
    .FirstOrDefault();

string description = managementObject["Description"];
_NICINDEX  = managementObject["Index"];
//repeat for the other 8 values following the above pattern
return description;

Note that if at all possible you should do the sorting on the DB side, and also restrict the output to just 1 value on the DB side. Without knowing the specifics of the query provider you're working with, I couldn't say what the preferable way of doing that is.

Upvotes: 2

Related Questions