I've a LINQ query like below:
foreach (var property in from property in properties where property.Name != "Type" select property)
{
}
how would you go about making this statement more concise without using the actual extension method which looks unattractive (i.e. without using .Where like: foreach (var property in properties.Where(...)).
You cant really..
You could put the query into a separate line.
var selectedProperties = from property in properties
where property.Name != "Type"
select property;
foreach (var property in selectedProperties)
{
}
Or you could factor the query out into a separate method if it is really huge.
foreach ( var property in ComplexSelectionOfProperties () )
...
But really I would say the exention method in this case is much neater. Its only when the queries get more complex and involve joins that the query syntax becomes tidier. (IMHO)
Beauty is always in the eye of the beholder :)
However in such a case I would go create a method that filters non-Type properties and iterate over its results.
Something like this:
IEnumerable<IProperty> GetNonTypeProperties(IEnumerable<IProperty> properties)
{
return (from property in properties where property.Name != "Type" select property);
}
void foo()
{
foreach (var property in GetNonTypeProperties(properties))
{
}
}
The lack of conciseness comes precisely from the sql style syntax : using a "dot" notation you will sensibly shorten your expression :
foreach (var property in properties.Where(property => property.Name != "Type"))
{
}
If you want to shorten the longest part which is obviously the boolean test, you have to put it elsewhere.
Either in the foreach loop itself :
foreach (var property in properties)
{
if(property.Name != "Type")
{
...
}
}
Either if a separate function :
foreach (var property in properties.Where(IsNotType))
{
}
//and farther :
bool IsNotType(Property p)
{
return property.Name != "Type";
}
But anyway you want to perform a loop with a test on each element, so in a way or another you will have to code that and it will take a minimum amount of characters.
Just in case that you don't like the lambda expression, not the extension method itself, you can make your own extension method with query inside, like this:
public static IEnumerable<Property> PropertiesExceptType(this IEnumerable<Property> properties) {
return from property in properties
where property.Name != "Type"
select property;
}
and use it:
foreach(var property in properties.PropertiesExceptType()) {
// ...
}
The good thing about encapsulating your query in separate method is that you can debug the method with loop and change the code on the fly (VS won't let you do this if you have a linq query right inside this method).
I honestly don't see anything wrong with var propery in properties.Where(), it's much better than any query expression in this context IMO. But if you want to stick with your query, at least introduce a variable:
var filteredProperties = from property in properties
where property.Name != "Type"
select property;
foreach(var property in filteredProperties)
{
// ...
}
People who will read and debug it later will thank you. But I still think that extension method is the way to go here
Related
In C#, I'm going to use lambda expression, I have such a code
var item = dbContext.Products.ToList();
How can i get a property of Product table.
try this
var item = dbContext.Products.FirstOrDefault().Name;
With Lamba expression normally you can access and read information from "list" or in this case, IQueryable objects.
With your code you can access to objects with something like this:
var item = dbContext.Products.FirstOrDefault();
// item may be null if products table is empty
if (item != null)
{
// now you can access at object properties (example)
var data = item.PropertyData;
}
Your question may open others way that including reflection for exploring object without well known class definition...
If you want to get the property for each product with lambda expression, then you should make a lambda expression like x => x.Prop when you do your query
if (dbContext.Products != null){
var list = dbContext.Products.ToList();
var query = list.Select(x => x.Prop //your property will appear here...
}
Well, I need to repeat same code for many properties.
I've seen examples taking Action delegates, but they don't fit quite well here.
I want something like this: (see explanation below)
Dictionary<Property, object> PropertyCorrectValues;
public bool CheckValue(Property P) { return P.Value == PropertyCorrectValues[P]; }
public void DoCorrection(Property P) { P.Value = PropertyCorrectValues[P]; }
.
I want to have a dictionary containing many properties and their respective "correct" values. (I know it's not well declared, but that's the idea). Properties are not necessarely inside my class, some of them are in objects of different assemblies.
A method bool CheckValue(Property). This method must access the actual value of the property and compare to the correct value.
And a method a void DoCorrection(Property). This one sets the property value to the correct value.
Remember I have many of those properties, I wouldn't like to call the methods by hand for each property. I'd rather iterate through the dicionary in a foreach statement.
So, the main question is in the title.
I've tried the by ref, but properties don't accept that.
Am I obligated to use reflection??? Or is there another option (if I need, reflection answer will be accepted as well).
Is there anyway I can make a dictionary with pointers in C#? Or some kind of assignment that changes the value of variable's target instead of changing the target to another value?
Thanks for the help.
You can do this using reflection. Get a list of the properties on the object of interest with typeof(Foo).GetProperties(). Your PropertyCorrectValues property can have type IDictionary<PropertyInfo, object>. Then use the GetValue and SetValue methods on PropertyInfo to perform the desired operations:
public bool CheckProperty(object myObjectToBeChecked, PropertyInfo p)
{
return p.GetValue(myObjectToBeChecked, null).Equals(PropertyCorrectValues[p]);
}
public void DoCorrection(object myObjectToBeCorrected, PropertyInfo p)
{
p.SetValue(myObjectToBeCorrected, PropertyCorrectValues[p]);
}
In addition to Ben's code I'd like to contribute the following code fragment:
Dictionary<string,object> PropertyCorrectValues = new Dictionary<string,object>();
PropertyCorrectValues["UserName"] = "Pete"; // propertyName
PropertyCorrectValues["SomeClass.AccountData"] = "XYZ"; // className.propertyName
public void CheckAndCorrectProperties(object obj) {
if (obj == null) { return; }
// find all properties for given object that need to be checked
var checkableProps = from props
in obj.GetType().GetProperties()
from corr in PropertyCorrectValues
where (corr.Key.Contains(".") == false && props.Name == corr.Key) // propertyName
|| (corr.Key.Contains(".") == true && corr.Key.StartsWith(props.DeclaringType.Name + ".") && corr.Key.EndsWith("." + props.Name)) // className.propertyName
select new { Property = props, Key = corr.Key };
foreach (var pInfo in checkableProps) {
object propValue = pInfo.Property.GetValue(obj, null);
object expectedValue = PropertyCorrectValues[pInfo.Key];
// checking for equal value
if (((propValue == null) && (expectedValue != null)) || (propValue.Equals(expectedValue) == false)) {
// setting value
pInfo.Property.SetValue(obj, expectedValue, null);
}
}
}
When using this "automatic" value correction you might also consider:
You cannot create a PropertyInfo object just by knowing the property name and independently of the declaring class; that's why I chose string for the key.
When using the same property name in different classes then you might need to change the code that is doing the actual assignment because the type between the correct value and the property type might differ.
Using the same property name in different classes will always perform the same check (see point above), so you might need a syntax for property names to restrict it to a specific class (simple dot notation, doesn't work for namespaces or inner classes, but might be extended to do so)
If needed you can replace the "check" and "assign" part with separate method calls, but it might be done inside the code block as stated in my example code.
I can get a list from the solution of all calls to a particuliar method using the following code:
var createCommandList = new List<MethodSymbol>();
INamedTypeSymbol interfaceSymbol =
(from p
in solution.Projects
select p.GetCompilation().GetTypeByMetadataName(
"BuySeasons.BsiServices.DataResource.IBsiDataConnection")
).FirstOrDefault();
foreach (ISymbol symbol in interfaceSymbol.GetMembers("CreateCommand"))
{
if (symbol.Kind == CommonSymbolKind.Method
&& symbol is MethodSymbol)
{
createCommandList.Add(symbol as MethodSymbol);
}
}
foreach (MethodSymbol methodSymbol in createCommandList)
{
foreach (ReferencedSymbol referenceSymbol
in methodSymbol.FindReferences(solution))
{
foreach (ReferenceLocation referenceLocation
in from l
in referenceSymbol.Locations
orderby l.Document.FilePath
select l)
{
if (referenceLocation.Location.GetLineSpan(false)
.StartLinePosition.Line ==
referenceLocation.Location.GetLineSpan(false)
.EndLinePosition.Line)
{
Debug.WriteLine("{0} {1} at {2} {3}/{4} - {5}",
methodSymbol.Name,
"(" + String.Join(",",
(from p
in methodSymbol.Parameters
select p.Type.Name + " " + p.Name).ToArray()
) + ")",
Path.GetFileName(referenceLocation.Location.GetLineSpan(false)
.Path),
referenceLocation.Location.GetLineSpan(false)
.StartLinePosition.Line,
referenceLocation.Location.GetLineSpan(false)
.StartLinePosition.Character,
referenceLocation.Location.GetLineSpan(false)
.EndLinePosition.Character));
}
else
{
throw new ApplicationException("Call spans multiple lines");
}
}
}
}
But this gives me a list of ReferencedSymbol. Although this gives me the file and line number that the method is called from I would also like to get the specific arguments that the method is called with. How can I either convert what I have or get the same information with Roslyn? (notice the I first load the solution with the Solution.Load method and then loop through to find out where the method is defined/declared (createCommandList)).
You are already using Roslyn here. When you have a referenceSymbol, you can get at the Method Declaration Syntax and then look down into the tree to get the Parameter list.
I've inserted a arguments variable that uses your referenceSymbol:
// Snip start
foreach (MethodSymbol methodSymbol in createCommandList)
{
foreach (ReferencedSymbol referenceSymbol
in methodSymbol.FindReferences(solution))
{
var arguments = referenceSymbol.Definition.DeclaringSyntaxNodes.First()
.DescendantNodes().OfType<ParameterSyntax>().ToList();
foreach (ReferenceLocation referenceLocation in
from l
in referenceSymbol.Locations
orderby l.Document.FilePath
select l)
{
// Snip end
When you perform a Debug output, you can then use that list of arguments to get the names.
My solution requires getting the First() of the DeclaringSyntaxNodes, which I don't like very much but cannot find another way to get at the Descendant Nodes of the Syntax Tree.
I have discovered another way of getting the Parameter list from a method that might work for others as well.
Say I have the following method that has two parameters:
public string DebugPage(string enabled, string show){
//do stuff
}
Then you get your nodes however you wish. For example this gives me a list of public methods:
IEnumerable<MethodDeclarationSyntax> methods = from m in root.DescendantNodes().OfType<MethodDeclarationSyntax>() where m.Modifiers.ToString().Contains("public") select m;
Then I can iterate through that list of methods to get at the method's ParameterList property which is exposed to make this operation really easy. By the end of this loop the list parameters will hold the names of each parameter in the method (in the example of the Debug method above it will hold the values enabled and show as expected):
var parameters = new List<string>();
foreach (var method in methods)
{
foreach (var n in method.ParameterList.Parameters)
{
var parameterName = n.Identifier.Text;
parameters.Add(parameterName);
}
}
You can search the syntax tree of the reference at the specific source location it occurs to find the node you are looking for. You'll need to use a call like DescendentNodes from the tree's root node and you'll probably need to request the language specific node type you are looking for. Once you have the node in the referecing tree you can then use that tree's semantic model to tell you other information about the arguments.
I have a single instance of an object:
AS_SYSTEM system = ctx.AS_SYSTEM.Where(s => s.SYSTEM_ID == query).First();
And i want to remove some properties from it. All properties that ends with "Reference". Something like
system.GetType().GetProperties().Name.EndsWith("Reference")
I want to remove all ef properties that are linked to other tables.
To nullify (the values) of all properties ending with 'Reference', using reflection:
var properties = system.GetType().GetProperties().Where(x => x.Name.EndsWith("Reference"));
foreach (var p in properties)
{
p.SetValue(system, null, null);
}
Though I'm not sure that you really need to use reflection here, thats how its done.
I have a rather complex database, where we are using Linq to SQL. I'm creating a model layer where I would like to only have one method. But my problem is that we often like to order the collection. Is it possible somehow to accomplish something like this:
public static List<Object> GetObject(Object.Property)
{
return some Linq.ToList();
}
I know I can use linq on my list afterwards.
Hmm it was maybe a bit to diffuse question.
OK I solved it with reflection and a string in the argument..
MyObjectDataDataContext context = new MyObjectDataDataContext ();
PropertyInfo[] piArray = context.MyObject.GetType().GetProperties();
PropertyInfo pi = piArray.FirstOrDefault(s => s.Name == "property");
if (pi != null)
{
return context.MyObject.OrderBy(t => pi.PropertyType);
}
I guess you are trying to access the same data, but depending on a 'column' criteria, return the data sorted?
Once you have the IEnumerable data, you can sort it as follows:
list.OrderBy(t => t.ColumnYouWantToSortBy)
As in the following documentation