Dynamic string interpolation - c#

Can anyone help me with this?
Required Output: "Todo job for admin"
class Program
{
static void Main(string[] args)
{
Console.WriteLine(ReplaceMacro("{job.Name} job for admin", new Job { Id = 1, Name = "Todo", Description="Nothing" }));
Console.ReadLine();
}
static string ReplaceMacro(string value, Job job)
{
return value; //Output should be "Todo job for admin"
}
}
class Job
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}

Two suggestions:
DataBinder.Eval
string ReplaceMacro(string value, Job job)
{
return Regex.Replace(value, #"{(?<exp>[^}]+)}", match => {
return (System.Web.UI.DataBinder.Eval(new { Job = job }, match.Groups["exp"].Value) ?? "").ToString();
});
}
Linq.Expression
Use the Dynamic Query class provided in the MSDN LINQSamples:
string ReplaceMacro(string value, Job job)
{
return Regex.Replace(value, #"{(?<exp>[^}]+)}", match => {
var p = Expression.Parameter(typeof(Job), "job");
var e = System.Linq.Dynamic.DynamicExpression.ParseLambda(new[] { p }, null, match.Groups["exp"].Value);
return (e.Compile().DynamicInvoke(job) ?? "").ToString();
});
}
In my opinion, the Linq.Expression is more powerful, so if you trust the input string, you can do more interesting things, i.e.:
value = "{job.Name.ToUpper()} job for admin"
return = "TODO job for admin"

You can't use string interpolation this way. But you can still use the pre-C#6 way to do it using string.Format:
static void Main(string[] args)
{
Console.WriteLine(ReplaceMacro("{0} job for admin", new Job { Id = 1, Name = "Todo", Description = "Nothing" }));
Console.ReadLine();
}
static string ReplaceMacro(string value, Job job)
{
return string.Format(value, job.Name);
}

This generic solution Extend the answer provided by #Dan
It can be used for any typed object.
install System.Linq.Dynamic
Install-Package System.Linq.Dynamic -Version 1.0.7
string ReplaceMacro(string value, object #object)
{
return Regex.Replace(value, #"{(.+?)}",
match => {
var p = Expression.Parameter(#object.GetType(), #object.GetType().Name);
var e = System.Linq.Dynamic.DynamicExpression.ParseLambda(new[] { p }, null, match.Groups[1].Value);
return (e.Compile().DynamicInvoke(#object) ?? "").ToString();
});
}
See a working demo for a Customer type

You could use RazorEngine:
using RazorEngine;
class Program
{
static void Main(string[] args)
{
Console.WriteLine(ReplaceMacro("#Model.Name job for admin", new Job { Id = 1, Name = "Todo", Description="Nothing" }));
Console.ReadLine();
}
static string ReplaceMacro(string value, Job job)
{
return Engine.Razor.RunCompile(value, "key", typeof(Job), job);
}
}
It even supports Anonymous Types and method calls:
string template = "Hello #Model.Name. Today is #Model.Date.ToString(\"MM/dd/yyyy\")";
var model = new { Name = "Matt", Date = DateTime.Now };
string result = Engine.Razor.RunCompile(template, "key", null, model);

Little late to the party! Here is the one I wrote -
using System.Reflection;
using System.Text.RegularExpressions;
public static class AmitFormat
{
//Regex to match keywords of the format {variable}
private static readonly Regex TextTemplateRegEx = new Regex(#"{(?<prop>\w+)}", RegexOptions.Compiled);
/// <summary>
/// Replaces all the items in the template string with format "{variable}" using the value from the data
/// </summary>
/// <param name="templateString">string template</param>
/// <param name="model">The data to fill into the template</param>
/// <returns></returns>
public static string FormatTemplate(this string templateString, object model)
{
if (model == null)
{
return templateString;
}
PropertyInfo[] properties = model.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
if (!properties.Any())
{
return templateString;
}
return TextTemplateRegEx.Replace(
templateString,
match =>
{
PropertyInfo property = properties.FirstOrDefault(propertyInfo =>
propertyInfo.Name.Equals(match.Groups["prop"].Value, StringComparison.OrdinalIgnoreCase));
if (property == null)
{
return string.Empty;
}
object value = property.GetValue(model, null);
return value == null ? string.Empty : value.ToString();
});
}
}
Example -
string format = "{foo} is a {bar} is a {baz} is a {qux} is a really big {fizzle}";
var data = new { foo = 123, bar = true, baz = "this is a test", qux = 123.45, fizzle = DateTime.UtcNow };
Compared with other implementations given by Phil Haack and here are the results for the above example -
AmitFormat took 0.03732 ms
Hanselformat took 0.09482 ms
OskarFormat took 0.1294 ms
JamesFormat took 0.07936 ms
HenriFormat took 0.05024 ms
HaackFormat took 0.05914 ms

Wrap the string in a function...
var f = x => $"Hi {x}";
f("Mum!");
//... Hi Mum!

You need named string format replacement. See Phil Haack's post from years ago: http://haacked.com/archive/2009/01/04/fun-with-named-formats-string-parsing-and-edge-cases.aspx/

Not exactly but with bit tweek, I have created generic interpolation which support fields / property only.
public static string Interpolate(this string template, params Expression<Func<object, string>>[] values)
{
string result = template;
values.ToList().ForEach(x =>
{
MemberExpression member = x.Body as MemberExpression;
string oldValue = $"{{{member.Member.Name}}}";
string newValue = x.Compile().Invoke(null).ToString();
result = result.Replace(oldValue, newValue);
}
);
return result;
}
Test case
string jobStr = "{Name} job for admin";
var d = new { Id = 1, Name = "Todo", Description = "Nothing" };
var result = jobStr.Interpolate(x => d.Name);
Another
string sourceString = "I wanted abc as {abc} and {dateTime} and {now}";
var abc = "abcIsABC";
var dateTime = DateTime.Now.Ticks.ToString();
var now = DateTime.Now.ToString();
string result = sourceString.Interpolate(x => abc, x => dateTime, x => now);

Starting from the accepted answer I created a generic extension method:
public static string Replace<T>(this string template, T value)
{
return Regex.Replace(template, #"{(?<exp>[^}]+)}", match => {
var p = Expression.Parameter(typeof(T), typeof(T).Name);
var e = System.Linq.Dynamic.Core.DynamicExpressionParser.ParseLambda(new[] { p }, null, match.Groups["exp"].Value);
return (e.Compile().DynamicInvoke(value) ?? "").ToString();
});
}

Answer from #ThePerplexedOne is better, but if you really need to avoid string interpolation, so
static string ReplaceMacro(string value, Job job)
{
return value?.Replace("{job.Name}", job.Name); //Output should be "Todo job for admin"
}

You should change your function to:
static string ReplaceMacro(Job obj, Func<dynamic, string> function)
{
return function(obj);
}
And call it:
Console.WriteLine(
ReplaceMacro(
new Job { Id = 1, Name = "Todo", Description = "Nothing" },
x => $"{x.Name} job for admin"));

If you really need this, you can do it using Roslyn, create string – class implementation like
var stringToInterpolate = "$#\"{{job.Name}} job for admin\"";
var sourceCode = $#"
using System;
class RuntimeInterpolation(Job job)
{{
public static string Interpolate() =>
{stringToInterpolate};
}}";
then make an assembly
var assembly = CompileSourceRoslyn(sourceCode);
var type = assembly.GetType("RuntimeInterpolation");
var instance = Activator.CreateInstance(type);
var result = (string) type.InvokeMember("Interpolate",
BindingFlags.Default | BindingFlags.InvokeMethod, null, instance, new object[] {new Job { Id = 1, Name = "Todo", Description="Nothing" }});
Console.WriteLine(result);
you'll get this code by runtime and you'll get your result (also you'll need to attach link to your job class to that assembly)
using System;
class RuntimeInterpolation(Job job)
{
public static string Interpolate() =>
$#"{job.Name} job for admin";
}
You can read about how to implement CompileSourceRoslyn here Roslyn - compiling simple class: "The type or namespace name 'string' could not be found..."

Wondering no one has mentioned mustache-sharp. Downloadable via Nuget.
string templateFromSomewhere = "url: {{Url}}, Name:{{Name}}";
FormatCompiler compiler = new FormatCompiler();
Generator generator = compiler.Compile(templateFromSomewhere);
string result = generator.Render(new
{
Url="https://google.com",
Name = "Bob",
});//"url: https://google.com, Name:Bob"
More examples could be found here, at the unit testing file.

The implementation I'd come up with very similar to giganoide's allowing callers to substitute the name used in the template for the data to interpolate from. This can accommodate things like feeding it anonymous types. It will used the provided interpolateDataAs name if provided, otherwise if it isn't an anonymous type it will default to the type name, otherwise it expects "data". (or specifically the name of the property) It's written as an injectable dependency but should still work as an extension method.
public interface ITemplateInterpolator
{
/// <summary>
/// Attempt to interpolate the provided template string using
/// the data provided.
/// </summary>
/// <remarks>
/// Templates may want to use a meaninful interpolation name
/// like "enquiry.FieldName" or "employee.FieldName" rather than
/// "data.FieldName". Use the interpolateDataAs to pass "enquiry"
/// for example to substitute the default "data" prefix.
/// </remarks>
string? Interpolate<TData>(string template, TData data, string? interpolateDataAs = null) where TData : class;
}
public class TemplateInterpolator : ITemplateInterpolator
{
/// <summary>
/// <see cref="ITemplateInterpolator.Interpolate(string, dynamic, string)"/>
/// </summary>
string? ITemplateInterpolator.Interpolate<TData>(string template, TData data, string? interpolateDataAs)
{
if (string.IsNullOrEmpty(template))
return template;
if (string.IsNullOrEmpty(interpolateDataAs))
{
interpolateDataAs = !typeof(TData).IsAnonymousType() ? typeof(TData).Name : nameof(data);
}
var parsed = Regex.Replace(template, #"{(?<exp>[^}]+)}", match =>
{
var param = Expression.Parameter(typeof(TData), interpolateDataAs);
var e = System.Linq.Dynamic.Core.DynamicExpressionParser.ParseLambda(new[] { param }, null, match.Groups["exp"].Value);
return (e.Compile().DynamicInvoke(data) ?? string.Empty).ToString();
});
return parsed;
}
For detecting the anonymous type:
public static bool IsAnonymousType(this Type type)
{
if (type.IsGenericType)
{
var definition = type.GetGenericTypeDefinition();
if (definition.IsClass && definition.IsSealed && definition.Attributes.HasFlag(TypeAttributes.NotPublic))
{
var attributes = definition.GetCustomAttributes(typeof(CompilerGeneratedAttribute), false);
return (attributes != null && attributes.Length > 0);
}
}
return false;
}
Test Suite:
[TestFixture]
public class WhenInterpolatingATemplateString
{
[TestCase("")]
[TestCase(null)]
public void ThenEmptyValueReturedWhenNoTemplateProvided(string? template)
{
ITemplateInterpolator testInterpolator = new TemplateInterpolator();
var testData = new TestData { Id = 14, Name = "Test" };
var result = testInterpolator.Interpolate(template!, testData);
Assert.That(result, Is.EqualTo(template));
}
[Test]
public void ThenTheTypeNameIsUsedForTheDataReferenceForDefinedClasses()
{
ITemplateInterpolator testInterpolator = new TemplateInterpolator();
var testData = new TestData { Id = 14, Name = "Test" };
string template = "This is a record named \"{TestData.Name}\" with an Id of {testdata.Id}."; // case insensitive.
string expected = "This is a record named \"Test\" with an Id of 14.";
var result = testInterpolator.Interpolate(template, testData);
Assert.That(result, Is.EqualTo(expected));
}
[Test]
public void ThenTheDefaultNameIsUsedForTheDataReferenceForAnonymous()
{
ITemplateInterpolator testInterpolator = new TemplateInterpolator();
var testData = new { Id = 14, Name = "Test" };
string template = "This is a record named \"{data.Name}\" with an Id of {data.Id}.";
string expected = "This is a record named \"Test\" with an Id of 14.";
var result = testInterpolator.Interpolate(template, testData);
Assert.That(result, Is.EqualTo(expected));
}
[Test]
public void ThenTheProvidedDataReferenceNameOverridesTheTypeName()
{
ITemplateInterpolator testInterpolator = new TemplateInterpolator();
var testData = new TestData { Id = 14, Name = "Test" };
string template = "This is a record named \"{otherData.Name}\" with an Id of {otherData.Id}.";
string expected = "This is a record named \"Test\" with an Id of 14.";
var result = testInterpolator.Interpolate(template, testData, "otherData");
Assert.That(result, Is.EqualTo(expected));
}
[Test]
public void ThenExceptionIsThrownWhenTemplateReferencesUnknownDataValues()
{
ITemplateInterpolator testInterpolator = new TemplateInterpolator();
var testData = new TestData { Id = 14, Name = "Test" };
string template = "This is a record named \"{testData.Name}\" with an Id of {testData.Id}. {testData.ExtraDetails}";
Assert.Throws<ParseException>(() => { var result = testInterpolator.Interpolate(template, testData, "testData"); });
}
[Test]
public void ThenDataFormattingExpressionsAreApplied()
{
ITemplateInterpolator testInterpolator = new TemplateInterpolator();
var testData = new { Id = 14, Name = "Test", IsActive = true, EffectiveDate = DateTime.Today };
string template = "The active state is {data.IsActive?\"Yes\":\"No\"}, Effective {data.EffectiveDate.ToString(\"yyyy-MM-dd\")}";
string expected = "The active state is Yes, Effective " + DateTime.Today.ToString("yyyy-MM-dd");
var result = testInterpolator.Interpolate(template, testData);
Assert.That(result, Is.EqualTo(expected));
}
private class TestData
{
public int Id { get; set; }
public string Name { get; set; }
}
}

I really don't understand the point of your ReplaceMacro method...
But here's how it should work:
class Program
{
static void Main(string[] args)
{
var job = new Job { Id = 1, Name = "Todo", Description = "Nothing" };
Console.WriteLine($"{job.Name} job for admin");
Console.ReadLine();
}
}
But if you really want the dynamic feel to it, your ReplaceMacro method should just take one parameter, which is the job:
static string ReplaceMacro(Job job)
{
return $"{job.Name} job for admin.";
}
And use it like:
var job = new Job { Id = 1, Name = "Todo", Description = "Nothing" };
Console.WriteLine(ReplaceMacro(job));
Or something to that effect.

Related

How to filter a list using another list in LINQ C#

The list needs to filter is having data like: '1000', '1000A', '1000B', '2000', '2000C', '2003', '2006A'
The list by which I am filtering having data like: '1000', '2000', '2003'
Expected output: 1000', '1000A', '1000B', '2000', '2000C', '2003'
(output is expected like we do in SQL server LIKE operator)
Suppose you are having two class like below,
public class MainClass
{
public string ActualValue { get; set; }
}
public class FilterClass
{
public string Description { get; set; }
}
I am loading some dummy data like this,
List<MainClass> mainList = new List<MainClass>();
mainList.Add(new MainClass() { ActualValue = "1000" });
mainList.Add(new MainClass() { ActualValue = "1000A" });
mainList.Add(new MainClass() { ActualValue = "1002F" });
mainList.Add(new MainClass() { ActualValue = "1002A" });
mainList.Add(new MainClass() { ActualValue = "1003" });
List<FilterClass> filterList = new List<FilterClass>();
filterList.Add(new FilterClass() { Description = "1003" });
filterList.Add(new FilterClass() { Description = "1002" });
The O/P will be given as per your requirement by,
var output1 = mainList.Where(x => filterList.Any(y => x.ActualValue.Contains(y.Description))).ToList();
Try with regex, like this:
var list1 = new List<string>{"1000", "1000A", "1000B","2000","2000A","3000BV"};
var list2 = new List<string>{"1000","2000"};
var result = list1.Where(x => list2.Any(y => Regex.IsMatch(x, $".*{y}.*"))).ToList();
Note: .* are the equivalent of % in SQL.
you could use linq in this way :
var filterList = new List<string>(){"1000", "1000A", "1000B", "2000", "2000C", "2003", "2006A"};
var filterLikeList = new List<string>(){"1000", "2000", "2003"};
var results = filterList.Where(x=> filterLikeList.Any(y=>x.Contains(y)));

Unit test to check for unused properties

I have a function that runs through the properties of a class and replaces the keyword between two dollar signs with the same name from a template.
An example of a class:
public class FeedMessageData : IMailObject
{
public string Username { get; private set;}
public string SubscriptionID { get; private set; }
public string MessageTime { get; private set; }
public string Subject { get; private set; }
public FeedMessageData(string username, string subscriptionID, DateTime messageTime)
{
this.Username = username;
this.SubscriptionID = subscriptionID;
this.MessageTime = messageTime.ToShortDateString();
this.Subject = "Feed " + DateTime.Now + " - SubscriptionID: " + this.SubscriptionID;
}
}
And this is the function to replace the template with the properties:
private string mergeTemplate(string template, IMailObject mailObject)
{
Regex parser = new Regex(#"\$(?:(?<operation>[\w\-\,\.]+) ){0,1}(?<value>[\w\-\,\.]+)\$", RegexOptions.Compiled);
var matches = parser.Matches(template).Cast<Match>().Reverse();
foreach (var match in matches)
{
string operation = match.Groups["operation"].Value;
string value = match.Groups["value"].Value;
var propertyInfo = mailObject.GetType().GetProperty(value);
if (propertyInfo == null)
throw new TillitException(String.Format("Could not find '{0}' in object of type '{1}'.", value, mailObject));
object dataValue = propertyInfo.GetValue(mailObject, null);
template = template.Remove(match.Index, match.Length).Insert(match.Index, dataValue.ToString());
}
return template;
}
I'm looking to create a unit test that writes to the console, possible properties that aren't utilized in the template. An example would be if there wasn't a $SubscriptionID$ in the template. I've tried using PropertyInfo, which gives me the properties of the class, but how do I then use this information to check if they have already been used in the template?
Moq (https://github.com/moq/moq4/wiki) provides ways to verify property/method access.
Follow the tutorials on this link for more details. To verify that your properties are being consumed in your template, you can make use of the VerifyGet method, an example below:
[Fact]
public void VerifyAllPropertiesHaveBeenConsumedInTemplate()
{
var mockMailObject = new Mock<IMailObject>();
var template = "yourTemplateOrMethodThatReturnsYourTemplate";
var result = mergeTemplate(template, mockMailObject.Object);
mockMailObject.VerifyGet(m => m.Username, Times.Once);
mockMailObject.VerifyGet(m => m.SubscriptionID, Times.Once);
mockMailObject.VerifyGet(m => m.MessageTime, Times.Once);
mockMailObject.VerifyGet(m => m.Subject, Times.Once);
}

How to get attribute in C# object like javascript?

There is a function I want to implement in C#:
public string getName(object ob)
{
// TODO: if object has "Name" attribute, return "Name".Value, else return null;
}
When I invoke this function,
string result1 = getName(new {Name = "jack", Age = 12}) // result1 = "jack"
string result2 = getName(new {Age = 12}) // result2 = null
I know it's easy to implement in JavaScript or other dynamic languages... I am curious about if it can implement in C#?
Yes, you can. Take a look to Reflection
public string getName(object ob)
{
var type = ob.GetType();
if (type.GetProperty("Name") == null) return null;
else return type.GetProperty("Name").GetValue(ob, null);
}
There is several ways to achieve that. For example by using reflection. It will take one line code:
public static string GetName(object ob) =>
ob.GetType().GetProperty("Name")?.GetValue(ob)?.ToString();
Or below c#6 way:
public static string GetName(object ob)
{
return ob.GetType().GetProperty("Name")?.GetValue(ob)?.ToString();
}
You can even serialize your object to JSON or XML, then try to get your value from that:
public static string GetName(object obj)
{
string json = JsonConvert.SerializeObject(obj);
string result = JsonConvert.DeserializeAnonymousType(json, new { Name = "" })?.Name;
return result;
}
UPDATE: While this may work for your needs, it is probably not the best solution as pointed out by Grek40, and Damien_The_Unbeliever in comments below due to several limitations.
Not completely deleting the answer as it may give you some information about the alternative despite of its limitations.
Original Answer:
Try this..
Rextester can be found -- http://rextester.com/DQOBW76322
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
namespace Rextester
{
public class Program
{
public static void Main(string[] args)
{
string result1 = getName(new {Name = "jack", Age = 12}); // result1 = "jack"
string result2 = getName(new {Age = 12});
Console.WriteLine(result1);
Console.WriteLine(result2);
}
public static string getName(object obj)
{
var testData = obj.GetType().GetProperties().ToDictionary(p => p.Name, p => p.GetValue(obj).ToString());
if(testData.Any(d => d.Key == "Name")){
return testData["Name"];
}
return null;
}
}
}

Use workflow to evaluate dynamic expression

I would like to pass an object and expression into a dynamically created workflow to mimic the Eval function found in many languages. Can anyone help me out with what I am doing wrong? The code below is a very simple example if taking in a Policy object, multiple its premium by 1.05, then return the result. It throws the exception:
Additional information: The following errors were encountered while processing the workflow tree:
'DynamicActivity': The private implementation of activity '1: DynamicActivity' has the following validation error: Value for a required activity argument 'To' was not supplied.
And the code:
using System.Activities;
using System.Activities.Statements;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Policy p = new Policy() { Premium = 100, Year = 2016 };
var inputPolicy = new InArgument<Policy>();
var theOutput = new OutArgument<object>();
Activity dynamicWorkflow = new DynamicActivity()
{
Properties =
{
new DynamicActivityProperty
{
Name="Policy",
Type=typeof(InArgument<Policy>),
Value=inputPolicy
}
},
Implementation = () => new Sequence()
{
Activities =
{
new Assign()
{
To = theOutput,
Value=new InArgument<string>() { Expression = "Policy.Premium * 1.05" }
}
}
}
};
WorkflowInvoker.Invoke(dynamicWorkflow);
}
}
public class Policy
{
public int Premium { get; set; }
public int Year { get; set; }
}
}
You can use Workflow Foundation to evaluate expressions, but it is far easier to use almost any other option.
The key issue at play with your code was that you were not trying to evaluate the expression (with either VisualBasicValue or CSharpValue). Assigning InArgument`1.Expression is an attempt to set the value - not to set the value to the result of an expression.
Keep in mind that compiling expressions is fairly slow (>10ms), but the resultant compiled expression can be cached for quick executions.
Using Workflow:
class Program
{
static void Main(string[] args)
{
// this is slow, only do this once per expression
var evaluator = new PolicyExpressionEvaluator("Policy.Premium * 1.05");
// this is fairly fast
var policy1 = new Policy() { Premium = 100, Year = 2016 };
var result1 = evaluator.Evaluate(policy1);
var policy2 = new Policy() { Premium = 150, Year = 2016 };
var result2 = evaluator.Evaluate(policy2);
Console.WriteLine($"Policy 1: {result1}, Policy 2: {result2}");
}
}
public class Policy
{
public double Premium, Year;
}
class PolicyExpressionEvaluator
{
const string
ParamName = "Policy",
ResultName = "result";
public PolicyExpressionEvaluator(string expression)
{
var paramVariable = new Variable<Policy>(ParamName);
var resultVariable = new Variable<double>(ResultName);
var daRoot = new DynamicActivity()
{
Name = "DemoExpressionActivity",
Properties =
{
new DynamicActivityProperty() { Name = ParamName, Type = typeof(InArgument<Policy>) },
new DynamicActivityProperty() { Name = ResultName, Type = typeof(OutArgument<double>) }
},
Implementation = () => new Assign<double>()
{
To = new ArgumentReference<double>() { ArgumentName = ResultName },
Value = new InArgument<double>(new CSharpValue<double>(expression))
}
};
CSharpExpressionTools.CompileExpressions(daRoot, typeof(Policy).Assembly);
this.Activity = daRoot;
}
public DynamicActivity Activity { get; }
public double Evaluate(Policy p)
{
var results = WorkflowInvoker.Invoke(this.Activity,
new Dictionary<string, object>() { { ParamName, p } });
return (double)results[ResultName];
}
}
internal static class CSharpExpressionTools
{
public static void CompileExpressions(DynamicActivity dynamicActivity, params Assembly[] references)
{
// See https://learn.microsoft.com/en-us/dotnet/framework/windows-workflow-foundation/csharp-expressions
string activityName = dynamicActivity.Name;
string activityType = activityName.Split('.').Last() + "_CompiledExpressionRoot";
string activityNamespace = string.Join(".", activityName.Split('.').Reverse().Skip(1).Reverse());
TextExpressionCompilerSettings settings = new TextExpressionCompilerSettings
{
Activity = dynamicActivity,
Language = "C#",
ActivityName = activityType,
ActivityNamespace = activityNamespace,
RootNamespace = null,
GenerateAsPartialClass = false,
AlwaysGenerateSource = true,
ForImplementation = true
};
// add assembly references
TextExpression.SetReferencesForImplementation(dynamicActivity, references.Select(a => (AssemblyReference)a).ToList());
// Compile the C# expression.
var results = new TextExpressionCompiler(settings).Compile();
if (results.HasErrors)
{
throw new Exception("Compilation failed.");
}
// attach compilation result to live activity
var compiledExpression = (ICompiledExpressionRoot)Activator.CreateInstance(results.ResultType, new object[] { dynamicActivity });
CompiledExpressionInvoker.SetCompiledExpressionRootForImplementation(dynamicActivity, compiledExpression);
}
}
Compare to the equivalent Roslyn code - most of which is fluff that is not really needed:
public class PolicyEvaluatorGlobals
{
public Policy Policy { get; }
public PolicyEvaluatorGlobals(Policy p)
{
this.Policy = p;
}
}
internal class PolicyExpressionEvaluator
{
private readonly ScriptRunner<double> EvaluateInternal;
public PolicyExpressionEvaluator(string expression)
{
var usings = new[]
{
"System",
"System.Collections.Generic",
"System.Linq",
"System.Threading.Tasks"
};
var references = AppDomain.CurrentDomain.GetAssemblies()
.Where(a => !a.IsDynamic && !string.IsNullOrWhiteSpace(a.Location))
.ToArray();
var options = ScriptOptions.Default
.AddImports(usings)
.AddReferences(references);
this.EvaluateInternal = CSharpScript.Create<double>(expression, options, globalsType: typeof(PolicyEvaluatorGlobals))
.CreateDelegate();
}
internal double Evaluate(Policy policy)
{
return EvaluateInternal(new PolicyEvaluatorGlobals(policy)).Result;
}
}
Roslyn is fully documented, and has the helpful Scripting API Samples page with examples.

Programmatically generating C# Code Documentation (summary) with CodeDOM

I want to generate C# code documentation with CodeDOM.
Code without Documentation:
public class MyType {
public static BitmapImage File {
get { return GetFile("..."); }
}
}
Code with Documentation:
/// <summary> Gets the File from the location </summary>
public class MyType {
public static BitmapImage File {
get { return GetFile("..."); }
}
}
or
/// <summary>
/// Gets the File from the location
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
public class MyType {
public static BitmapImage File {
get { return GetFile("..."); }
}
}
I'm able to generate the Class, the Member, I can Add some attributes and decorators, But how to generate documentation - unfortunately no.
How I generate the members:
private CodeTypeDeclaration CreateType() {
var classType = new CodeTypeDeclaration("MyType") {
Attributes = MemberAttributes.Public | MemberAttributes.Static
};
var properties = CreateMembers();
classType.Members.AddRange(properties);
return classType;
}
private CodeTypeMember[] CreateMembers() {
var members = _members.Where(x => IsMy(x.Name));
var props = members.Select(CreateProperty).ToArray();
return props;
}
private CodeTypeMember CreateProperty(X x) {
var name = Path.GetFileNameWithoutExtension(x.Name);
var property = new CodeMemberProperty {
Name = name,
HasGet = true,
Attributes = MemberAttributes.Public | MemberAttributes.Static,
Type = new CodeTypeReference(typeof(BitmapImage)),
};
var targetObject = new CodeTypeReferenceExpression(typeof(Y));
var method = Return(new CodeMethodInvokeExpression(targetObject, "Getx", Primitive(x.Name)));
property.GetStatements.Add(method);
return property;
}
private bool IsMy(string path) {
var extension = Path.GetExtension(path.ToLower());
var isMy = Regex.IsMatch(extension, #"\.(jpg)$");
return isMy;
}
Edit:
Add Implementation:
private CodeTypeMember CreateProperty(X x) {
var name = Path.GetFileNameWithoutExtension(x.Name);
var property = new CodeMemberProperty {
Name = name,
HasGet = true,
Attributes = MemberAttributes.Public | MemberAttributes.Static,
Type = new CodeTypeReference(typeof(BitmapImage)),
};
var targetObject = new CodeTypeReferenceExpression(typeof(Y));
var method = Return(new CodeMethodInvokeExpression(targetObject, "Getx", Primitive(x.Name)));
property.GetStatements.Add(method);
//because CodeMemberProperty.Comments is readonly I cast it to CodeTypeMember, which has read/write Comments
var docStart = new CodeCommentStatement("<summary>", true);
var fileSystemName = string.Format("File system Name: {0}", x.Name);
var docContent = new CodeCommentStatement(fileSystemName, true);
var docEnd = new CodeCommentStatement("</summary>", true);
var result = property as CodeTypeMember;
result.Comments.AddRange(new CodeCommentStatementCollection {
docStart,
docContent,
docEnd
});
return result;
}
CodeTypeDeclaration class1 = new CodeTypeDeclaration("Class1");
class1.Comments.Add(new CodeCommentStatement("<summary>", true));
class1.Comments.Add(new CodeCommentStatement("Create a Hello World application.", true));
class1.Comments.Add(new CodeCommentStatement("</summary>", true));
How to: Create an XML Documentation File Using CodeDOM

Categories

Resources