Roslyn (Lambda) Expression Bodied Property Syntax - c#

I wrote a function to convert LocalDeclaration's to Global Resources. Right now I'm replacing with each definition with a property, but I want to replace it with a property using the new syntax =>
public PropertyDeclarationSyntax ConvertToResourceProperty(string resouceClassIdentifier, string fieldName, string resourceKey, CSharpSyntaxNode field)
{
var stringType = SyntaxFactory.ParseTypeName("string");
var resourceReturnIdentifier = SyntaxFactory.IdentifierName(resouceClassIdentifier + "." + resourceKey);
var returnResourceStatement = SyntaxFactory.ReturnStatement(resourceReturnIdentifier).NormalizeWhitespace();
var getRescourceBlock = SyntaxFactory.Block(returnResourceStatement);
var getAccessor = SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration, getRescourceBlock).WithAdditionalAnnotations(Formatter.Annotation, Simplifier.Annotation);
var propertyDeclaration = SyntaxFactory.PropertyDeclaration(stringType, fieldName).AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword), SyntaxFactory.Token(SyntaxKind.StaticKeyword)).NormalizeWhitespace();
propertyDeclaration = propertyDeclaration.AddAccessorListAccessors(getAccessor).WithAdditionalAnnotations(Formatter.Annotation);
SyntaxTrivia[] leadingTrivia = field.GetLeadingTrivia().ToArray() ?? new[] { SyntaxFactory.Whitespace("\t") };
return propertyDeclaration.WithTrailingTrivia(SyntaxFactory.Whitespace("\r\n"))
.WithLeadingTrivia(leadingTrivia)
.WithAdditionalAnnotations(Simplifier.Annotation);
}
This code create a property like so:
public static string LocalResourceName
{
get{ return Resources.LocalResourceName; }
}
I would like it to make the property like so:
public static string LocalResourceName =>Resources.LocalResourceName;
I'm not too sure what will create an expression bodied property from the syntaxfactory? Can anyone point me to the right method?

After scouring the internet I've found a way to do it. Why is there no documentation for roslyn?
public PropertyDeclarationSyntax ConvertToResourceProperty(string resouceClassIdentifier, string fieldName, string resourceKey, CSharpSyntaxNode field)
{
var stringType = SyntaxFactory.ParseTypeName("string");
var resourceClassName = SyntaxFactory.IdentifierName(resouceClassIdentifier);
var resourceKeyName = SyntaxFactory.IdentifierName(resourceKey);
var memberaccess = SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, resourceClassName, resourceKeyName);
var propertyLambda = SyntaxFactory.ArrowExpressionClause(memberaccess);
var propertyDeclaration = SyntaxFactory.PropertyDeclaration(new SyntaxList<AttributeListSyntax>(), new SyntaxTokenList(),
stringType, null, SyntaxFactory.Identifier(fieldName), null,
propertyLambda, null, SyntaxFactory.Token(SyntaxKind.SemicolonToken))
.AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword),
SyntaxFactory.Token(SyntaxKind.StaticKeyword)).WithAdditionalAnnotations(Formatter.Annotation).NormalizeWhitespace();
return propertyDeclaration.WithTrailingTrivia(SyntaxFactory.ElasticCarriageReturnLineFeed)
.WithLeadingTrivia(field.GetLeadingTrivia().ToArray())
.WithAdditionalAnnotations(Simplifier.Annotation);
}

Related

String interpolation: How do I make this function work with any type

This is a function to work with lists in string interpolation. It takes a List and an inner Func, and it appends the string result of the inner Func called for each member of the list, with a separator.
So the following builds a valid start of an Insert statement...
static void Main(string[] args)
{
var tableName = "customers";
var cols = new List<dynamic>
{
new { Name = "surname"},
new { Name = "firstname"},
new { Name = "dateOfBirth"}
};
Func<List<dynamic>, Func<dynamic, string>, string, string> ForEach = (list, func, separator) =>
{
var bldr = new StringBuilder();
var first = true;
foreach (var obj in list)
{
if (!first)
bldr.Append(separator);
first = false;
bldr.Append(func(obj));
}
return bldr.ToString();
};
var InsertStatement = $"Insert into { tableName } ( {ForEach(cols, col => col.Name, ", ")} )";
Console.WriteLine(InsertStatement);
Console.ReadLine();
}
Outputs...
Insert into customers ( surname, firstname, dateOfBirth )
It works for dynamic. How do I make it work for any type? The outer Func shouldn't care about the Type in the list, it just passes it through to the inner Func.
The .NET framework already gives you a generic function to achieve what you are trying to do String.Join and you can combine it with a LINQ Select statement, which will allow you to use a lambda on a generic type to select the property that you want to print. You can view the source code of these methods if you are interested as they are open source.
using System;
using System.Collections.Generic;
using System.Linq;
public class MyType
{
public string Name { get; set; }
}
public class Program
{
public static void Main()
{
var tableName = "customers";
var cols = new List<MyType>
{
new MyType { Name = "surname"},
new MyType { Name = "firstname"},
new MyType { Name = "dateOfBirth"}
};
var InsertStatement = $"Insert into { tableName } ( {String.Join(", ", cols.Select(col => col.Name))} )";
Console.WriteLine(InsertStatement);
}
}
Replace dynamic with object, or TValue with a type constraint stipulating it must be a class (where TValue : class), and call obj.ToString() instead of just obj
However, this doesn't guarantee it would "work with any type" - for that you need to know that those types all follow a contract to output the desired column name as their string representation. To get more specificity, require that your accepted types must implement some interface eg IColumnName and put that interface into the type constraint instead
You can create the text easily like this:
var query = $"INSERT INTO {tableName}({string.Join(",", cols.Select(x=>x.Name))})";
However, if for learning purpose you are going to handle the case using a generic method, you can create a generic function like the following and then easily use a for loop and strip additional separator using TrimEnd, or as a better option, like String.Join implementation of .NET Framework get enumerator like this:
string Join<TItem>(
IEnumerable<TItem> items, Func<TItem, string> itemTextSelecor, string separator)
{
var en = items.GetEnumerator();
if (!en.MoveNext())
return String.Empty;
var builder = new StringBuilder();
if (en.Current != null)
builder.Append(itemTextSelecor(en.Current));
while (en.MoveNext())
{
builder.Append(separator);
if (en.Current != null)
builder.Append(itemTextSelecor(en.Current));
}
return builder.ToString();
}
And use it this way:
var tableName = "customers";
var cols = new[]
{
new { Name = "surname"},
new { Name = "firstname"},
new { Name = "dateOfBirth"}
};
var InsertStatement = $"INSERT INTO {tableName} ({Join(cols, col => col.Name, ", ")})"
+ $"VALUES({Join(cols, col => $"#{col.Name}", ", ")})";

c# - dynamic string interpolation [duplicate]

This question already has answers here:
Is there a "String.Format" that can accept named input parameters instead of index placeholders? [duplicate]
(9 answers)
Closed 4 years ago.
I'm trying to format some string dynamically with available variables in a specific context/scope.
This strings would have parts with things like {{parameter1}}, {{parameter2}} and these variables would exist in the scope where I'll try to reformat the string. The variable names should match.
I looked for something like a dynamically string interpolation approach, or how to use FormattableStringFactory, but I found nothing that really gives me what I need.
var parameter1 = DateTime.Now.ToString();
var parameter2 = "Hello world!";
var retrievedString = "{{parameter2}} Today we're {{parameter1}}";
var result = MagicMethod(retrievedString, parameter1, parameter2);
// or, var result = MagicMethod(retrievedString, new { parameter1, parameter2 });
Is there an existing solution or should I (in MagicMethod) replace these parts in the retrievedString with matching members of the anonymous object given as parameter (using reflection or something like that)?
EDIT:
Finally, I created an extension method to handle this:
internal static string SpecialFormat(this string input, object parameters) {
var type = parameters.GetType();
System.Text.RegularExpressions.Regex regex = new System.Text.RegularExpressions.Regex( "\\{(.*?)\\}" );
var sb = new System.Text.StringBuilder();
var pos = 0;
foreach (System.Text.RegularExpressions.Match toReplace in regex.Matches( input )) {
var capture = toReplace.Groups[ 0 ];
var paramName = toReplace.Groups[ toReplace.Groups.Count - 1 ].Value;
var property = type.GetProperty( paramName );
if (property == null) continue;
sb.Append( input.Substring( pos, capture.Index - pos) );
sb.Append( property.GetValue( parameters, null ) );
pos = capture.Index + capture.Length;
}
if (input.Length > pos + 1) sb.Append( input.Substring( pos ) );
return sb.ToString();
}
and I call it like this:
var parameter1 = DateTime.Now.ToString();
var parameter2 = "Hello world!";
var retrievedString = "{parameter2} Today we're {parameter1}";
var result = retrievedString.SpecialFormat( new { parameter1, parameter2 } );
Now, I don't use double braces anymore.
You can use reflection coupled with an anonymous type to do this:
public string StringFormat(string input, object parameters)
{
var properties = parameters.GetType().GetProperties();
var result = input;
foreach (var property in properties)
{
result = result.Replace(
$"{{{{{property.Name}}}}}", //This is assuming your param names are in format "{{abc}}"
property.GetValue(parameters).ToString());
}
return result;
}
And call it like this:
var result = StringFormat(retrievedString, new { parameter1, parameter2 });
While not understanding what is the dificulty you're having, I'm placing my bet on
Replace( string oldValue, string newValue )
You can replace your "tags" with data you want.
var parameter1 = DateTime.Now.ToString();
var parameter2 = "Hello world!";
var retrievedString = "{{parameter2}} Today we're {{parameter1}}";
var result = retrievedString.Replace("{{parameter2}}", parameter2).Replace({{parameter1}}, parameter1);
EDIT
Author mentioned that he's looking at something that will take parameters and iterate the list. It can be done by something like
public static void Main(string[] args)
{
//your "unmodified" srting
string text = "{{parameter2}} Today we're {{parameter1}}";
//key = tag(explicitly) value = new string
Dictionary<string, string> tagToStringDict = new Dictionary<string,string>();
//add tags and it's respective replacement
tagToStringDict.Add("{{parameter1}}", "Foo");
tagToStringDict.Add("{{parameter2}}", "Bar");
//this returns your "modified string"
changeTagWithText(text, tagToStringDict);
}
public static string changeTagWithText(string text, Dictionary<string, string> dict)
{
foreach (KeyValuePair<string, string> entry in dict)
{
//key is the tag ; value is the replacement
text = text.Replace(entry.Key, entry.Value);
}
return text;
}
The function changeTagWithText will return:
"Bar Today we're Foo"
Using this method you can add all the tags to the Dictionary and it'll replace all automatically.
If you know order of parameters, you can use string.Format() method (msdn). Then, your code will look like:
var parameter1 = DateTime.Now.ToString();
var parameter2 = "Hello world!";
var retrievedString = "{{0}} Today we're {{1}}";
var result = string.Format(retrievedString, parameter2, parameter1);

Getting parameters of Func<T> variable

I have a rather complicated issue. I am trying to get a unique key from a method and its formal and actual parameters. The goal of the method, is to take a method call, and return a unique key based on 1) The name of the class and method and 2) The name and values of the parameters it is called with.
The method looks like this (sorry for all the details, but I can't find a sensible way to make the example smaller yet still explain my problem)
public class MethodKey
{
public static string GetKey<T>(Expression<Func<T>> method, params string[] paramMembers)
{
var keys = new Dictionary<string, string>();
string scope = null;
string prefix = null;
ParameterInfo[] formalParams = null;
object[] actual = null;
var methodCall = method.Body as MethodCallExpression;
if (methodCall != null)
{
scope = methodCall.Method.DeclaringType.FullName;
prefix = methodCall.Method.Name;
IEnumerable<Expression> actualParams = methodCall.Arguments;
actual = actualParams.Select(GetValueOfParameter<T>).ToArray();
formalParams = methodCall.Method.GetParameters();
}
else
{
// TODO: Check if the supplied expression is something that makes sense to evaluate as a method, e.g. MemberExpression (method.Body as MemberExpression)
var objectMember = Expression.Convert(method.Body, typeof (object));
var getterLambda = Expression.Lambda<Func<object>>(objectMember);
var getter = getterLambda.Compile();
var m = getter();
var m2 = ((System.Delegate) m);
var delegateDeclaringType = m2.Method.DeclaringType;
var actualMethodDeclaringType = delegateDeclaringType.DeclaringType;
scope = actualMethodDeclaringType.FullName;
var ar = m2.Target;
formalParams = m2.Method.GetParameters();
//var m = (System.MulticastDelegate)((Expression.Lambda<Func<object>>(Expression.Convert(method.Body, typeof(object)))).Compile()())
//throw new ArgumentException("Caller is not a method", "method");
}
// null list of paramMembers should disregard all parameters when creating key.
if (paramMembers != null)
{
for (var i = 0; i < formalParams.Length; i++)
{
var par = formalParams[i];
// empty list of paramMembers should be treated as using all parameters
if (paramMembers.Length == 0 || paramMembers.Contains(par.Name))
{
var value = actual[i];
keys.Add(par.Name, value.ToString());
}
}
if (paramMembers.Length != 0 && keys.Count != paramMembers.Length)
{
var notFound = paramMembers.Where(x => !keys.ContainsKey(x));
var notFoundString = string.Join(", ", notFound);
throw new ArgumentException("Unable to find the following parameters in supplied method: " + notFoundString, "paramMembers");
}
}
return scope + "¤" + prefix + "¤" + Flatten(keys);
}
private static object GetValueOfParameter<T>(Expression parameter)
{
LambdaExpression lambda = Expression.Lambda(parameter);
var compiledExpression = lambda.Compile();
var value = compiledExpression.DynamicInvoke();
return value;
}
}
Then, I have the following test, which works OK:
[Test]
public void GetKey_From_Expression_Returns_Expected_Scope()
{
const string expectedScope = "MethodNameTests.DummyObject";
var expected = expectedScope + "¤" + "SayHello" + "¤" + MethodKey.Flatten(new Dictionary<string, string>() { { "name", "Jens" } });
var dummy = new DummyObject();
var actual = MethodKey.GetKey(() => dummy.SayHello("Jens"), "name");
Assert.That(actual, Is.Not.Null);
Assert.That(actual, Is.EqualTo(expected));
}
However, if I put the () => dummy.SayHello("Jens") call in a variable, the call fails. Because I then no longer get a MethodCallExpression in my GetKey method, but a FieldExpression (subclass of MemberExpression. The test is:
[Test]
public void GetKey_Works_With_func_variable()
{
const string expectedScope = "MethodNameTests.DummyObject";
var expected = expectedScope + "¤" + "SayHello" + "¤" + MethodKey.Flatten(new Dictionary<string, string>() { { "name", "Jens" } });
var dummy = new DummyObject();
Func<string> indirection = (() => dummy.SayHello("Jens"));
// This fails. I would like to do the following, but the compiler
// doesn't agree :)
// var actual = MethodKey.GetKey(indirection, "name");
var actual = MethodKey.GetKey(() => indirection, "name");
Assert.That(actual, Is.Not.Null);
Assert.That(actual, Is.EqualTo(expected));
}
The Dummy class SayHello method definitions are trivial:
public class DummyObject
{
public string SayHello(string name)
{
return "Hello " + name;
}
public string Meet(string person1, string person2 )
{
return person1 + " met " + person2;
}
}
I have two questions:
Is there any way to send the variable indirection to MethodKey.GetKey, and get it as a MethodCallExpression type?
If not, how can I get the name and value of the method supplied if I get a MemberExpression instead? I have tried a few bits in the "else" part of the code, but haven't succeeded.
Any help is appreciated.
Thanks in advance, and sorry for the long post.
The problem is you are putting it into the wrong type of variable. Your method expects Expression<Func<T>> and you are using a variable of type Func<string> to store it. The following should fix your problem:
Expression<Func<string>> foo = () => dummy.SayHello("Jens");
var actual = MethodKey.GetKey<string>(foo, "name");
converting a .net Func<T> to a .net Expression<Func<T>> discusses the differences between a Func and an Expression<Func> and converting between the two and at a glance it says don't. The compiler makes them into totally different things. So make it the right thing at compile time and it should work fine.
If this isn't an option then possibly an overload that takes a Func instead of an Expression might work for you.
Note that in both cases I would pass the variable directly rather than trying to make it into a new expression in your call.

Using reflection to retrieve a value from a list

I have a simple method which retrieves a table from an azure mobile service.
public static async List<T>GetDataFromListTable<T>()
{
var data = await MobileService.GetTable<T>().ToListAsync();
return data.Count != 0 ? data : null;
}
This works fine.
What I am trying to do is have another method that takes a parameter name which is returned from the service and return the value of that parameter. So far I have this
public static async Task<T> GetDataFromTable<T>(string paramName)
{
var k = Activator.CreateInstance(typeof(T));
var members = typeof(T).GetProperties().Select(t=>t.Name).ToList();
if (!members.Contains(paramName))
return (T)k;
var mn = typeof(T).GetProperties()[members.IndexOf(paramName)];
var data = GetDataFromListTable<T>();
var retval = data.Select(t => t.mn);
}
The issue is obviously that I can't do the Linq query as T doesn't contain mn. I can also not use
var retval = data.Select(t=>t.paramName);
as paramname is a just a string representation of a member within a class.
In a nutshell...
method 1 has the parameter name, grabs a list from method 2. From the returned list in method 2, find the parameter name and return the associated value.
Is there a way to do what I'm trying to do?
You can do:
var retval = data.Select(t => mn.GetGetMethod().Invoke(t, null));
or
var retval = data.Select(t => mn.GetValue(t, null));
You can also simplify your code with something like this (not tested, sorry):
public static async Task<T> GetDataFromTable<T>(string paramName)
{
var k = Activator.CreateInstance(typeof(T));
var mn = typeof(T).GetProperty(paramName);
if (mn == null)
return (T)k;
var data = GetDataFromListTable<T>();
var retval = data.Select(t => mn.GetGetMethod().Invoke(t, null));
...
}
I think using expression trees would be more convenient since you're working with collections. Your method signature needs to incorporate the types T and TResult since it is using Select which returns an IEnumerable<TResult>.
public static async Task<IEnumerable<TResult>> SelectData<T, TResult>(
string propertyName
)
{
if(string.IsNullOrWhiteSpace(propertyName))
{
return Enumerable.Empty<TResult>();
}
var dataTask = GetTableData<T>();
var tType = Expression.Parameter(typeof(T), "t");
var property = Expression.Property(tType, propertyName);
var selectExpression =
Expression.Lambda<Func<T, TResult>>(property, tType)
.Compile();
return (await dataTask).Select(selectExpression);
}
Isn't it possible to do this
var retval = data.Select(t => mn.GetValue(t, null));

Retrieve Optionsets in Dynamics 2011

I am using this code to retrieve global optionsets
var request = new RetrieveOptionSetRequest {Name = "OptionsetNameGoesHere"};
var retrieveOptionSetResponse =(RetrieveOptionSetResponse) DynamicsHandler._serviceProxy.Execute(request);
var retrievedOptionSetMetadata =(OptionSetMetadata) retrieveOptionSetResponse.OptionSetMetadata;
var optionList = retrievedOptionSetMetadata.Options.ToArray();
foreach (var optionMetadata in optionList)
{
Printout(optionMetadata.Label.LocalizedLabels[0].Label + "\n");
}
But how do I retrieve optionsets like AccountCategory (AccountCategoryCode) so that I can bind them to a Combobox?
You should get it with a RetrieveAttributeRequest. It will return an RetrieveAttributeResponse which contains the Property AttributeMetadata.
In your case it should be of type OptionSetMetadata, which is what you are looking for.
This is how I have solved this problem.
CRMBase is my base class with connection to the CRM instance. Codelanguage: C#
public static Dictionary<int, string> GetAll(CRMBase conn, string entityName, string attributeName)
{
OptionMetadataCollection result = RetrieveOptionSetMetaDataCollection(conn, entityName, attributeName);
return result.Where(r => r.Value.HasValue).ToDictionary(r => r.Value.Value, r => r.Label.UserLocalizedLabel.Label);
}
// Method to retrieve OptionSet Options Metadadata collection.
private static OptionMetadataCollection RetrieveOptionSetMetaDataCollection(CRMBase conn, string prmEntityName, string prmAttributeName)
{
RetrieveEntityRequest retrieveEntityRequest = new RetrieveEntityRequest();
retrieveEntityRequest.LogicalName = prmEntityName;
retrieveEntityRequest.EntityFilters = Microsoft.Xrm.Sdk.Metadata.EntityFilters.Attributes;
RetrieveEntityResponse retrieveEntityResponse = (RetrieveEntityResponse)conn._orgContext.Execute(retrieveEntityRequest);
return (from AttributeMetadata in retrieveEntityResponse.EntityMetadata.Attributes where
(AttributeMetadata.AttributeType == AttributeTypeCode.Picklist & AttributeMetadata.LogicalName == prmAttributeName)
select ((PicklistAttributeMetadata)AttributeMetadata).OptionSet.Options).FirstOrDefault();
}

Categories