How can I shorten beautifully this C# code? - c#

I want to eliminate some duplicated code. Can anyone make this code shorter and better?
switch (now.site)
{
case item.SITE.AMAZON:
try
{
price = driver.FindElement(By.XPath("//*[#id=\"priceblock_ourprice\"]")).Text;
fetched = true;
}
catch
{
try
{
price = driver.FindElement(By.XPath("//*[#id=\"priceblock_dealprice\"]")).Text;
fetched = true;
}
catch
{
fetched = false;
}
}
break;
case item.SITE.ALI:
try
{
price = driver.FindElement(By.XPath("//*[#id=\"j-sku-discount-price\"]")).Text;
fetched = true;
}
catch
{
try
{
price = driver.FindElement(By.XPath("//*[#id=\"j-sku-price\"]")).Text;
fetched = true;
}
catch
{
fetched = false;
}
}
break;
}
}

You could create a dictionary of available path strings. I don't know what the type of site is. I assume string
private static Dictionary<string, string[]> pricePaths = new Dictionary<string, string[]> {
[item.SITE.AMAZON] = new string[] { "//*[#id=\"priceblock_ourprice\"]",
"//*[#id=\"priceblock_dealprice\"]" },
[item.SITE.ALI] = new string[] { "//*[#id=\"j-sku-discount-price\"]",
"//*[#id=\"j-sku-price\"]" },
};
With this, you can write a more generic logic.
bool fetched = false;
if (pricePaths.TryGetValue(now.site, out string[] paths)) {
foreach (string path in paths) {
try {
price = driver.FindElement(By.XPath(path)).Text;
fetched = true;
break;
} catch {
}
}
}
It allows you to easily add new sites. The sites can have an arbitrary number of alternate paths.

It appears that you're really trying to get a decimal value. Let's use this assumption and make a method that returns a decimal? to indicate success (i.e. a value) or failure (i.e. null).
public static class Ex
{
public static decimal? FindDecimalMaybe(this IWebDriver driver, string path)
{
try
{
if (decimal.TryParse(driver.FindElement(By.XPath(path)).Text, out decimal result))
{
return result;
}
}
catch { } // I hate this, but there doesn't seem to be a choice
return null;
}
}
The purpose of this method is to hide the unpleasant fact that the Selenium library appears to code by exception.
I've also created it as an extension method so that the call to driver.FindElement is replaced by something that looks familiar - driver.FindDecimalMaybe.
Now I take the same approach as Oliver with a dictionary:
private static Dictionary<string, string[]> __pricePaths = new Dictionary<string, string[]>
{
{ item.SITE.AMAZON, new [] { "//*[#id=\"priceblock_ourprice\"]", "//*[#id=\"priceblock_dealprice\"]" } },
{ item.SITE.ALI, new [] { "//*[#id=\"j-sku-discount-price\"]", "//*[#id=\"j-sku-price\"]" } },
};
Now it's trivial to get the values out:
decimal? price =
__pricePaths[now.site]
.Select(path => driver.FindDecimalMaybe(path))
.Where(x => x.HasValue)
.FirstOrDefault();
If the price variable has a value then success, but if it is null then the call did not succeed.

Here are some recommendations as to how you can beautify your code -
Declare constants for all XPath queries.
Declare a variable outside the switch statement to capture the price value based on your case statement.
Create a function with one try catch statements which accepts 2 xPath strings and an out parameter for Price and returns a boolean value to indicate if the query was successful.
Remove the try catch from under each case and call that function passing the correct xPath strings from the set of constants.
const string priceblock_ourprice = "//*[#id=\"priceblock_ourprice\"]";
const string priceblock_dealprice = "//*[#id=\"priceblock_dealprice\"]";
const string j_sku_discount_price = "//*[#id=\"j-sku-discount-price\"]";
const string j_sku_price = "//*[#id=\"j-sku-price\"]";
public void YourPrimaryFunction
{
decimal price;
switch (now.site)
{
case item.SITE.AMAZON:
fetched = FetchPrice(priceblock_ourprice, priceblock_dealprice, out price);
break;
case item.SITE.ALI:
fetched = FetchPrice(j_sku_discount_price, j_sku_price, out price);
break;
}
}
private bool FetchPrice(string xPathPrim, string xPathFallBack, decimal out price)
{
try
{
price = driver.FindElement(By.XPath(xPathPrim)).Text;
return true;
}
catch
{
try
{
price = driver.FindElement(By.XPath(xPathFallBack)).Text;
return true;
}
catch
{
return false;
}
}
}

Related

Posting a dictionary to .net core 3.1. API

I want to post the following DTO to a .NET Core API:
{
"Name": "Foo",
"Street": "Bar",
"DynamicInfo": {
"MetadataAsString": "23423",
"MetadataAsInt": 2,
"MetadataAsBool": true,
"SomeOtherValues": "blub"
}
}
The class I want to map this in C# looks like this:
public class Foo
{
public string Name { get; set; }
public string Street { get; set; }
public Dictionary<string, object> DynamicInfo { get; set; }
}
You can see that I am using two static properties (Name and Street), but I also want to post some dynamic data.
I expect the dynamic data to be written in a dictionary, but unfortunately this does not work.
The result I am getting in my debugger is a little confusing:
So the values arrive successful, but are in a strange format... I dont know how to even access the values.
How can I convert this to just a normal dictionary, containing objects?
For a solution that would not depend on your use case, I would use 2 objects. 1 for client input and 1 that has validated input. The client input object would contain a Dictionary<string,string>. And the validated input can contain your Dictionary<string,object> if its still something you intend to use.
If you use this approach. During validation of the client input, you could use bool.TryParse(DynamicInfo["MetadataAsBool"], out YourBoolean). Then simply add the YourBoolean to your new Dictionary<string,object> objectDictionary like objectDictionary.Add("BoolMetadata", YourBoolean)
I found a solution. The resulting object (ValueKind=String: 23423) is nothing else than a JSONElement. I did not understand this before.
This JSONElement has an enum that tells me what datatype I have, so I can use this to map my Dictionary of JSONElements to another dictionary of "real" objects.
var newDic = new Dictionary<string, object>();
foreach (var d in obj.DynamicInfo)
{
object obt;
string key = d.Key;
var a = enterprise.BasicInformation.TryGetValue(key, out obt);
if (obt == null) continue;
var doc = (JsonElement)obt;
string myString = null;
bool? myBool = null;
int? myInteger = null;
double? myFloatNumber = null;
if (doc.ValueKind == JsonValueKind.String)
{
myString = doc.ToString();
}
if (doc.ValueKind == JsonValueKind.True)
{
myBool = true;
}
if (doc.ValueKind == JsonValueKind.False)
{
myBool = false;
}
if (doc.ValueKind == JsonValueKind.Number)
{
double floatNumber;
doc.TryGetDouble(out floatNumber);
if ((floatNumber % 1) == 0)
{
myInteger = (int)floatNumber;
}
else
{
myFloatNumber = floatNumber;
}
}
if (myString != null)
{
newDic.Add(key, myString);
}
if (myBool != null)
{
newDic.Add(key, myBool);
}
if (myInteger != null)
{
newDic.Add(key, myInteger);
}
if (myFloatNumber != null)
{
newDic.Add(key, myFloatNumber);
}
}
The code might not be perfect - I will try to optimize it. But it does what it should.

Is there a simpler way to return fields from static function?

I often want to parse a string into various bits and have a readable way to return them.
I like this approach, but it involves creating a specific class
long orderID = Utils.UnTradeIdent(tradeIdent).OrderID;
In Utils.cs:
public class TradeIdentData
{
public string AccountIdent;
public long OrderID;
public string SubID;
}
public static TradeIdentData UnTradeIdent(string tradeIdent)
{
TradeIdentData tradeIdentData = new TradeIdentData();
var parts = tradeIdent.Split('!');
tradeIdentData.AccountIdent = parts[0];
if (parts[1].Contains("."))
{
var bits = parts[1].Split('.');
tradeIdentData.OrderID = long.Parse(bits[1]);
tradeIdentData.SubID = bits[1];
}
else
{
tradeIdentData.OrderID = long.Parse(parts[1]);
tradeIdentData.SubID = "";
}
return tradeIdentData;
}
A separate class with well-named properties (which you are already using) is currently the most readable way of doing this.
In C# 7 you will be able to use tuples for return values, like so:
public static (string AccountIdent, string OrderID, string SubID) UnTradeIdent(string tradeIdent)
{
string accountIdent, orderID, subID ;
... Code to initialise accountIdent, orderID and subID appropriately ...
// Now return the data as a tuple:
return (accountIdent, orderID, subID);
}
You can consume this as follows:
long orderID = Utils.UnTradeIdent(tradeIdent).OrderID;
Or if you want all the values:
var result = Utils.UnTradeIdent(tradeIdent);
// Use result.OrderId, result.SubID or result.AccountIdent
This is not going to be available until some time next year, though.
Also, even though this new tuple support makes it more convenient to WRITE the code, it doesn't let you document it using XML comments as well. Spending the time to write a simple and well-documented class will still often be better than using the new C# 7 tuple support.
See here for more details.
You can also use the out keyword to pass arguments by reference, see MSDN article out (C# Reference):
public static void UnTradeIdent(string tradeIdent, out string AccountIdent, out long OrderID, out string SubID)
{
var parts = tradeIdent.Split('!');
AccountIdent = parts[0];
if (parts[1].Contains("."))
{
var bits = parts[1].Split('.');
OrderID = long.Parse(bits[1]);
SubID = bits[1];
}
else
{
OrderID = long.Parse(parts[1]);
SubID = "";
}
}
UPDATED with suggestion from comments:
public static bool UnTradeIdent(string tradeIdent, out string AccountIdent, out long OrderID, out string SubID)
{
bool result = false;
AccountIdent = "";
OrderID = 0;
SubID = "";
try
{
var parts = tradeIdent.Split('!');
AccountIdent = parts[0];
if (parts[1].Contains("."))
{
var bits = parts[1].Split('.');
OrderID = long.Parse(bits[1]);
SubID = bits[1];
}
else
{
OrderID = long.Parse(parts[1]);
SubID = "";
}
}
catch(ArgumentNullException ane)
{
// Handle parsing exception
}
catch (FormatException fe)
{
// Handle parsing exception
}
catch (OverflowException oe)
{
// Handle parsing exception
}
return result;
}
Its pretty simple to do just by changing the return type to dynamic and using an anonymous class
public static dynamic UnTradeIdent(string tradeIdent)
{
var value1 = //parselogic
var value2 = //parselogic
return new { Identity = value1, Item2 = value2};
}
I would consider making additional static methods. Your current implementation is cleaner when you require all of the returned properties, but something like below might be appropriate when you only need one of them.
public static string TradeIdentToAccountIdent(string tradeIdent)
{
var parts = tradeIdent.Split('!');
return parts[0];
}
public static long TradeIdentToOrderID(string tradeIdent)
{
var parts = tradeIdent.Split('!');
if (parts[1].Contains("."))
{
var bits = parts[1].Split('.');
return long.Parse(bits[1]); // Taken from your example, should probably be bits[0]?
}
else
return long.Parse(parts[1]);
}
// My own take on it this time, you could obviously use your logic as well.
public static string TradeIdentToSubID(string tradeIdent)
{
var order = tradeIdent.Split('!')[1];
if (order.Contains("."))
return order.Split('.')[1];
else
return String.Empty;
}

C# Mongo Query efficiency

I have a FilterDefinition build that will look for an address based on the properties that are not empty.
public static FilterDefinition<TU> FindPointByAddress<TU>(Address address)
{
var filterBuilder = Builders<TU>.Filter;
var filterItems = new List<FilterDefinition<TU>>();
if (!String.IsNullOrWhiteSpace(address.Street))
{
filterItems.Add(filterBuilder.Eq("Address.Street", address.Street));
}
if (!String.IsNullOrWhiteSpace(address.City))
{
filterItems.Add(filterBuilder.Eq("Address.City", address.City));
}
if (!String.IsNullOrWhiteSpace(address.StateProvince))
{
filterItems.Add(filterBuilder.Eq("Address.StateProvince", address.StateProvince));
}
if (!String.IsNullOrWhiteSpace(address.PostCode))
{
filterItems.Add(filterBuilder.Eq("Address.PostCode", address.PostCode));
}
return filterBuilder.And(filterItems);
}
IMO this query feels dirty, is there a better way to build this type of query or is this the correct way?
A few days ago I had a similar situation. I wrote a simple method that takes a field name and field value as a string.
public void AddEqualCompareFilter(string fieldName, string fieldValue)
{
if (String.IsNullOrEmpty(fieldValue) == false) {
if (Filter != null) {
Filter = Filter & Builders<TranslationsDocument>.Filter.Eq(fieldName, fieldValue);
}
else {
FilterCount++;
Filter = Builders<TranslationsDocument>.Filter.Eq(fieldName, fieldValue);
}
}
}
I am then using this snippet to decide based on FilterCount:
if (FilterCount > 0) {
Result = collection.Find(Filter).ToListAsync().GetAwaiter().GetResult();
return true;
}
else {
Result = collection.Find(new BsonDocument()).ToListAsync().GetAwaiter().GetResult();
return true;
}

Function to return contents of a specific string

It is possible for a function to read/return the value of a string whose name is passed to it?
For example, I want to return true if the value of a string is not "0" by telling the function which string I want it to check.
public static bool IsEnabled(string sName)
{
if (TenderTypes.<sName> != "0")
{
return true;
}
else
{
return false;
}
}
This is mainly to make things quicker while I'm coding, I want to be able to keep the strings there but at the same time I want to be able to disable them by changing their value from a string number, to zero.
Let's say you have something like this:
class TenderTypes
{
static string myStr = "Some value";
static string anotherStr = "A different value";
// ...
};
And you want to query that string by the name of a field (for example "myStr").
Well, it can be done with reflection. But first an alternative:
Using a Dictionary
class TenderTypes
{
static Dictionary<string, string> strings =
new Dictionary<string, string>()
{
{"myStr", "Some value},
{"anotherStr", "Some value}
}
//...
}
Then you could write your method like this:
public static bool IsEnabled(string sName)
{
if (TenderTypes.strings[sName] != "0")
{
return true;
}
else
{
return false;
}
}
Using Reflection
Then you could write your method like this:
public static bool IsEnabled(string sName)
{
var type = typeof(TenderTypes);
var field = type.GetField(sName, BindingFlags.Static);
if (field.GetValue(null) != "0")
{
return true;
}
else
{
return false;
}
}
Don't know exactly what you want to achieve, your syntax looks like you are trying to query something generic <>... this is not really possible.
Maybe a simple dictionary will help you?
Create a new Dictionary<string, bool> should also help you if your "settings" are enabled or disabled...

Unreachable Code Detected Return Value

How to return value with this issue?
Help me please.
protected string SendState(Object ID_DIP,Object ID_SEQ,Object MODUL)
{
try
{
ViewState["ssDIP"] = ID_DIP.ToString();
ViewState["ssSEQ"] = ID_SEQ.ToString();
ViewState["ssMOD"] = MODUL.ToString();
return ID_DIP.AsString();
return ID_SEQ.AsString();
return MODUL.ToString();
}
catch (Exception)
{
return "";
}
}
You have multiple return statements, your code will not execute statements after first return statement. You can't return multiple values from your method, if you want to return multiple values you can either return List<string> for your case or create a temporary class and return its object.
In your code you are using AsString, I think you probably meant ToString
Define a class like:
public class MyReturnObject
{
public string ID_DIP { get; set; }
public string ID_SEQ { get; set; }
public string MODUL { get; set; }
}
Modify your method like:
protected MyReturnObject SendState(Object ID_DIP, Object ID_SEQ, Object MODUL)
{
try
{
ViewState["ssDIP"] = ID_DIP.ToString();
ViewState["ssSEQ"] = ID_SEQ.ToString();
ViewState["ssMOD"] = MODUL.ToString();
MyReturnObject obj = new MyReturnObject();
obj.ID_DIP = ID_DIP.ToString();
obj.ID_SEQ = ID_SEQ.ToString();
obj.MODUL = MODUL.ToString();
return obj;
}
catch (Exception)
{
return null;
}
}
first thing is u have multiple return values, so ur code wouldn't work, so u can use List<String> & to return in catch case, u could use a var at the top of method definition, like this--->
public List<string> SendState(Object ID_DIP,Object ID_SEQ,Object MODUL)
{
var returnValue = new List<string>();
try
{
ViewState["ssDIP"] = ID_DIP.ToString();
ViewState["ssSEQ"] = ID_SEQ.ToString();
ViewState["ssMOD"] = MODUL.ToString();
returnValue.add(ID_DIP.AsString());
returnValue.add(ID_DIP.AsString());
returnValue.add(MODUL.ToString());
}
catch (Exception)
{
returnValue = null;
}
return returnValue;
}
now u can use the above method like this--->
var result = SendState( params ) //<--- params r ur parameters
if(result != null)
// proceed
else
// no value found
If you want return multiple results which has the same type. I suggest you should apply coroutine pattern. Specifics at here as code below:
protected IEnumerable<string> SendState(Object ID_DIP,Object ID_SEQ,Object MODUL)
{
ViewState["ssDIP"] = ID_DIP.AsString();
ViewState["ssSEQ"] = ID_SEQ.AsString();
ViewState["ssMOD"] = MODUL.ToString();
yield return ID_DIP.AsString();
yield return ID_SEQ.AsString();
yield return MODUL.ToString();
}
You take reference the link to get more understand about coroutine pattern in C#.
In C#, methods generally return only one value. This value can be an object with multiple fields.
If you need to return multiple values from a method, you can use out parameters or return a type instance containing all the values.
As Habib suggested, returning List<string> is a good approach in your case.
You can only return a single value from a method, so of your three return statements only the first one will execute.
If you want to return multiple strings, return an array of strings:
protected string[] SendState(Object ID_DIP,Object ID_SEQ,Object MODUL) {
try {
ViewState["ssDIP"] = ID_DIP.ToString();
ViewState["ssSEQ"] = ID_SEQ.ToString();
ViewState["ssMOD"] = MODUL.ToString();
return new string[] {
ID_DIP.ToString(),
ID_SEQ.ToString(),
MODUL.ToString()
};
} catch (Exception) {
return null; // or perhaps an empty array...?
}
}
Any code that is written after the first return statement (within the same block) will not execute. In case you want to return multiple values from a method, consider using objects.

Categories

Resources