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.
Related
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?
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!
I have a linq query as follows,
var result =
from Record in DBContext.GetAll().
group new { Record.Filed1, Record.Field2} by Record.Field3
into newGroup
select new
{
BodyRegion = newGroup.Key,
ByScanner =
from exam in newGroup
group exam.MaxValue by exam.Model
into myGroup
select myGroup,
ByExam =
from exam in newGroup
group exam.MaxValue by exam.Protocol
into myGroup2
select myGroup2
};
Then I iterate throught them,
foreach (var rec in result)
{
foreach (var byScanner in rec.ByScanner)
{
ProcessResult(byScanner.Key, byScanner.ToList());
}
foreach (var byExam in rec.ByExam )
{
ProcessResult(byExam.Key, byExam.ToList());
}
}
Everything works fine.
But Iwant to move Linq query (first code snippet) to a function, what should be the return type the function?
Return type of a function can not be var. If I give IEnumerable< Object > then while iterating I can't access rec.ByScanner, rec.ByExam because Object doesn't contain them.
How to resolve this issue?
EDIT:
I tried by creating a new class and filling them in that. But Grouping attributes byScanner.Key, byScanner.ToList() are not accessible. How this can be solved?
You are using an Anonymous Type. These shouldn't be passed around methods.
One thing you can do is create (Let's call it 'Record') a class with properties BodyRegion, ByScanner and ByExam and pass IEnumerable<Record>.
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);
}
}
This probably isn't the best way, but I am currently retrieving the amount of RAM on a machine using:
manageObjSearch.Query = new ObjectQuery("SELECT TotalVisibleMemorySize FROM Win32_OperatingSystem");
manageObjCol = manageObjSearch.Get();
foreach (ManagementObject mo in manageObjCol)
sizeInKilobytes = Convert.ToInt64(mo["TotalVisibleMemorySize"]);
It works well and good, but I feel I could be doing this more directly and without a foreach over a single element, but I can't figure out how to index a ManagementObjectCollection
I want to do something like this:
ManagementObject mo = new ManagementObject("Win32_OperatingSystem.TotalVisibleMemorySize")
mo.Get();
Console.WriteLine(mo["TotalVisibleMemorySize"].ToString())
or maybe even something like
ManagementClass mc = new ManagementClass("Win32_OperatingSystem");
Console.WriteLine(mc.GetPropertyValue("TotalVisibleMemorySize").ToString());
I just can't seem to figure it out. Any ideas?
The foreach statement is hiding the enumerator you need to access. You can do it directly like this:
var enu = manageObjSearch.Get().GetEnumerator();
if (!enu.MoveNext()) throw new Exception("Unexpected WMI query failure");
long sizeInKilobytes = Convert.ToInt64(enu.Current["TotalVisibleMemorySize"]);