Iterating through list selected from ManagementObjectCollection slow - c#

I am trying to help with a slow legacy application that gets certain data through the ManagementObjectCollection class. This function takes around 40 seconds, however it took a little over a minute when getting the data directly from the ManagementObjectCollection. We pared down the query and selected the data into a list which helped execution time, however it is still very slow considering how many elements are supposed to be here. I have the following code:
string baseStr;
ManagementObjectCollection moReturn;
ManagementObjectSearcher moSearch;
ManagementObjectSearcher moSearch32;
List<string> programs = new List<string>();
List<ProgramDescription> moList;
moSearch32 = new ManagementObjectSearcher("Select Name, Version, InstallDate from Win32_Product");
moReturn = moSearch32.Get();
moList = moReturn.Cast<ManagementObject>().AsEnumerable()
.Select(x => new ProgramDescription
{
Name = x["Name"],
Version = x["Version"],
InstallDate = x["installDate"]
}).ToList();
foreach (ProgramDescription currentMo in moList)
{
if (currentMo.Name == null)
continue;
else
{
baseStr = currentMo.Name.ToString() + ", " + currentMo.Version?.ToString()
+ ", " + currentMo.InstallDate?.ToString();
}
}
As you can see, I am attempting to convert this to a list of another object and iterate through that. However, I have noticed that even when doing this, the foreach loop takes an eternity despite the moList object only having ~290 items. The management object search and converting it to a list seem to happen instantly, but this iteration is still taking forever. Why is this? If this is just a quirk in relation to accessing these management objects, is there a more efficient way of getting this specific query?

Related

Creating strongly typed object from BigQuery Row in c#

After running my query in Google Cloud Platform, the results are of type BigQueryResults. To create an object from this results, I run a foreach loop across the rows of this results and keep adding them to the object as shown below:
var data = new List<DefectData>();
foreach (BigQueryRow row in results)
{
DefectData defectData = new DefectData();
defectData.DefectId = long.Parse(SqlUtility.GetValue(row, "DefectId"));
defectData.TrueSize = float.Parse(SqlUtility.GetValue(row, "TrueSize"));
defectData.WaferId = SqlUtility.GetValue(row, "WaferId");
defectData.DieX = int.Parse(SqlUtility.GetValue(row, "DieX"));
data.Add(defectData);
}
The results may have millions of records. The foreach loop takes too much time to create the object. I tried to use Parallel.foreach as well. That too doesn't help. How do I make this process of creating object to run faster?

Get value of DataContext Result column by variable name

I've been searching and playing around with GetType() for a while, but I keep getting the name of the column (which I already have) and not the value.
I'm trying to build a csv file from the results of a method (stored procedure call) in a datacontext.
I'm fine up to figuring out how to dynamically grab result.some_column_name from the result set.
using (SomeDataContext ctx = new SomeDataContext())
{
List<Some_MethodResult> results = ctx.Get_Some_Method(parameter1, parameter2, parameter3).ToList();
var colnames = ctx.Mapping.MappingSource.GetModel(typeof(SomeDataContext)).GetMetaType(typeof(Get_Some_MethodResult)).DataMembers;
foreach (Get_Some_MethodResult r in results)
{
foreach (var colname in colnames)
{
string line = "\"" + r.GetType().GetField(colname.Name).ToString() + "\",";
sb.Append(line);
}
}
The above gets me the name of the field, and I'm looking for the value. GetMember() doesn't seem to get me any better results.
I'm still looking to see if I can find out the right way to dynamically refer to a column by column name, but if this is simple and just a non-often-asked question, I'm hoping someone can point me in the right direction.
Save the result of GetField and call GetValue on it, passing r as the parameter.
But you don't want to be calling GetField inside a nested loop. Loop through the columns once, saving all the GetField() results into a FieldInfo[] array. Then inside your nested loops, fetch from the array.
If that isn't performant enough, you can use an expression tree to build a Func<Get_Some_MethodResult, object> from each field that accesses that field on any arbitrary object, and save an array of those instead of FieldInfo. (For properties, this was possible using GetGetMethod and CreateDelegate before expression trees). Then you wouldn't need any reflection at all inside the nested loops.
Of course, all of this complexity might be avoided if you skipped over your ORM mapping and just processed the IDataReader that came back from the stored procedure. A data reader provides easy indexed access to both column values and values.
It's really quite trivial to write a function that turns any IDataReader into CSV.
So, the answer has a couple of parts to it. Kudos to my co-worker Tony Colich for helping me learn on this one.
First, it's GetProperties() and not GetFields() that I should have been looking at for the column values returned by the sproc.
Knowing that helped immensely. The new loop looks like (please ignore the colval/boolean bit):
using (SomeDataContext ctx = new SomeDataContext())
{
List<Some_MethodResult> results = ctx.Get_Some_Method(parameter1, parameter2, parameter3).ToList();
var colnames = ctx.Mapping.MappingSource.GetModel(typeof(SomeDataContext)).GetMetaType(typeof(Get_Some_MethodResult)).DataMembers;
var props = typeof(Get_Some_MethodResult).GetProperties().Where(p => colnames.Any(n => p.Name == n.Name));
foreach (Get_Some_MethodResult r in results)
{
foreach (var colname in colnames)
{
bool firstcol = true;
foreach (var colname in colnames)
{
var prop = props.FirstOrDefault(p => p.Name == colname.Name);
if (prop != null)
{
string colval = "";
if (!firstcol)
{
colval = ",\"" + prop.GetValue(r, null).ToString() + "\"";
}
else
{
colval = "\"" + prop.GetValue(r, null).ToString() + "\"";
}
//var field =
sb.Append(colval);
firstcol = false;
}
}
sb.AppendLine();
}
}
Now, as far as whether or not I should have done this at all, there are only so many hours to spend on a project sometimes, and this was the path of least resistance. Thanks to all who responded!

Fastest way of getting properties of ManagementObject

I have a method that returns list of services on remote machine. I'm getting the ManagementObjectCollection using ManagementObjectSearcher.Get() and WIN32 query. Then in foreach loop I'm creating instance of my Service class and add it to result List. While initializing new Service I'm getting ManagementObject properties using GetPropertyValue(string). The problem i'm facing is that this process is very slow. I think that GetPropertyValue is slow(I'm using it 7 times per loop). Is there faster way of getting properties from ManagementObject class?
var query = new ObjectQuery("Select Name, DisplayName, ProcessId, Description, State, StartMode, StartName From Win32_Service");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query);
ManagementObjectCollection allServices = searcher.Get();
foreach (ManagementObject p in allServices)
{Service newService = new Service{ Name = p.GetPropertyValue("Name"),etc...} result.Add(newService);}
I have been fighting with this, trying to understand what is so slow.
I wrote a test program, and used Stopwatch to time practically everything.
I have a wql query that returns 3 devices from Win32_PnPSignedDriver.
I used three different methods to retrieve the results from the query.
ManagementObjectCollection queryResults;
ManagementObjectSearcher searcher = new ManagementObjectSearcher();
var myWql = "SELECT * FROM Win32_PnPSignedDriver WHERE ..."
searcher.Scope = new ManagementScope(#"root\CIMV2");
searcher.Query = new WqlObjectQuery(wmiQry);
queryResults = searcher.Get();
searcher.Get() is plenty fast.
I tested three methods of retrieving data from ManagementObjectCollection queryResults.
foreach (ManagementBaseObject device in queryResults) { }
IEnumerator enumerator = queryResults.GetEnumerator(); while (enumerator.MoveNext()) { }
queryResults.CopyTo(deviceArray, 0); foreach (var btDevice in deviceArray)
First round testing:
Method 1: Was very slow -- over 3000 ms to execute an empty loop.
Method 2: Was very fast. 1 ms
Method 3: Also very fast. 0 ms.
Then I saw the fault in my testing. The first loop counted the object in the collection, and then this was remembered by the framework. If I executed Method 1 several times, only the initial loop was slow, but then repeating the foreach was 0 to 1 ms.
I restructured my test to re-execute the query before each fetch of the data.
Method 1: Was slow every time.
Method 2: Also slow every time.
Method 3: My Stopwatch timing reported 0 to 1 ms, but I noticed the execution time was much longer. ???
Looking deeply into what I coded, I saw that I did not time the following line:
ManagementObject[] deviceArray = new ManagementObject[queryResults.Count];
Which is actually two commands:
var count = queryResults.Count;
ManagementObject[] deviceArray = new ManagementObject[count];
I timed each separately, and saw that the queryResults.Count took almost all the time. Often > 3000 ms.
I then hard-coded the size of the array to avoid the call: queryResults.Count
However, when I executed
queryResults.CopyTo(deviceArray, 0);
The CopyTo method still needed to know how many items are in the ManagementObjectCollection, and now the CopyTo took > 3000 ms where it had been 0 or 1 ms before.
So, it would appear that ManagementObjectCollection. get_Count is the bottleneck, and I do not know anyway to retrieve the results without causing the Count getter to be executed.

C# Using WMI To Query LogicalDisk Information

I'm looking to get performance information for the disks on my local computer using WMI. According to the documentation for Win32_PerfFormattedData_PerfDisk_LogicalDisk (http://msdn.microsoft.com/en-us/library/aa394261(v=vs.85).aspx), it looks like I should just be able to query the information and get the values.
However, when I run the code I get "0" for each value for the properties.
var selectQuery = new SelectQuery("Select * from Win32_PerfFormattedData_PerfDisk_LogicalDisk");
var searcher = new ManagementObjectSearcher(selectQuery);
foreach (ManagementObject disk in searcher.Get())
{
foreach (PropertyData property in disk.Properties)
{
var propertyValue = property.Value; // 0 value.
}
}
I have also queried Win32_PerfRawData_PerfDisk_LogicalDisk and I do get actual raw values. Of course, I would rather have the formatted data instead of the raw data. The documentation for converting the raw data to the formatted data seems to be lacking when it comes to using WMI.
Any idea why I am getting 0 for each value?
Here is a working example just tested this
You can use the WMI Performance Class Counters. An example of this would be polling the PerfDisk_LogicalDisk
ManagementObjectSearcher mos = new ManagementObjectSearcher("select * from Win32_PerfFormattedData_PerfDisk_LogicalDisk");
foreach (ManagementObject service in mos.Get())
{
foreach (PropertyData propData in service.Properties)
{
Console.WriteLine("{0} {1}", propData.Name, propData.Value);
}
}

Linq statement to retrieve printers with EnableBIDI = true

This should be an easy one for Linq guru's.
I wanna retrieve all the physical printers that are available to a user. As far as I can tell there is no property/setting that you can query which tells you whether a printer is a physical of a virtual one. But there's already a question about that topic : Is there a possibility to differ virtual printer from physical one?
I'm using a reliable-ish way to see if it's a virtual one, and I check whether the "EnableBIDI" property is set to true.
ManagementObjectSearcher searcher = new ManagementObjectSearcher(query);
ManagementObjectCollection coll = searcher.Get();
var installedPhysicalPrinters = new List<string>();
var scratch = from ...
The result should be that the installedPhysicalPrinters contains a list of installedprinter names.
I got this far
foreach (ManagementObject printer in coll)
{
var validPrinter = (from PropertyData p in printer.Properties
from ManagementObject pr in coll
where p.Name == "EnableBIDI" && (bool) p.Value == true
select p).FirstOrDefault();
if (validPrinter != null)
{
installedPrinters.Add(validPrinter.Value.ToString());
// this is just to see the properties
foreach (PropertyData property in printer.Properties)
{
lstMessages.Items.Add(string.Format("{0}: {1}", property.Name, property.Value));
}
}
}
Obviously this is wrong, and I end up with a list of "true" values in the list, instead of the printer names.
Using nested for eaches it's easy to solve, but that's sooo 2004...
The issue with combining LINQ and System.Management is that the latter uses older non-generic containers (like ManagementObjectCollection) that you can't use directly with LINQ because it uses the non-generic IEnumerable interface.
Luckily you can work around that, as in the following example:
static void Main(string[] args)
{
Func<PropertyData, bool> indicatesPhysical = property => property.Name == "EnableBIDI"; // && (bool) property.Value;
Func<ManagementBaseObject, bool> isPhysicalPrinter = obj => obj.Properties.OfType<PropertyData>().Any(indicatesPhysical);
var searcher = new ManagementObjectSearcher("Select * from Win32_Printer");
var collection = searcher.Get();
var physicalPrinters = collection.OfType<ManagementBaseObject>().Where(isPhysicalPrinter);
foreach (var item in physicalPrinters)
{
Console.WriteLine(item.Properties["DeviceID"].Value);
Console.WriteLine(item.Properties["EnableBIDI"].Value);
}
}
You can use the Enumerable.OfType<>() extension method to transform the specified IEnumerable interface into an IEnumerable<ManagementBaaseObject>. That will help in getting those queries along.
Your example on how to differentiate between physical and virtual printers didn't work on my end, but it's irrelevant to what the main problem is that you're dealing with, namely trying to make code more readable by using LINQ expressions.

Categories

Resources