Get string path properties from object path clearly in C# - c#

i need to have this result :
ProgrammeEtude.Description
So, i have done something like that and it work
modelMember = $"{nameof(Gabarit.ProgrammeEtude)}.{nameof(Gabarit.ProgrammeEtude.Description)}";
But it's ugly and if we have more than one class to reach, it will not be clean. So, i would like to know if it's possible to create a function to get the fullName property without the first class clearly. Only by calling a function
// Like that
modelMember = typeof(ProgrammeEtude).GetPropertyFullName(nameof(ProgrammeEtude.Description));
// Or like that
modelMember = GetPropertyFullName(ProgrammeEtude.Description);
Thank you!
Final solution help By Ecoron :
public void Test1()
{
var result = NameOf<Gabarit>(x => x.ProgrammeEtude.Description);
}
public static string NameOf<T>(Expression<Func<T, object>> selector)
{
return string.Join(".", selector.ToString().Split('.').Skip(1));
}

You can do it in runtime:
public class SomeClass
{
public SomeClass Other;
}
public class Tests
{
[Test]
public void Test1()
{
var result = NameOf<SomeClass>(x => x.Other.Other.Other);
}
public static string NameOf<T>(Expression<Func<T,object>> selector)
{
const string joinWith = ".";
return nameof(T) + joinWith + string.Join(joinWith, selector.ToString().Split('.').Skip(1));
}
}
Result: SomeClass.Other.Other.Other
You can play with this function to get desired result - with/out namespaces/indexes/separation select just start or end or skip something, etc.
Be aware that this working great only if you don't use some funky variables/enums inside which accessed by dot. For more correct version you should traverse expression yourself, but in this example Im just kinda lazy to write this all, and better to use simple approach.

Related

How do I get autoproperties on one line when generating code with Roslyn?

I have the following partial code which I use to generate datacontracts based on an excel-file we use for customer facing workshops and such.
private PropertyDeclarationSyntax[] GenerateProperties()
{
var props = new List<PropertyDeclarationSyntax>();
props.Add(SF.PropertyDeclaration(SF.ParseTypeName("IMigrationInformation"), "MigrationInformation")
.AddModifiers(SF.Token(SyntaxKind.PublicKeyword), SF.Token(SyntaxKind.OverrideKeyword))
.AddAccessorListAccessors(
SF.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
.WithBody(SF.Block(SF.ReturnStatement(SF.ObjectCreationExpression(SF.ParseTypeName($"{Form.RegistryName}MigrationInformation")))))
));
foreach (var field in Form.AllDataFields().Where(f => f.FieldTypeInfo != null))
{
props.Add(SF.PropertyDeclaration(SF.ParseTypeName(field.FieldTypeInfo.BackingType.Name), field.SafeName)
.AddModifiers(SF.Token(SyntaxKind.PublicKeyword))
.AddAccessorListAccessors(
SF.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration).WithSemicolonToken(SF.Token(SyntaxKind.SemicolonToken)),
SF.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration).WithSemicolonToken(SF.Token(SyntaxKind.SemicolonToken))
));
}
return props.ToArray();
}
The code works surprisingly well with one small snag. The code generated looks like this:
public string VariableName
{
get;
set;
}
And I really want it to look like this:
public string VariableName { get; set; }
Does anyone know how to do this, if possible?
I got the exact same problem. In my case, I was saving the code to file after converting it to a string something like this:
private void WriteCodeToFile(NamespaceDeclarationSyntax ns)
{
var codeAsString = ns.NormalizeWhitespace()
.ToFullString();
File.WriteAllText(destFileName, codeAsString);
}
So, in that case, I just solved it byrunning a regex replace on the string, in an extension method:
private static readonly Regex AutoPropRegex = new Regex(#"\s*\{\s*get;\s*set;\s*}\s");
public static string FormatAutoPropertiesOnOneLine(this string str)
{
return AutoPropRegex.Replace(str, " { get; set; }");
}
And then call it after converting it to string:
var codeAsString = ns.NormalizeWhitespace()
.ToFullString()
.FormatAutoPropertiesOnOneLine();
You are not speficying how you do your file-writing step (if any), so if this is not relevant at all in your case, I apologize for not targeting your question 100%. In any case, someone else might benefit from it.
You could implement a CSharpSyntaxRewriter and than apply it to a parent SyntaxNode:
public static class WhitespaceFormatter
{
public static SyntaxNode NormalizeWhitespacesSingleLineProperties(this SyntaxNode node) =>
node.NormalizeWhitespace().SingleLineProperties();
public static SyntaxNode SingleLineProperties(this SyntaxNode node) => new SingleLinePropertyRewriter().Visit(node);
class SingleLinePropertyRewriter : CSharpSyntaxRewriter
{
public override SyntaxNode VisitPropertyDeclaration(PropertyDeclarationSyntax node) =>
node.NormalizeWhitespace(indentation: "", eol: " ")
.WithLeadingTrivia(node.GetLeadingTrivia())
.WithTrailingTrivia(node.GetTrailingTrivia());
}
}
Use NormalizeWhitespacesSingleLineProperties to apply default whitespaces to everything but property declarations which will be written to a single line. Make sure not to call Format or NormalizeWhitespace() on your node afterwards, because it will again clutter your property declarations.
As one of simple solution (another way create a nodes and tokens with correct trivias) just use SyntaxNodeExtensions.NormalizeWhitespace(...) for nodes that you want to represent at the one line:
...
foreach (var field in Form.AllDataFields().Where(f => f.FieldTypeInfo != null))
{
props.Add(SF.PropertyDeclaration(SF.ParseTypeName(field.FieldTypeInfo.BackingType.Name), field.SafeName)
.AddModifiers(SF.Token(SyntaxKind.PublicKeyword))
.AddAccessorListAccessors(
SF.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration).WithSemicolonToken(SF.Token(SyntaxKind.SemicolonToken)),
SF.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration).WithSemicolonToken(SF.Token(SyntaxKind.SemicolonToken))
).NormalizeWhitespace(indentation: "", eol: " "));
}

Linq Expression to String

Here's a simple method in MVC that converts into html tag.
#Html.HiddenFor(model => model.myName);
This is converted into the below html.
<input type="hidden" name="myName" value="ABC..." />
How this expression is converted into the string. Let's suppose If I want to write this kind of method, how can I know the property name and its value. Suppose from the HiddenFor argument, how myName & myName value will be extracted.
This is not specific to MVC but related to LINQ expression.
Thanks a lot in advance.
This has nothing to do with LINQ (except that LINQ makes heavy use of those "expression trees").
model => model.MyProperty is a lambda expression which can be parsed when treated as an Expression<Func<Model, T>> instead of just a Func<Model, T>.
Let me answer your question by a (mostly self-explanatory) example:
public static string GetPropertyName<T>(Expression<Func<Model, T>> expr)
{
var member = (MemberExpression)expr.Body;
var property = (PropertyInfo)member.Member;
return property.Name;
}
which can be used as follows:
public class Model
{
public int MyProperty { get; set; }
}
static void Main(string[] args)
{
// Prints "MyProperty"
Console.WriteLine(GetPropertyName(model => model.MyProperty));
}
Have a look at the ExpressionHelper class in the Mvc source
https://github.com/aspnet/Mvc/blob/9c545aa343ccb1bf413888573c398fe56017d9ee/src/Microsoft.AspNet.Mvc.Core/Rendering/Expressions/ExpressionHelper.cs
The method GetExpressionText converts the lambda expression in question
Parsing expressions is hard. I had the same issue and there is a much simpler solution.
Although it is not as elegant, it is quick, easy, short and it handles subclasses.
Expression.Body.ToString() actually works and returns the whole expression as a string. The hardest work is to process the string afterward:
class Program
{
public class Car
{
public int Id;
public CarModel Model;
public string Owner ;
}
public class CarModel
{
public string Name;
public string Brand;
}
public static void Main(string[] args)
{
Console.WriteLine(ExpToString(x => x.Id));
Console.WriteLine(ExpToString(x => x.Owner));
Console.WriteLine(ExpToString(x => x.Model));
Console.WriteLine(ExpToString(x => x.Model.Name));
Console.WriteLine(ExpToString(x => x.Model.Brand));
}
// The inelegant solution that works
public static string ExpToString<T>(Expression<Func<Car, T>> exp)
{
var s = exp.Body.ToString();
return s.Remove(0, s.IndexOf('.') + 1);
}
}
The output:
Id
Owner
Model
Model.Name
Model.Brand
I am actually using this in WinForms for data binding initializations and I am pretty satisfied with it.

Reflection : get static property name

I need to get the property name of a static property dynamically called as a parameter.
Here is my Portable Class Library code:
public partial class Test
{
public Test()
{
string staticPropName = Test.GetPropName(Test.Row); // result must be "Row" without additional string
System.Diagnostics.Debug.WriteLine("propName=" + staticPropName);
}
public static int Row { get; set; }
public static string GetPropName(object Property)
{
return "Row"; // using reflection
}
}
I don't know the name of the property and I don't want to define it with an additional string.
You can't do that - when function is called it gets value of the property and have no idea where this value come from. Your sample is equivalent of
string staticPropName = Test.GetPropName(42);
which nobody would expect to return name.
You can try to require Expression as argument so you can actually inspect what method get called with like following staring point (https://stackoverflow.com/questions/1011109/how-do-you-get-the-name-of-the-property):
public static string GetPropName<TResult>(Expression<Func<TResult>> expression)
{
MemberExpression body = (MemberExpression)expression.Body;
return body.Member.Name;
}
string staticPropName = Test.GetPropName(()=> Test.Prop);
Note that you need checks to make sure expression is just one you expect and not something like () => Test + 42 or more complex one and report nice error.

Creating helper method that can be used in LINQ (EF)

Scenario:
I Have a table FooTable when column Foo is varchar(8) NOT NULL and the info in this column is like:
Foo
-----------
13940-00
13940-01
13940-02
13941-00
13941-01
Where the numeric part after the hyphen (-) always have two digits.
Problem:
I'm using Ado.net Entity Framework, and I created a class with 2 static methods to help get first and second part of the number:
public class HelperFoo
{
public static string Prefix(string value) { /* code here */ }
public static string Suffix(string value) { /* code here */ }
}
So now I can do something like this:
context.FooTable.Where(w => HelperFoo.Prefix(w.Foo) == "13940");
But, as you probably already know, this command line throws a NotSupportedException. It's because LINQ don't recognize HelperFoo.Prefix, so it can't convert the expression in SQL.
I can write a block of code in SQL that do the same that my methods of HelperFoo so I can create the SQL to my methos.
Question
Can I create something (class, method, or other) that makes LINQ knows my method when I executed the LINQ code?
EDITED
PS: I need something generic that works like a method or SQL function because I need to get this Prefix and Suffix in scenarios like Select, OrderBy, GroupBy and many others.
You could try creating your own IQueryable filters for the FooTable, a bit like this:
public static class Filters
{
public static IQueryable<FooTable> WithPrefix(this IQueryable<FooTable> item, string prefix)
{
return item.Where(i => i.Foo.StartsWith(prefix));
// note that this should be the same code you have in the
// Prefix() method inside HelperFoo...
}
}
Which you can use like this:
context.FooTable.WithPrefix("13940");
UPDATE: Sadly the second option here does not work:
Another option would be to have the helper methods not return a value but a Predicate<> for FooTable:
public class HelperFoo
{
public static Func<FooTable, bool> Prefix(string value)
{
return (i) => i.Foo.Substring(0, 5) == value;
}
public static Func<FooTable, bool> Suffix(string value) { /* code here */ }
}
And use it like this:
context.FooTable.Where(HelperFoo.Prefix("13940"));
Caveat: I'm not entirely sure the second method would not give you the same problem though.
With the the plethera of awnsers and you stating it needs to be more generic and you need the prefix and suffix avaliable to the Select, OrderBy, & GroupBy keywords, you should have the prefix and suffix in two different fields.
Foo Table
----------
Prefix | Suffix
----------------
10245 | 05
With that, you can query them individually to get what you want:
var resultSet = Db.Foo.Where(x => x.Suffix == "05").OrderBy(x => x.Prefix);
With this you can easily add a read-only property to get a formatted value:
public [partial] class Foo {
//Your other code
public string FormattedValue {
get { return Prefix + "-" + Suffix; }
}
}
You can use .StartsWith to check the prefix:
string prefix = "13940";
var result = context.FooTable.Where(w => w.Foo.StartsWith(prefix + "-"));
and .EndsWith to check the suffix:
string suffix = "02";
var result = context.FooTable.Where(w => w.Foo.EndsWith("-" + suffix));
You could create custom getters in your Foo class:
public class Foo
{
//your code
[NotMapped]
public string Prefix { get { return /*whatever*/; }
}
I think this should work.
You cannot filter your database selection based on a custom function - as you say, it cannot convert this to SQL.
For this specific problem, I could propose you use the StartsWith function, which does work on SQL server
context.FooTable.Where(w => w.Foo.StartsWith("13940"));
Use Microsoft.Linq.Translations.
It would look something like this:
partial class FooTable
{
private static readonly CompiledExpression<FooTable,string> prefixExpression
= DefaultTranslationOf<FooTable>.Property(e => e.Prefix).Is(e => e.Foo.Substring(0, 5));
public string Prefix
{
get { return prefixExpression.Evaluate(this); }
}
}
And queried like:
context.FooTable.Where(w => w.Prefix == "13940").WithTranslations();
Nuget gallery page
Documentation
EDIT: This solution works in Select, GroupBy, OrderBy.

how to turn a string into a linq expression?

Similar: Convert a string to Linq.Expressions or use a string as Selector?
A similar one of that one: Passing a Linq expression as a string?
Another question with the same answer: How to create dynamic lambda based Linq expression from a string in C#?
Reason for asking something which has so many similar questions:
The accepted answer in those similar questions is unacceptable in that they all reference a library from 4 years ago (granted that it was written by code master Scott Gu) written for an old framework (.net 3.5) , and does not provide anything but a link as an answer.
There is a way to do this in code without including a whole library.
Here is some sample code for this situation:
public static void getDynamic<T>(int startingId) where T : class
{
string classType = typeof(T).ToString();
string classTypeId = classType + "Id";
using (var repo = new Repository<T>())
{
Build<T>(
repo.getList(),
b => b.classTypeId //doesn't compile, this is the heart of the issue
//How can a string be used in this fashion to access a property in b?
)
}
}
public void Build<T>(
List<T> items,
Func<T, int> value) where T : class
{
var Values = new List<Item>();
Values = items.Select(f => new Item()
{
Id = value(f)
}).ToList();
}
public class Item
{
public int Id { get; set; }
}
Note that this is not looking to turn an entire string into an expression such as
query = "x => x.id == somevalue";
But instead is trying to only use the string as the access
query = x => x.STRING;
Here's an expression tree attempt. I still don't know if this would work with Entity framework, but I figure it is worth a try.
Func<T, int> MakeGetter<T>(string propertyName)
{
ParameterExpression input = Expression.Parameter(typeof(T));
var expr = Expression.Property(input, typeof(T).GetProperty(propertyName));
return Expression.Lambda<Func<T, int>>(expr, input).Compile();
}
Call it like this:
Build<T>(repo.getList(), MakeGetter<T>(classTypeId))
If you can use an Expression<Func<T,int>> in place of a just a Func, then just remove the call to Compile (and change the signature of MakeGetter).
Edit:
In the comments, TravisJ asked how he could use it like this: w => "text" + w.classTypeId
There's several ways to do this, but for readability I would recommend introducing a local variable first, like this:
var getId = MakeGetter<T>(classTypeId);
return w => "text" + getId(w);
The main point is that the getter is just a function, and you can use it exactly like you normally would. Read Func<T,int> like this: int DoSomething(T instance)
Here is an extension method for you with my testing code (linqPad):
class test
{
public string sam { get; set; }
public string notsam {get; set; }
}
void Main()
{
var z = new test { sam = "sam", notsam = "alex" };
z.Dump();
z.GetPropertyByString("notsam").Dump();
z.SetPropertyByString("sam","john");
z.Dump();
}
static class Nice
{
public static void SetPropertyByString(this object x, string p,object value)
{
x.GetType().GetProperty(p).SetValue(x,value,null);
}
public static object GetPropertyByString(this object x,string p)
{
return x.GetType().GetProperty(p).GetValue(x,null);
}
}
results:
I haven't tried this, and not sure if it would work, but could you use something like:
b => b.GetType().GetProperty(classTypeId).GetValue(b, null);

Categories