Roslyn VisitExpressionStatement does not update expression - c#

I am trying to refactor some code to use dependency injection. I would ike to change this:-
public Customer GetCustomer(int customerId)
{
return DALFactory.Factory.GetDAL<ICustomerDAL>().GetCustomer(customerId);
}
to this:-
public Customer GetCustomer(int customerId)
{
return this.customerDAL.GetCustomer(customerId);
}
My attempt looks like this:-
public class BllRewriter : CSharpSyntaxRewriter
{
public override SyntaxNode VisitExpressionStatement(ExpressionStatementSyntax node)
{
if (node.ToString().StartsWith("DALFactory.Factory.GetDAL<"))
{
// code is "DALFactory.Factory.GetDAL<ICustomerDAL>().GetCustomer(customerId);"
string code = node.ToString();
// interfaceName is "ICustomerDAL"
string interfaceName = GetInterfaceName(code);
// propertyName is "customerDAL"
string propertyName = interfaceName.Substring(1);
propertyName = "this." + propertyName.Substring(0, 1).ToLowerInvariant() + propertyName.Substring(1);
int closingBracketIndex = code.IndexOf(">");
string expression = code.Substring(closingBracketIndex + 3);
// "newLine" is "this.customerDAL.GetCustomer(customerId);"
string newLine = $"{propertyName}{expression}";
ExpressionSyntax newExpressionSyntax = SyntaxFactory.ParseExpression(newLine);
ExpressionStatementSyntax newExpressionStatementSyntax = SyntaxFactory.ExpressionStatement(newExpressionSyntax);
// node.ToString() is "this.customerDAL.GetCustomer(customerId);;"
node = node.ReplaceNode(node, newExpressionStatementSyntax);
}
return base.VisitExpressionStatement(node);
}
private static string GetInterfaceName(string code)
{
string interfaceName = code.Substring("DALFactory.Factory.GetDAL<".Length);
int closingBracketIndex = interfaceName.IndexOf('>');
interfaceName = interfaceName.Substring(0, closingBracketIndex);
return interfaceName;
}
}
Stepping through the solution I see that it executes correctly and AFAICS it should update the code with the modified expression. But it doesn't. I can't work out why. Any ideas?

return DALFactory.Factory.GetDAL<ICustomerDAL>().GetCustomer(customerId); is ReturnStatement.
DALFactory.Factory.GetDAL<ICustomerDAL>().GetCustomer(customerId) is InvocationExpression.
try override VisitReturnStatement or VisitInvocationExpression return new node.
public override SyntaxNode VisitInvocationExpression(InvocationExpressionSyntax node)
{
if (node.ToString().StartsWith("DALFactory.Factory.GetDAL<"))
{
// ...
return SyntaxFactory.ParseExpression($"{propertyName}{expression}");
}
return base.VisitExpressionStatement(node);
}
You need to return new tree when using rewriter like this:
var bllRewriter = new BllRewriter();
var newRoot = bllRewriter.Visit(root);
var newCode = newRoot.GetText();

Related

Best way to append query string parameter to URL from object

I have query string class.
public class PagingModel
{
public int PageNumber { get; set; } = 1;
public string Filter { get; set; } = "text";
}
string url = "Menu/GetMenus";
I have to generate the URI with a query string based on an object in ASP.NET Core 5 preview. Is there any built in query helper?.
Required output:
/Menu/GetMenus?PageNumber=3&Filter=text
MVC Controller:
public async Task<IActionResult> index_partial([FromQuery] PagingModel paging)
{
var data = await _apiService.GetMenusAsync(paging);
return PartialView("_IndexPartial", data);
}
Service:
public async Task<PagedList<MenuModel>> GetMenusAsync(PagingModel paging)
{
string Requiredurl = "Menu/GetMenus?page="+ paging.PageNumber;
}
I got this extension method.. No need to generate query string manually.Only class object we need to pass. i thought some one else can use the same thing ...
public static string AppendObjectToQueryString(string uri, object requestObject)
{
var type = requestObject.GetType();
var data = type.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.ToDictionary
(
p => p.Name,
p => p.GetValue(requestObject)
);
foreach (var d in data)
{
if (d.Value == null)
{
continue;
}
if ((d.Value as string == null) && d.Value is IEnumerable enumerable)
{
foreach (var value in enumerable)
{
uri = QueryHelpers.AddQueryString(uri, d.Key, value.ToString());
}
}
else
{
uri = QueryHelpers.AddQueryString(uri, d.Key, d.Value.ToString());
}
}
return uri;
}
Ex: In my case i called this way.
string uri = "Menu/GetMenus";
string full_uri = QueryStringExtension.AppendObjectToQueryString(uri, paging);
With a Query String this simple I would just do
PagingModel qsData = new PagingModel();
//set qsData properties as needed
string urlWithQueryString = $"/Menu/GetMenus?{nameof(PagingModel.PageNumber)}={qsData.PageNumber}&nameof(PagingModel.Filter)}={qsData.Filter}";
However more standard is to do something like
string urlWithQueryString = this.Url.Action("GetMenus", "Menu", new PagingModel { PageNumber = 3, Filter = "text" }, this.Request.Url.Scheme);
But best solution depends on your specific case - can you add your action method definition for GetMenus ?
Update for your additional code :
Seeing as looks like you want to generate the url inside the service I would simply do this :
public async Task<PagedList<MenuModel>> GetMenusAsync(PagingModel paging)
{
string Requiredurl = $"/Menu/GetMenus?{nameof(PagingModel.PageNumber)}={paging.PageNumber}&nameof(PagingModel.Filter)}={paging.Filter}";
}

Views returned based on variable and existence

I have an asp.net-mvc website I am working on. The site is meant to as a basis for multiple clients all with their own unique business requirements. For any given controller method, I may or may not have a customized view for the client based on their ClientId.
Right now how I am handling this is through a ResourceSelectorObject like so:
public class ClientResourceSelector
{
public ClientResourceSelector(int clientId)
{
this.ClientId = clientId;
}
public int ClientId { get; set; }
public readonly List<ViewProfile> ViewProfiles = new List<ViewProfile>()
{
new ViewProfile { ClientId = 8, Controller = "Contact", Action = "NewContact", View = "C008/NewContact" }
};
public string ViewName(string controller, string action)
{
var Profile = ViewProfiles.FirstOrDefault(X => X.Controller.Equals(controller) && X.Action.Equals(action) && X.ClientId == ClientId);
if (Profile == null) return string.Empty;
return Profile.View;
}
}
Then in the code, I use that object in this manner:
// GET: Contact/NewContact
public ActionResult NewContact()
{
var selector = new ClientResourceSelector(ClientId);
string alternate_view = selector.ViewName("Contact", "NewContact");
if (String.IsNullOrEmpty(alternate_view))
return View(NewContactViewModel.Instance(ClientId));
else
return View(alternate_view, NewContactViewModel.Instance(ClientId));
}
The problem, and this is definitely the programming equivalent of "First World Problems," but I would like to still be able to just call View(viewModel) and have it select the appropriate view to display programmatically without my having to remember to register each view in the selector.
Obviously, I would then want to override the View() method in the abstract controller that all of my controllers are inheriting from. But I am unsure of how that code would look. Any suggestions would be helpful.
Here is how I've created ones in the past. Most of the Tenant systems I've built use some type of route/request parameter (could easily be updated to use DNS or wahtever, you have a lot of options) to determine the specific Tenant. I use an action filter that executes before any controller (or routing) to populate the route data (useful for Tenant specific routes as well).
public class TenantActionFilterAttribute : ActionFilterAttribute
{
internal const string _Tenant = "tenant";
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
// Do this how ever you want, right now I'm using querystring
// Could be changed to use DNS name or whatever
var tenant = filterContext.HttpContext.Request.QueryString[_Tenant] as string;
if (tenant != null)
{
filterContext.RouteData.Values[Tenant] = tenant;
}
}
}
Either globally register the action filter:
RegisterGlobalFilters(GlobalFilters.Filters);
(Or using a Dependency Injection Framework)
Then a custom ViewEngine:
public class TenantViewEngine : RazorViewEngine
{
private string GetPrefix(ControllerContext controllerContext)
{
var result = string.Empty;
var tenant = controllerContext.RouteData.Values[TenantActionFilterAttribute.Tenant] as string;
if (!string.IsNullOrEmpty(tenant))
{
result = "Tenants/" + tenant + "/";
}
return result;
}
protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
{
var prefix = GetPrefix(controllerContext);
if (partialPath.StartsWith("~/"))
{
partialPath = partialPath.Insert(2, prefix);
}
else if (partialPath.StartsWith("~") || partialPath.StartsWith("/"))
{
partialPath = partialPath.Insert(1, prefix);
}
else if (string.IsNullOrEmpty(partialPath))
{
partialPath = prefix + partialPath;
}
return base.CreatePartialView(controllerContext, partialPath);
}
protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
{
var prefix = GetPrefix(controllerContext);
if (viewPath.StartsWith("~/"))
{
viewPath = viewPath.Insert(2, prefix);
}
else if (viewPath.StartsWith("~") || viewPath.StartsWith("/"))
{
viewPath = viewPath.Insert(1, prefix);
}
else if (!string.IsNullOrEmpty(viewPath))
{
viewPath = prefix + viewPath;
}
if (masterPath.StartsWith("~/"))
{
masterPath = masterPath.Insert(2, prefix);
}
else if (masterPath.StartsWith("~") || masterPath.StartsWith("/"))
{
masterPath = masterPath.Insert(1, prefix);
}
else if (!string.IsNullOrEmpty(masterPath))
{
masterPath = prefix + masterPath;
}
return base.CreateView(controllerContext, viewPath, masterPath);
}
}
I can't exactly remember how this works, but the search paths change from the default to something very close to:
"~/Tenants/<TenantName>/Areas/{3}/Views/{1}/{0}.cshtml",
"~/Areas/{3}/Views/{1}/{0}.cshtml",
"~/Tenants/<TenantName>//Views/{1}/{0}.cshtml",
"~/Views/{1}/{0}.cshtml",
"~/Tenants/<TenantName>//Views/Shared/{0}.cshtml",
"~/Views/Shared/{0}.cshtml",
Where 1:Controller, 2:View/Action, 3:AreaName

MVVM Command property stacktrace

I'd like to know which property command is executed using the following pattern in my ApplicationCommand, any idea ? Today the Log always show ApplicationCommand.Execute(), not very usefull to trace user actions.
Class declaration :
public class MyViewModel
{
public ICommand BlinkCommand { get; set; }
public ICommand CheckCommand { get; set; }
public MyViewModel()
{
BlinkCommand = new ApplicationCommand(() => DoBlink());
CheckCommand = new ApplicationCommand(() => DoCheck());
}
...
}
My application command implementation for all commands :
public class ApplicationCommand : RelayCommand
{
public void Execute()
{
// No try-catch, I never code bugs ;o)
Log.Info("Prepare to execute the command " + this);
base.Execute();
Log.Info("Finished to execute the command " + this);
}
}
Using 1 command = 1 class, it's fine. Using this way, which seems to be widely used on the WWW, I don't know how to proceed :(
Thanks by advance for your help
You should refactor your ApplicationCommand like this:
public class ApplicationCommand : RelayCommand
{
string commandName;
public void Execute()
{
// No try-catch, I never code bugs ;o)
Log.Info("Prepare to execute the command " + commandName);
base.Execute();
Log.Info("Finished to execute the command " + commandName);
}
public static void SetCommand(MyViewModel vm,
Expression<Func<MyViewModel,ICommand>> commandSelector,
Action action){
var me = commandSelector.Body as MemberExpression;
if(me == null) throw new ArgumentException("Invalid command selector!");
var newCommand = new ApplicationCommand(action);
newCommand.commandName = me.Member.Name;
vm.GetType()
.GetProperty(newCommand.commandName)
.SetValue(vm, newCommand, null);
}
}
//then use it like this
public MyViewModel()
{
ApplicationCommand.SetCommand(this, e => e.BlinkCommand, () => DoBlink());
ApplicationCommand.SetCommand(this, e => e.CheckCommand, () => DoCheck());
}
I understand that it's for debugging purpose, so we should print the property name and then using the approach above is better.
You could add an extra instance string to the ApplicationCommand that you pass when constructing an instance and log that when execute is called.
public class ApplicationCommand : RelayCommand
{
private string logName;
public ApplicationCommand(Action<T> execute, string logName = "")
{
// ...
this.logName = logName;
}
public void Execute()
{
// No try-catch, I never code bugs ;o)
Log.Info("Prepare to execute the command " + this.logName);
base.Execute();
Log.Info("Finished to execute the command " + this.logName);
}
}
Using it:
BlinkCommand = new ApplicationCommand(() => DoBlink(), "Blink");
Did you try call GetType()?
public class ApplicationCommand : RelayCommand
{
public ApplicationCommand(Expression<Func<ICommand>> property,Action<T> execute, string logName = "")
{
// ...
this.logName = logName;
}
public void Execute()
{
// No try-catch, I never code bugs ;o)
Log.Info("Prepare to execute the command " + GetTypeName(this.GetType()) + "." + GetPropertyPath(property));
base.Execute();
Log.Info("Finished to execute the command " + GetTypeName(this.GetType()) + "." + GetPropertyPath(property));
}
}
private string GetPropertyPath(LambdaExpression propertyPath, bool acceptsFields = false)
{
Stack<MemberInfo> properties = GetPropertyPathStack(propertyPath, acceptsFields);
return string.Join(".", properties.Select(p => p.Name));
}
private Stack<MemberInfo> GetPropertyPathStack(LambdaExpression propertyPath, bool acceptFields = false)
{
MemberExpression member = propertyPath.Body as MemberExpression;
Stack<MemberInfo> properties = new Stack<MemberInfo>();
while(member != null)
{
if (member.Member is PropertyInfo || (acceptFields && member.Member is FieldInfo))
{
properties.Push(member.Member);
if (member.Expression is MemberExpression)
{
member = member.Expression as MemberExpression;
}
else
{
ConstantExpression constant = member.Expression as ConstantExpression;
member = null;
}
}
else
{
member = null;
}
}
return properties;
}
private string GetTypeName(Type type)
{
if (type.IsGenericType)
{
return GetGenericTypeName(type);
}
else
{
return type.FullName;
}
}
private string GetGenericTypeName(Type type)
{
Type[] genericArguments = type.GetGenericArguments();
string argumentNames = string.Join(", ", genericArguments.Select(GetTypeName));
return string.Format("{0}<{1}>", type.GetBaseName(), argumentNames);
}
Hope, this will help you or inspire you for better solution.
Usage:
BlinkCommand = new ApplicationCommand(() => BlinkCommand, () => DoBlink());

Making multiple calls in unit test without using a for loop

Lets say I have a unit test similar to the below, is there a way to write one unit test rather than several but also avoid having a for loop in the unit test?
[Test]
public void RunTestWithMultipleOptions()
{
MyClass code = new MyClass();
code.Prefix = "{DS1}"; //Options are {DS1}, {DS2}, {DS3}, {DS4}
//Property could be set to
//code.Prefix = "{DS1}{DS2}";
//code.Prefix = "{DS1}{DS2}{DS3}";
//And so on
//Based on how many {DS} used a method needs calling
code.InputDataStore(1,"Data1");
//If used {DS1}{DS2} in Prefix then
//code.InputDataStore(1,"Data1");
//code.InputDataStore(2,"Data2");
//If used {DS1}{DS2}{DS3} in Prefix then
//code.InputDataStore(1,"Data1");
//code.InputDataStore(2,"Data2");
//code.InputDataStore(3,"Data3");
string OutputData = String.Empty;
code.Output += delegate(int Id, string Data)
{
if (Id == (int)OutputsEnum.OutputModified)
OutputData = Data;
};
//Call the input method which will raise the Output event which we can assert against
code.Input("hi there");
//Assert that output has replace the prefix {DS} with the data in the datastorecontent list
Assert.AreEqual("Data1hi there", OutputData);
}
I can pass in the property value to the unit test method and use test cases but based on what the property is MyMethod needs to be called x number of times. Without putting a loop in the test I can't think of a way without having all the permetations as separate unit tests.
UPDATE: Here is the main contents of the class:
public event Action<int, string> Output;
public string Prefix { get; set; }
public string Postfix { get; set; }
private List<string> DataStoreContents = new List<string>() { "", "", "", "" };
public void Input(string Data)
{
if (Output != null)
{
if (!String.IsNullOrEmpty(Prefix))
{
Prefix = Prefix.Replace("{DS1}", DataStoreContents[0]);
Prefix = Prefix.Replace("{DS2}", DataStoreContents[1]);
Prefix = Prefix.Replace("{DS3}", DataStoreContents[2]);
Prefix = Prefix.Replace("{DS4}", DataStoreContents[3]);
}
if (!String.IsNullOrEmpty(Postfix))
{
Postfix = Postfix.Replace("{DS1}", DataStoreContents[0]);
Postfix = Postfix.Replace("{DS2}", DataStoreContents[1]);
Postfix = Postfix.Replace("{DS3}", DataStoreContents[2]);
Postfix = Postfix.Replace("{DS4}", DataStoreContents[3]);
}
Output((int)OutputsEnum.OutputBeforeModified, Data);
Output((int)OutputsEnum.OutputModified, Prefix + Data + Postfix);
Output((int)OutputsEnum.OutputAfterModified, Data);
}
}
}
public void InputDataStore(int DataStore, string Data)
{
if (DataStore < 1 || DataStore > 4)
throw new ArgumentOutOfRangeException("Datastore number out of range");
DataStoreContents[DataStore - 1] = Data;
}
}
I want to test that when I call InputDataStore(1,"MyData1"); InputDataStore(2, "MyData"); that Output does in fact replace the relevant {DS1} values with the relevant string and also combine it with any other {DS} values
One options is to test each call apart and all of them together. If you test A&B&C, you can limit yourself to testing A,B,C apart and A&B&C together. One option is the next code(made some assumptions):
[TestFixture]
public class ToTestFixture
{
[SetUp]
public void SetUp()
{
_instance = new ToTest();
_instance.InputDataStore(1, "1");
_instance.InputDataStore(2, "2");
_instance.InputDataStore(3, "3");
_instance.InputDataStore(4, "4");
}
private ToTest _instance;
[TestCase("{DS1}","1")]
[TestCase("{DS2}", "2")]
[TestCase("{DS3}", "3")]
[TestCase("{DS4}", "4")]
[TestCase("{DS1}{DS2}{DS3}{DS4}", "1234")]
[Test]
public void TestPrefixReplacements(string input, string expectedResult)
{
_instance.Prefix = input;
//Call the input method which will raise the Output event which we can test
_instance.Input("Any string goes here as we test only prefix." );
Assert.AreEqual(expectedResult, _instance.Prefix);
}
}
internal enum OutputsEnum
{
OutputBeforeModified,
OutputModified,
OutputAfterModified
}
public class ToTest
{
public event Action<int, string> Output = (x, result) => Console.WriteLine(x.ToString() + result);
public string Prefix { get; set; }
public string Postfix { get; set; }
private List<string> DataStoreContents = new List<string>() {"1", "2", "3", "4"};
public void Input(string Data)
{
if (Output != null)
{
if (!String.IsNullOrEmpty(Prefix))
{
Prefix = Prefix.Replace("{DS1}", DataStoreContents[0]);
Prefix = Prefix.Replace("{DS2}", DataStoreContents[1]);
Prefix = Prefix.Replace("{DS3}", DataStoreContents[2]);
Prefix = Prefix.Replace("{DS4}", DataStoreContents[3]);
}
if (!String.IsNullOrEmpty(Postfix))
{
Postfix = Postfix.Replace("{DS1}", DataStoreContents[0]);
Postfix = Postfix.Replace("{DS2}", DataStoreContents[1]);
Postfix = Postfix.Replace("{DS3}", DataStoreContents[2]);
Postfix = Postfix.Replace("{DS4}", DataStoreContents[3]);
}
Output((int) OutputsEnum.OutputBeforeModified, Data);
Output((int) OutputsEnum.OutputModified, Prefix + Data + Postfix);
Output((int) OutputsEnum.OutputAfterModified, Data);
}
}
public void InputDataStore(int DataStore, string Data)
{
if (DataStore < 1 || DataStore > 4)
throw new ArgumentOutOfRangeException("Datastore number out of range");
DataStoreContents[DataStore - 1] = Data;
}
}
Anyhow I feel there is a bond between "DS1" and the index of the array. (1-0, 2-1). This means next refactoring is possible:
Prefix = Prefix.Replace("{DS"+index+"}", DataStoreContents[index-1]);
More than that I guess output action decision is strange and two if-s are duplicate code. This is what I mean:
public void Input(string Data)
{
if (Output != null)
{
Prefix = ApplyReplaceRules(Prefix);
Postfix = ApplyReplaceRules(Postfix);
Output((int) OutputsEnum.OutputBeforeModified, Data);
Output((int) OutputsEnum.OutputModified, Prefix + Data + Postfix);
Output((int) OutputsEnum.OutputAfterModified, Data);
}
}
private string ApplyReplaceRules(string patternString)
{
if (!String.IsNullOrEmpty(Postfix))
{
patternString = patternString.Replace("{DS1}", DataStoreContents[0]);
patternString = patternString.Replace("{DS2}", DataStoreContents[1]);
patternString = patternString.Replace("{DS3}", DataStoreContents[2]);
patternString = patternString.Replace("{DS4}", DataStoreContents[3]);
}
return patternString;
}

c# gettype of object from class

How could I make this work?:
public class myClass
{
public string first;
public int second;
public string third;
}
public string tester(object param)
{
//Catch the name of what was passed not the value and return it
}
//So:
myClass mC = new myClass();
mC.first = "ok";
mC.second = 12;
mC.third = "ko";
//then would return its type from definition :
tester(mC.first) // would return : "mc.first" or "myClass.first" or "first"
//and
tester(mC.second) // would return : "mc.second" or "myClass.second" or "second"
In the absence of infoof, the best you can do is Tester(() => mC.first) via expression trees...
using System;
using System.Linq.Expressions;
public static class Test
{
static void Main()
{
//So:
myClass mC = new myClass();
mC.first = "ok";
mC.second = 12;
mC.third = "ko";
//then would return its type from definition :
Tester(() => mC.first); // writes "mC.first = ok"
//and
Tester(() => mC.second); // writes "mC.second = 12"
}
static string GetName(Expression expr)
{
if (expr.NodeType == ExpressionType.MemberAccess)
{
var me = (MemberExpression)expr;
string name = me.Member.Name, subExpr = GetName(me.Expression);
return string.IsNullOrEmpty(subExpr)
? name : (subExpr + "." + name);
}
return "";
}
public static void Tester<TValue>(
Expression<Func<TValue>> selector)
{
TValue value = selector.Compile()();
string name = GetName(selector.Body);
Console.WriteLine(name + " = " + value);
}
}
This is not possible. Variable names don't exist in compiled code, so there's no way you can retrieve a variable name at runtime
That's not possible. "param" will have no information on where the value came from.
When calling tester(), a copy of the value in one of the properties is made, so the "link" to the property is lost.

Categories