Warning "Message template is not a compile time constant" - c#

Within a class I have to following code which occurs several times:
var message = $"My Message with Value1='{value1}' and Value2='{value2}'.";
Log.Error(exception, message);
throw new CustomException(message);
message is always different, but the approach of logging the error and throwing an exception is always the same.
Now both Rider and ReSharper within Visual Studio give the following warning: Message template is not a compile time constant.
I can refactor it into this and the warning goes away:
const string messagePattern = "My Message with Value1='{0}' and Value2='{1}'.";
var messageParameters = new object[] { value1, value2 };
Log.Error(exception, messagePattern, messageParameters);
throw new CustomException(string.Format(messagePattern, messageParameters));
But how can I make this code reusable to avoid that I have to repeat the logging and throwing? This is what I'd like to do:
const string messagePattern = "My Message with Value1='{0}' and Value2='{1}'.";
var messageParameters = new object[] { value1, value2 };
LogAndThrow(exception, messagePattern, messageParameters);
void LogAndThrow(Exception exception, string messagePattern, string messageParameters)
{
Log.Error(exception, messagePattern, messageParameters);
throw new CustomException(string.Format(messagePattern, messageParameters));
}
But this gives the same error because messagePattern is not a const anymore and declaring it as in string messagePattern doesn't help.
Is there any way to make this code reusable except disabling the warning?

Currently, your code will throw an exception if the value of value1 is (for example) "{0}". Basically your first piece of code is is not considering what it passes in to be a format string with placeholders, but an already formatted string. You shouldn't call string.Format with that.
I would suggest you provide overloads for LogAndThrow and CustomException that have a single string parameter (for the already-formatted message) and no additional messageParameters parameter (because you don't need it), which does not call string.Format.

To get rid of the warning message should be "My Message with ..." without the $.
Your comments confused me and I came up with the following snippet that contains your desired code. I'm not reviewing your method here, I am addressing the issue of the incorrect use of $.
The result is My Message with Value1='1' and Value2='A'. and not warning (or error if this was .net 5) about message not being a constant.
// BTW. Changed 'string' to 'object[]'
static void LogAndThrow(Exception exception, string messagePattern, object[] messageParameters)
{
//Log.Error(exception, messagePattern, messageParameters);
throw new Exception(string.Format(messagePattern, messageParameters));
}
static void Main()
{
try
{
const string messagePattern = "My Message with Value1='{0}' and Value2='{1}'."; // No $
var messageParameters = new object[] { 1, "A" };
LogAndThrow(new Exception("X"), messagePattern, messageParameters);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}

Related

Get name from variable in calling method - creating calling method signature, parameters and values

I'm looking for a way to get hold of the name of a variable that was passed into an extensionmethod. I want to have the name of the parameter in the calling variable. Sounds strange, let me explain.
assume this piece of testing code
private static void TestingMethod(string firstParam, int secondParam, bool thirdParam)
{
try
{
throw new InvalidOperationException("This named optional params stuff, it's just not working boss");
}
catch (Exception ex)
{
GenericLog_Exception(ex, "it's on fire, please help");
}
}
On the last line you can see the exception being logged. I want to be able to provide optional parameter support. So the developers can add parameter information when needed.
I've seen many posts about this on stackoverflow, lot's of different approaches. Main thing is; it can't be done fully generically.
Some code for explanation:
static string GetCallingMethodSignature()
{
StackTrace stackTrace = new StackTrace();
// Get calling method name
var callingMethod = stackTrace.GetFrame(1).GetMethod();
var callingMethod_Name = callingMethod.Name;
// get calling method params
string retVal = string.Empty;
var callingMethod_Parameters = callingMethod.GetParameters();
retVal = callingMethod_Name + "(";
foreach (var param in callingMethod_Parameters)
{
retVal += param.Name + ": " + param.ToString() + ",";
}
retVal.Remove(retVal.Length - 1, 1);
retVal += ")";
return retVal;
}
Now, this testing code is getting the calling method name and it's parameters. That is, the names of the parameters. But not their values. The param.tostring() part only returns the type name. Not the value. I've been reading on this and it seems this can't be done via reflection.
So then I went for another approach, why not provide the parameters the developer finds suitable for logging. You don't need all of them most of the time anyway.
private static string GenericLog_Exception(Exception exceptionData, string extraInformation, params KeyValuePair<string, object>[] parameters)
So, this being a new testmethod, i'm providing the parameters of choice into the exception logging method. But if you want to make this work, it's one hell of a job everytime to make this call.
private static void TestingMethod(string firstParam, int secondParam, bool thirdParam)
{
try
{
throw new InvalidOperationException("This named optional params stuff, it's just not working boss");
}
catch (Exception ex)
{
GenericLog_Exception(ex, "it's on fire, please help", new KeyValuePair<string, object>[]{
new KeyValuePair<string, object>("firstParam", firstParam),
new KeyValuePair<string, object>("secondParam", secondParam),
new KeyValuePair<string, object>("thirdParam", thirdParam)
});
}
}
Now this is working. But as said, i find the bottom part to cumbersome. I was thinking along the lines of an extensionmethod, so I can shorten the creation of each kvp.
internal static class ExtensionMethodsForTesting
{
internal static KeyValuePair<string, object> AsKeyValuePair(this string parameter)
{
var name = nameof(parameter);
return new KeyValuePair<string, object>(name, parameter);
}
}
And this would then be used as
GenericLog_Exception(ex, "it's on fire, please help", new KeyValuePair<string, object>[] { firstParam.AsKeyValuePair() });
This confronts me with the same issue I had before; the nameof(parameter), ofcourse, returns "parameter". I would also have to make a couple of extension methods for each type. Or check for the type in the extension method to make sure i get the correct value.
So, in short: how can i get the name of this variable that invokes the extension method?
You could do the following "hack". Change the signature of your method to the following:
private static string GenericLog_Exception(
Exception exceptionData,
string extraInformation,
params Expression<Func<object>>[] parameters)
And now your call site would look a little bit cleaner:
GenericLog_Exception(ex,
"it's on fire, please help",
() => firstParam,
() => secondParam,
() => thirdParam);
And you can extract parameter info from the expressions the following way (using C# tuple support for convenience):
private static (object Value, string ParamName) GetParameterInfo
(Expression<Func<object>> expr)
{
//First try to get a member expression directly.
//If it fails we know there is a type conversion: parameter is not an object
//(or we have an invalid lambda that will make us crash)
//Get the "to object" conversion unary expression operand and then
//get the member expression of that and we're set.
var m = (expr.Body as MemberExpression) ??
(expr.Body as UnaryExpression).Operand as MemberExpression;
return (expr.Compile().Invoke(), m.Member.Name);
}

(Azure) BrokeredMessage.GetBody<xxx>

I'm trying to put together a 'generic' subscriber that I can (re)use with Azure ServiceBus.
But I'm stuck as follows;
my code once stripped of non essential parts looks like this.
Subscribing.Client.OnMessage((recdMessage =>
{
var msgBody = recdMessage.GetBody<myClass>();
}, options);
I want my msgBody to be of the type that has been serialised into the body of the message.
Indeed if myClass were to be something like TelephonyEventMessage and the message received was of that type then my msgBody would be a correctly instantiated/rehydrated object of that type.
But although I can use recdMessage. ContentType to get the string name of the class in that message.... I just cant figure what I need to put in myClass above.
I'm at the end of my knowledge now and no amount of searches seems to look like an answer for me. Do I need to add a specific version for every type that may exist in my messages?
You can use this to receive messages from a subscription if you are expecting a number of different object types:
public void ReceiveMessageFromSubscription<T>(string topicPath, string subscriptionName, Action<T> action)
{
var client = SubscriptionClient.CreateFromConnectionString(ConnectionString, topicPath, subscriptionName);
client.OnMessage((message) =>
{
try
{
_logger.Information("Processing message");
action(message.GetBody<T>());
message.Complete();
}
catch(Exception ex)
{
_logger.Error(ex, "Error processing message");
message.Abandon();
}
} );
}
And then pass in a method which knows how to handle the object, as below. You could have a number of these methods, all calling ReceiveMessageFromSubscription.
public void ProcessObject()
{
_serviceBusService.ReceiveMessageFromSubscription<MyObject>(mytopic, mysubscription, _myobjectService.ProcessObject);
}

Get Line Number and File Information in Code Where code is written in c# [duplicate]

Here is an example of what I want to do:
MessageBox.Show("Error line number " + CurrentLineNumber);
In the code above the CurrentLineNumber, should be the line number in the source code of this piece of code.
How can I do that?
In .NET 4.5 / C# 5, you can get the compiler to do this work for you, by writing a utility method that uses the new caller attributes:
using System.Runtime.CompilerServices;
static void SomeMethodSomewhere()
{
ShowMessage("Boo");
}
...
static void ShowMessage(string message,
[CallerLineNumber] int lineNumber = 0,
[CallerMemberName] string caller = null)
{
MessageBox.Show(message + " at line " + lineNumber + " (" + caller + ")");
}
This will display, for example:
Boo at line 39 (SomeMethodSomewhere)
There's also [CallerFilePath] which tells you the path of the original code file.
Use the StackFrame.GetFileLineNumber method, for example:
private static void ReportError(string message)
{
StackFrame callStack = new StackFrame(1, true);
MessageBox.Show("Error: " + message + ", File: " + callStack.GetFileName()
+ ", Line: " + callStack.GetFileLineNumber());
}
See Scott Hanselman's Blog entry for more information.
[Edit: Added the following]
For those using .Net 4.5 or later, consider the CallerFilePath, CallerMethodName and CallerLineNumber attributes in the System.Runtime.CompilerServices namespace. For example:
public void TraceMessage(string message,
[CallerMemberName] string callingMethod = "",
[CallerFilePath] string callingFilePath = "",
[CallerLineNumber] int callingFileLineNumber = 0)
{
// Write out message
}
The arguments must be string for CallerMemberName and CallerFilePath and an int for CallerLineNumber and must have a default value. Specifying these attributes on method parameters instructs the compiler to insert the appropriate value in the calling code at compile time, meaning it works through obfuscation. See Caller Information for more information.
I prefer one liners so:
int lineNumber = (new System.Diagnostics.StackFrame(0, true)).GetFileLineNumber();
In .NET 4.5 you can get the line number by creating the function:
static int LineNumber([System.Runtime.CompilerServices.CallerLineNumber] int lineNumber = 0)
{
return lineNumber;
}
Then each time you call LineNumber() you will have the current line. This has the advantage over any solution using the StackTrace that it should work in both debug and release.
So taking the original request of what is required, it would become:
MessageBox.Show("Error enter code here line number " + LineNumber());
This is building on the excellent answer by Marc Gravell.
For those who need a .NET 4.0+ method solution:
using System;
using System.IO;
using System.Diagnostics;
public static void Log(string message) {
StackFrame stackFrame = new System.Diagnostics.StackTrace(1).GetFrame(1);
string fileName = stackFrame.GetFileName();
string methodName = stackFrame.GetMethod().ToString();
int lineNumber = stackFrame.GetFileLineNumber();
Console.WriteLine("{0}({1}:{2})\n{3}", methodName, Path.GetFileName(fileName), lineNumber, message);
}
How to call:
void Test() {
Log("Look here!");
}
Output:
Void Test()(FILENAME.cs:104)
Look here!
Change the Console.WriteLine format how you like!
If its in a try catch block use this.
try
{
//Do something
}
catch (Exception ex)
{
System.Diagnostics.StackTrace trace = new System.Diagnostics.StackTrace(ex, true);
Console.WriteLine("Line: " + trace.GetFrame(0).GetFileLineNumber());
}
You only asked about the line number and with nullable project type, you then need to use a something like this
internal class Utils
{
public static int Line([CallerLineNumber] int? lineNumber =null)=>lineNumber;
}
in your code, if you like to get a line number you then just call
var line=Utils.Line();
if you are logging and you would like to document the line number in say logging than call the method like this
public void MyMethod(int someValue)
{
switch(someValue)
{
case 1:
if(abc<xyz)
{
logger.LogInformation("case value {someValue} this line {line} was true", someValue ,Utils.Line()-2);
}
break;
case 2:
logger.LogInformation("case value {someValue} this {line} was executed",someValue,Utils.Line());
break;
caste 3:
logger.LogInformation("case value {someValue} this {line} was executed",someValue,Utils.Line());
break;
}
}
You can extend this pattern with any of the other [CallerXXX] methods and not use them where ever, not just in the method parameters.
in the Nuget Package Walter I use a super cool class named ExceptionObject
if you import the NuGet package you have some nice extension methods on the Exception class as well as access to a CallStack showing the call chain including method parameters and parameter values of all methods called.
It's like a stack of an exception only with values showing how you got where you got with what values.
public void MyMethod()
{
try
{
//get me all methods, signatures, parameters line numbers file names etc used as well as assembly info of all assemblies used for documentation of how the code got here
var stack= new CallStack();
foreach( var frame in StackedFrames)
{
logger.LogDebug(frame.ToString());
}
}
catch(SqlException e)
{
var ex = new ExceptionObject(e);
logger.LogException(e,"{this} exception due to {message} {server} {procedure} TSQL-line:{sqlline}\n{TSQL}"
,e.GetType().Name
,e.Message
,ex.SqlServer
,ex.SqlProcedureName
,ex.SqlLineNumber
,ex.Tsql
,ex.CallStack);
}
catch(Exception e)
{
var ex = new ExceptionObject(e);
logger.LogException(e,"{this} exception due to {message} signature: signature}\nCallStack:", e.GetType().Name,e.Message,ex.Signature,ex.CallStack);
}
}

Error in Custom DibsPaymentMethodService Overriding ProcessCallback

I'm trying to customize the dialogue between uCommerce and DIBS to allow ticket registrations.
So far, I've succeeded in copying the DibsPageBuilder and adding the maketicket parameter. However, I need to customize the callback made to the function ProcessCallcack in the DibsPaymentMethodService class.
I copied the ProcessCallback and its content carefully and added it to my custom class, but when I run the checkout pipeline I keep getting the following error:
UCommerce.Pipelines.PipelineException: Exception occoured while processing pipeline
'UCommerce.Pipelines.Checkout.CheckoutPipeline'. See inner exception for details.
---> System.Security.SecurityException: Payment insufficient to cover order total
for OrderGuid xxx. Please ensure that payments cover the entire value of the order
before checking out. at
Commerce.Pipelines.Checkout.ValidatePaymentsMadeAgainstOrderTotalTask.Execute(PurchaseOrder subject)
at UCommerce.Pipelines.Pipeline`1.Execute(T subject) --- End of inner exception
stack trace ---...
I don't get why the checkout pipeline fail to succeed when I manually override the ProcessCallback with the same code the function usually is made of. In other words, if I don't override ProcessCallback the function runs smoothly, but then I don't get to do my customization. I have to say, that the error occurs before I customize anything.
My current uCommerce platform runs version 2.6.1. The code I've copied is:
public override void ProcessCallback(Payment payment)
{
if (payment.PaymentStatusId != 10000001)
return;
DibsPaymentMethodServiceConfigurationSection instance =
DibsPaymentMethodServiceConfigurationSection.Instance;
string s = HttpContext.Current.Request["transact"];
if (string.IsNullOrEmpty(s))
throw new ArgumentException("transact must be present in query string.");
int result;
if (!int.TryParse(s, out result))
throw new FormatException("transact must be a valid int32");
PaymentStatusCode paymentStatusCode = PaymentStatusCode.Authorized;
if (instance.UseMd5)
{
string parameter1 = this.GetParameter("authkey", "When using md5 \"{0}\" cannot be null or empty", new string[1]
{
"authkey"
});
string parameter2 = this.GetParameter("currency", "When using md5 \"{0}\" cannot be null or empty", new string[1]
{
"currency"
});
string parameter3 = this.GetParameter("amount", "When using md5 \"{0}\" cannot be null or empty", new string[1]
{
"amount"
});
int currencyNumber = new CurrencyCodeTranslater().FromIsoCode(parameter2);
string postMd5Key = new DibsMd5Computer().GetPostMd5Key(result.ToString(), parameter3, currencyNumber);
if (!parameter1.Equals(postMd5Key))
paymentStatusCode = PaymentStatusCode.Declined;
}
payment.PaymentStatus = PaymentStatus.Get((object) paymentStatusCode);
payment.TransactionId = result.ToString();
this.ProcessPaymentRequest(new PaymentRequest(payment.PurchaseOrder, payment));
}
Many thanks in advance.
/brinck10

Unit Testing contents of error message?

I have a class that raises an event with an error message.
In some of my tests I am subscribing to the event and asserting that the error message is not empty.
[Test]
public MyMethod_DoBad_ErrorMessageNotEmpty()
{
var logic = new MyClass();
string ErrorMessage = String.Empty;
logic.DisplayError += delegate(string s)
{
ErrorMessage = s;
};
logic.DoItBadly();
Assert.IsFalse(String.IsNullOrWhiteSpace(ErrorMessage));
}
//MyClass
public void DoItBadly()
{
//do something naughty but not final
DisplayError("Naughty");
//some other problem arises
if (1==1)
DisplayError("Something else naughty");
}
However I am starting to find in edge case testing that my new tests that should fail, pass because it has raised an error event previously in the code before it has got to where I want it to.
Therefore should I be asserting that the error message contains a specified string?
However I am starting to find in edge case testing that my new tests that should fail, pass because it has raised an error event previously in the code before it has got to where I want it to.
That suggests you're either reusing an existing object between tests, or that your test is doing too much. If you can't help but do work before the real operation you want to test, you can write your test as:
// Construct objects
// Do setup work
// Check that there's no error message yet
// Do work you expect to fail
// Check that there *is* an error message
Of course you can check for the exact error message, but that's likely to end up being time-consuming. If you're using reasonably ad-hoc error reporting (not worrying about i18n etc) then I'd personally just check whether an error message is present or not.
I think you should test both this cases in different tests:
[Test]
public ShouldRaiseNaughtyErrorWhenDoBadly()
{
var logic = new MyClass();
string errorMessage = String.Empty;
logic.DisplayError += delegate(string s) {errorMessage = s; };
logic.DoItBadly();
Assert.That(errorMessage, Is.EqualTo("Naughty"));
}
[Test]
public ShouldRaiseElseNaughtyErrorWhenDoBadlyWithOtherProblem()
{
var logic = new MyClass();
string errorMessage = String.Empty;
logic.DisplayError += delegate(string s) {errorMessage = s; };
// do something for other problem condition
logic.DoItBadly();
Assert.That(errorMessage, Is.EqualTo("Something else naughty"));
}
Or, if you need to check both errors where raised:
[Test]
public ShouldRaiseBothErrors()
{
var logic = new MyClass();
List<string> errorMessages = new List<string>();
logic.DisplayError += delegate(string s) {errorMessages.Add(s); };
// do something for other problem condition
logic.DoItBadly();
Assert.That(errorMessages.Count, Is.EqualTo(2));
Assert.That(errorMessages[0], Is.EqualTo("Naughty"));
Assert.That(errorMessages[1], Is.EqualTo("Something else naughty"));
}
UPDATE:
Considering event-based nature of your notifications, you can catch them all and then search for some concrete error:
[Test]
public ShouldRaiseNaughtyErrorWhenDoBadly()
{
var logic = new MyClass();
List<string> errorMessages = new List<string>();
logic.DisplayError += delegate(string s) { errorMessages.Add(s); };
logic.DoItBadly();
Assert.That(errorMessages.Contains("Naughty"));
}
Ideally, you would want to isolate and abstract the areas of DoItBadly() that are polluting your error text with the error message, so that you can test the rest of the method without problem.
However, given the understanding that it can often be easier said than done, the next best thing would be to only populate ErrorMessage with s if a certain condition is met (or have a whitelist of error messages that would not populate ErrorMessage). So if you only set ErrorMessage if it is not the error you have deemed "acceptable," then your test should pass and your own requirement should be met.
Though even better would be to assert a positive outcome, instead of making your success case the absence of negative outcomes.

Categories