I am working on a switch case that triggers based certain guid is encountered..
Problem is that I can not store the guid without making it static readonly.
How do i resolve this issue?
public struct Types
{
public static readonly Guid Standard = new Guid("{11111111-1111-1111-1111-111111111111}");
public static readonly Guid Morning = new Guid("{22222222-2222-2222-2222-222222222222}");
}
public string GetStyle(Guid stage)
{
switch (stage)
{
case Types.Standard:
return "Not normal";
case Types.Morning:
return "Not anormal";
default:
return "Normal";
break;
}
}
With the latest switch syntax (aka "pattern matching") you can achieve that:
public static string GetStyle(Guid stage)
{
switch (stage)
{
case Guid standard when standard == new Guid("{11111111-1111-1111-1111-111111111111}"):
return "Not normal";
case Guid morning when morning == new Guid("{22222222-2222-2222-2222-222222222222}"):
return "Not anormal";
default:
return "Normal";
}
}
One way to handle this is to use a Dictionary<Guid, string> to map the guids to their string counterparts, and then just return a value from the dictionary if it exist. This alleviates the need for a switch statement altogether, and should result in cleaner code.
private Dictionary<Guid, string> StyleMap = new Dictionary<Guid, string>
{
{Types.Standard, "Not normal" },
{Types.Morning, "Not anormal" },
};
public string GetStyle(Guid stage)
{
string result;
return StyleMap.TryGetValue(stage, out result) ? result : "Normal";
}
Related
This should be simple but I can't seem to figure it out. I have a function to send data to an API:
public bool UpdateCustomer(int customerid, string status)
{
apicall(customerid, status);
return true;
}
UpdateCustomer(1234, "shipped")
This works fine. But the API has a predefined list of acceptable statuses, and I don't want to leave it to the person calling the function to get it right, I want to create some sort of enum or something:
public bool UpdateCustomer(int customerid, Status status)
{
apicall(customerid, status);
return true;
}
UpdateCustomer(1234, Statuses.Shipped)
I want to pass the value to the API of their actual accepted value, "shipped".
I've tried creating a class with static strings:
public class Status
{
public static string Cancelled = "cancelled";
public static string Unsubmitted = "unsubmitted";
public static string Saved = "saved";
public static string Submitted = "submitted";
public static string Arrived = "arrived";
public static string Completed = "completed";
public static string Shipped = "shipped";
}
but when I go to call UpdateCustomer(1234, Status.Shipped), it says it can't convert from a string to Status. I've done this with enums plenty of times but I can't use an enum because those only support int values.
What is the easiest way of doing this with string values (or anything besides integers, really)?
Edit: It does work if I set the function to public bool UpdateCustomer(int customerid, string status) and call it with UpdateCustomer(1234, Statuses.Shipped) but that defeats the purpose of idiot-proofing this, you can still pass whatever string value you want. I really want the function parameter to be type of Status so it forces the caller to use one of the pre-defined statuses.
enum Status
{
Cancelled,
Saved,
..
}
public bool UpdateCustomer(int customerid, Status status)
{
apicall(customerid, status.ToString());
return true;
}
enum StatusEnum
{
Cancelled,
Unsubmitted,
Saved,
Submitted,
Arrived,
Completed,
Shipped
}
public bool UpdateCustomer(int customerid, StatusEnum status)
{
string statusStr;
switch(status)
{
case StatusEnum.Cancelled:
statusStr = "cancelled";
break;
case StatusEnum.Unsubmitted:
statusStr = "unsubmitted";
break;
case StatusEnum.Saved:
statusStr = "saved";
break;
case StatusEnum.Submitted:
statusStr = "submitted";
break;
case StatusEnum.Arrived:
statusStr = "arrived";
break;
case StatusEnum.Completed:
statusStr = "completed";
break;
case StatusEnum.Shipped:
statusStr = "shipped";
break;
case default:
throw new NotImplementedException();
}
apicall(customerid, statusStr);
return true;
}
This can be done with a dictionary. Let's say you have a "Status" enum with 3 values. Complete, Incomplete, and Abandoned. Then you'll want to create a dictionary to be used as a lookup table.
Dictionary<Status, string> statusDict = new Dictionary<Status, string>()
{
{ Status.Complete, "Complete" },
{ Status.Incomplete, "Incomplete" },
{ Status.Abandoned, "Abandoned" }
};
When you receive the Status enum from them (CustomerStatus), all you need to do for your API call is...
apicall(customerID, statusDict[CustomerStatus]);
In a .NET Core 2.1 project, I'm using EF Core with Command pattern (using MediatR library) on a SQL Server database.
I setup the project to avoid client query evaluation, by using these settings:
var phaseOptions = new DbContextOptionsBuilder<PhaseDbContext>().UseSqlServer(configuration.GetConnectionString("PhaseDbContext"),
sqlServerOptions => sqlServerOptions
.EnableRetryOnFailure(
maxRetryCount: 5,
maxRetryDelay: TimeSpan.FromSeconds(30),
errorNumbersToAdd: null))
.ConfigureWarnings(warnings => warnings
.Throw(RelationalEventId.QueryClientEvaluationWarning)) // Disable Client query evaluation
.Options;
Now I get a QueryClientEvaluationException with this query:
var articleCodes = await PhaseContext.PhaseArticles
.Where(a => !request.ArticleFamily.HasValue || a.GetArticleFamily() == request.ArticleFamily.Value)
.ToListAsync(cancellationToken);
The problem is on the a.GetArticleFamily() method call, because that method now is defined as follows, inside the PhaseArticle entity class:
public class PhaseArticle
{
public int Id { get; set; }
public string Code { get; set; }
public string Description { get; set; }
public string UnitOfMeasure { get; set; }
public string Category { get; set; }
public string Group { get; set; }
public string Family { get; set; }
public double UnitCost { get; set; }
public string AdditionalDescription { get; set; }
public string ExternalCode { get; set;}
public string ColorCode { get; set;}
public string Note { get; set; }
public ArticleFamily GetArticleFamily()
{
switch (Family)
{
case "CEL":
return ArticleFamily.Cell;
case "STR":
return ArticleFamily.String;
case "RAW":
return ArticleFamily.OtherRawMaterial;
case "SFP":
return ArticleFamily.SemiFinishedPanel;
case "FP":
return ArticleFamily.FinishedPanel;
default:
return ArticleFamily.Other;
}
}
}
Now, I want to know if it is possible to keep the QueryClientEvaluationWarning option by somehow refactoring (and probably moving away from the entity class) the GetArticleFamily() method.
Update 2019/02/26
#StriplingWarrior I've updated again the code with your suggestion about ValueConverter(), but now it is giving this error:
Cannot convert Lambda expression into a tree of expressions.
Update 2019/02/25
Following #StriplingWarrior suggestion, I'm trying to write a custom converter but I'm not able to make my code compile.
The error with the code below is about return value of the first switch block (it's string but it is expected to be an enum) and about the expected input value of the second switch block (it's a string but it is expected to be an enum).
This is the code:
public static void ApplyPhaseConversions<T>(this ModelBuilder modelBuilder)
{
modelBuilder
.Entity<PhaseArticle>()
.Property(e => e.Family)
.HasConversion(new ValueConverter<ArticleFamily, string> {
v =>
{
switch (v)
{
case ArticleFamily.Cell:
return "CEL";
case ArticleFamily.String:
return "STR";
case ArticleFamily.OtherRawMaterial:
return "RAW";
case ArticleFamily.SemiFinishedPanel:
return "SFP";
case ArticleFamily.FinishedPanel:
return "FP";
default:
return "";
}
},
v =>
{
switch (v)
{
case "CEL":
return ArticleFamily.Cell;
case "STR":
return ArticleFamily.String;
case "RAW":
return ArticleFamily.OtherRawMaterial;
case "SFP":
return ArticleFamily.SemiFinishedPanel;
case "FP":
return ArticleFamily.FinishedPanel;
default:
return ArticleFamily.Other;
}
}});
}
It looks like you're using GetArticleFamily() to convert between the database values and your C# enums. EF Core has a built-in feature called Value Conversions which is meant to address this: https://learn.microsoft.com/en-us/ef/core/modeling/value-conversions
You should be able to define a ValueConverter to translate to and from ArticleFamily values, and then change the type of the Family property to ArticleFamily, and use that property in your query:
var articleCodes = await PhaseContext.PhaseArticles
.Where(a => !request.ArticleFamily.HasValue || a.Family == request.ArticleFamily.Value)
.ToListAsync(cancellationToken);
PS--I'm not sure what kind of query the code above will produce, but it might be better to compose your query like this:
var articleQuery = PhaseContext.PhaseArticles.AsQueryable();
if(request.ArticleFamily.HasValue)
{
articleQuery = articleQuery.Where(a => a.Family == request.ArticleFamily.Value);
}
var articleCodes = await articleQuery.ToListAsync(cancellationToken);
You can create a new variable and transfer the request.ArticleFamily.Value result so it can return ArticleFamily.Cell or ArticleFamily.String and then run the query
e.g.
if(request != null && !request.ArticleFamily.HasValue)
// or throw an exception here
return ...;
ArticleFamily newVariable = (ArticleFamily)Enum.Parse(typeof(ArticleFamily), request.ArticleFamily);
var articleCodes = await PhaseContext.PhaseArticles
.Where(a => a.Family == newVariable)
.ToListAsync(cancellationToken);
The validation of the method's parameters should be done before running the query. Another thing is what happens if the request is null?
Edit
There is also a need to validate the request object. There can be a case when you make a typo or mistake in structure(you forget to add a comma after defining a value of field) of the JSON object that you send to the API. In such a case, the request object would have a null value so there is a need to validate such a behavior. E.g. you can add
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
in the action of your controller to validate the whole request body. The client will receive the proper error message.
Finally, the solution was almost there, as also #StriplingWarrior said.
Due to limitations in the C# compiler, such that it can't create expression trees for this code, the solution is to factory the conversion code into methods and then call those in HasConversion.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder
.Entity<PhaseArticle>()
.Property(e => e.Family)
.HasConversion(new ValueConverter<ArticleFamily, string>(
v => StringFromArticleFamily(v),
v => ArticleFamilyFromString(v));
}
private static ArticleFamily ArticleFamilyFromString(string family)
{
switch (family)
{
case "CEL":
return ArticleFamily.Cell;
case "STR":
return ArticleFamily.String;
case "RAW":
return ArticleFamily.OtherRawMaterial;
case "SFP":
return ArticleFamily.SemiFinishedPanel;
case "FP":
return ArticleFamily.FinishedPanel;
default:
return ArticleFamily.Other;
}
}
private static string StringFromArticleFamily(ArticleFamily articleFamily)
{
switch (articleFamily)
{
case ArticleFamily.Cell:
return "CEL";
case ArticleFamily.String:
return "STR";
case ArticleFamily.OtherRawMaterial:
return "RAW";
case ArticleFamily.SemiFinishedPanel:
return "SFP";
case ArticleFamily.FinishedPanel:
return "FP";
default:
return "";
}
}
am using generated protobuf code (see http://code.google.com/p/protobuf/)
I have a generated class looks like this :
public class Fruits{
private int _ID_BANANA = (int)1;
private int _ID_APPLE = (int)2;
public int ID_BANANA
{
get { return _ID_BANANA; }
set { _ID_BANANA = value; }
}
public int ID_APPLE
{
get { return _ID_APPLE; }
set { _ID_APPLE = value; }
}
}
Then they are constant values but I can't use then as such in my code.
For example I want to do a mapper like this :
public static Color GetColor(int idFruit) {
switch (idFruit)
{
case new Fruits().ID_BANANA:
return Color.Yellow;
case new Fruits().ID_APPLE:
return Color.Green;
default:
return Color.White;
}
}
I have the error : a constant value is expected.
I thougth about creating an enum, but seems to be the wrong way, tryed something like :
public const int AppleId = new Fruits().ID_APPLE;
Not working either...
Someone have an idea?
Why not to use if else if statements here?
var fruits = new Fruits();
if (idFruit == fruits._ID_BANANA)
return Color.Yellow;
else if (idFruit == fruits._ID_APPLE)
return Color.Green;
else
return Color.White;
Or dictionary:
this.fruitsColorMap = new Dictionary<int, Color>
{
{ fruits._ID_BANANA, Color },
{ fruits._ID_APPLE, Green }
};
And then:
public static Color GetColor(int idFruit) {
if (this.fruitsColorMap.ContainsKey(idFruit) {
return this.fruitsColorMap[idFruit];
}
return Color.White;
}
The case values in a switch statement have to be constants - that's part of the switch statement's definition:
switch (expression)
{
case constant-expression:
statement
jump-statement
[default:
statement
jump-statement]
}
as per the switch (C#) documentation from Microsoft.
you will notice the constant-expression bit. That means that it can be determined at compile time. So calls to functions (and a reference to a property property is really a function call in disguise) are not allowed here.
We have constants defined in a static class along with functions like
public const string MSG_1 = "New error {0}"
public const string MSG_2 = "Random error occurred {1}"
public static string Message_ToForm(string MType, string[] vals)
public static GetNewType(string MType)
{
switch (MType)
{
case "MSG_1" : ...........
}
}
I require to call it from a program like
Message_ToForm("MSG_1", string[]);
How can I convert it to get the value of the constant from string type?
Basically, it should return me as "New error {0} when "MSG_1" is passed?
You might need a return type as String for your GetNewType
Suggestion:
If the constants are NOT reused and it if its ONLY for your lookup.
You could use a Dictionary to do the lookup
Dictionary<string, string> constValues = new Dictionary<string, string>()
{
{"MSG_1", "New error {0}"},
{"MSG_2", "Random error occurred {1}"}
};
public string GetNewType(string MType)
{
if (constValues.ContainsKey(MType))
return constValues[MType];
return string.Empty;
}
I'm really confused with your question, but think that's what you're looking for:
public static string GetNewType(string MType)
{
switch (MType)
{
case "MSG_1": return MSG_1;
case "MSG_2": return MSG_2;
default: throw new ArgumentException("MType");
}
}
But I must say - that's really bad approach! You should rethink your design.
I would create a MessageType enumeration and switch based on that.
enum MessageType
{
None = 0,
Msg1,
Msg2
}
public static string GetNewType(MessageType MType)
{
string msg = string.Empty;
switch (MType)
{
case MessageType.Msg1:
msg = MSG_1;
break;
case MessageType.Msg2:
msg = MSG_2;
break;
}
return msg;
}
You were missing a return type on your method. I believe this is what you want.
static string GetNewType(string MType)
{
switch (MType)
{
case "MSG_1" :
return MSG_1;
case "MSG_2":
return MSG_2;
}
return "";
}
But is there are reason your strings are saved as constants in variables? Couldn't you just return the strings in the switch? Like this:
switch (MType)
{
case "MSG_1" :
return "New error {0}";
case "MSG_2":
return "Random error occurred {1}";
}
Create a static readonly ReadOnlyDictionary property populated with your 'constants':
private static readonly System.Collections.ObjectModel.ReadOnlyDictionary<string, string> _msgDictionary =
new System.Collections.ObjectModel.ReadOnlyDictionary<string, string>(
new Dictionary<string, string>()
{
{ "MSG_1", "New error {0}" },
{ "MSG_2", "Random error occurred {1}" }
});
public static System.Collections.ObjectModel.ReadOnlyDictionary<string, string> Messages
{
get { return _msgDictionary; }
}
Then usage:
var msg = Messages["MSG_1"];
public static GetNewType(string MType)
{
string MType = yourClass.MSG_1
switch (MType)
{
case yourClass.MSG_1
break;
....
default:
// Code you might want to run in case you are
// given a value that doesn't match.
break;
}
}
I strongly recommend using the out of the box .NET localization feature. You simply add a resource file to your project and put all your string messages into it, the resource file is like a key value pair of string resources, the IDE automatically creates a property for each string message which you can use in your code.
Check this How to use localization in C# for more details.
That is, I have a method such as the following:
public static int CreateTaskGroup(string TaskGroupName,
string Market = "en-us", string Project = "MyProject",
string Team = "DefaultTeam", string SatelliteID="abc");
I would like to call this method from the command line, by reading the standard array of command line arguments. The obvious way to do it would be as follows:
if (args.Length == 1) CreateTaskGroup(args[0]);
if (args.Length == 2) CreateTaskGroup(args[0], args[1]);
if (args.Length == 3) CreateTaskGroup(args[0], args[1], args[2]);
Is it possible to do this in a more concise way?
Here's one alternative, with the downside that you have to redeclare the default value constants:
CreateTaskGroup(
args[0],
args.ElementAtOrDefault(1) ?? "en-us",
args.ElementAtOrDefault(2) ?? "MyProject",
args.ElementAtOrDefault(3) ?? "DefaultTeam",
args.ElementAtOrDefault(4) ?? "abc");
You can reduce this issue by declaring the strings as consts, e.g.:
public const string MarketDefault = "en-us";
public static int CreateTaskGroup(string TaskGroupName,
string Market = MarketDefault, ...)
static void Main(string[] args)
{
CreateTaskGroup(
args[0],
args.ElementAtOrDefault(1) ?? MarketDefault,
...);
}
But then it's not guaranteed by the compiler, nor overtly obvious, that MarketDefault is, in fact, still (code can be refactored in the future) the default for Market.
Edit: Here's an alternate solution, using reflection:
var argsForMethod = new List<string>(args);
var m = typeof(Program).GetMethod("CreateTaskGroup");
foreach (var p in m.GetParameters().Skip(args.Length))
if (p.Attributes.HasFlag(ParameterAttributes.HasDefault))
argsForMethod.Add((string)p.DefaultValue);
else
throw new NotImplementedException();
var result = (int)m.Invoke(null, argsForMethod.ToArray());
This can be a bit hard to read, and won't be too fast, but it does what you asked, without resorting to repetitive code, or having any uncertainty as to the default value of the parameters. You'll probably want to add some error handling for too few or too many parameters. I prefer this solution.
How about using params in CreateTaskGroup something like this
public static int CreateTaskGroup(params string[] args)
{
for ( int i = 0 ; i < args.Length ; i++ )
{
............
This is how I would implement the class to keep things clean and to assign the responsibility of knowing the default values to the TaskGroupCreator.
public class TaskGroupCreator
{
private string[] values;
public TaskGroupCreator(string[] values)
{
this.values = values;
}
public string TaskGroupName
{
get { return values[0]; }
}
public string Market
{
get { return this.GetElement(1, "en-us"); }
}
public string Project
{
get { return this.GetElement(2, "MyProject"); }
}
public string Team
{
get { return this.GetElement(3, "DefaultTeam"); }
}
public string SatelliteID
{
get { return this.GetElement(4, "abc"); }
}
public int CreateTaskGroup()
{
// Do stuff with your properties...
}
private string GetElement(int index, string defaultValue)
{
return this.values.ElementAtOrDefault(index) ?? defaultValue;
}
}
Usage:
var taskGroup = new TaskGroupCreator(args).CreateTaskGroup();
I'd do it this way..
CreateTaskGroup(args);
//.....
public static int CreateTaskGroup(params string[] args) {
if (args.Length == 0) throw new Exception("nope!");
args = args.Concat(Enumerable.Range(0, 5 - args.Length)
.Select<int, string>(_ => null)).ToArray();
string TaskGroupName = args[0];
string Market = args[1] ?? "en-us";
string Project = args[2] ?? "MyProject";
string Team = args[3] ?? "DefaultTeam";
string SatelliteID = args[4] ?? "abc";
//......
}
params keyword isn't mandatory, but could be convenient...
This is probably best out of what I've come up with:
public static int CreateTaskGroup(string[] arguments)
{
// optional error handling here
string TaskGroupName = arguments[0];
string Market = arguments.ElementAtOrDefault(1) ?? "en-us";
string Project = arguments.ElementAtOrDefault(2) ?? "MyProject";
string Team = arguments.ElementAtOrDefault(3) ?? "DefaultTeam";
string SatelliteID = arguments.ElementAtOrDefault(4) ?? "abc";
// function body as it was
This does the same thing but is less concise:
public static int CreateTaskGroup(string[] arguments)
{
string TaskGroupName, Market, Project, Team, SatelliteID;
switch (arguments.Length)
{
case 5:
string SatelliteID = arguments[4] ?? "abc";
goto case 4;
case 4:
string Team = arguments[3] ?? "DefaultTeam";
goto case 3;
case 3:
string Project = arguments[2] ?? "MyProject";
goto case 2;
case 2:
string Market = arguments[1] ?? "en-us";
goto case 1;
case 1:
string TaskGroupName = arguments[0];
break;
case 0:
// error handling here;
}
// function body as it was
You could call it concisely like this:
CreateTaskGroup(args);