|
Apologies for the shouting but this is important.
When answering a question please:
- Read the question carefully
- Understand that English isn't everyone's first language so be lenient of bad spelling and grammar
- If a question is poorly phrased then either ask for clarification, ignore it, or mark it down. Insults are not welcome
- If the question is inappropriate then click the 'vote to remove message' button
Insults, slap-downs and sarcasm aren't welcome. Let's work to help developers, not make them feel stupid..
cheers,
Chris Maunder
The Code Project Co-founder
Microsoft C++ MVP
|
|
|
|
|
For those new to message boards please try to follow a few simple rules when posting your question.- Choose the correct forum for your message. Posting a VB.NET question in the C++ forum will end in tears.
- Be specific! Don't ask "can someone send me the code to create an application that does 'X'. Pinpoint exactly what it is you need help with.
- Keep the subject line brief, but descriptive. eg "File Serialization problem"
- Keep the question as brief as possible. If you have to include code, include the smallest snippet of code you can.
- Be careful when including code that you haven't made a typo. Typing mistakes can become the focal point instead of the actual question you asked.
- Do not remove or empty a message if others have replied. Keep the thread intact and available for others to search and read. If your problem was answered then edit your message and add "[Solved]" to the subject line of the original post, and cast an approval vote to the one or several answers that really helped you.
- If you are posting source code with your question, place it inside <pre></pre> tags. We advise you also check the "Encode "<" (and other HTML) characters when pasting" checkbox before pasting anything inside the PRE block, and make sure "Use HTML in this post" check box is checked.
- Be courteous and DON'T SHOUT. Everyone here helps because they enjoy helping others, not because it's their job.
- Please do not post links to your question into an unrelated forum such as the lounge. It will be deleted. Likewise, do not post the same question in more than one forum.
- Do not be abusive, offensive, inappropriate or harass anyone on the boards. Doing so will get you kicked off and banned. Play nice.
- If you have a school or university assignment, assume that your teacher or lecturer is also reading these forums.
- No advertising or soliciting.
- We reserve the right to move your posts to a more appropriate forum or to delete anything deemed inappropriate or illegal.
cheers,
Chris Maunder
The Code Project Co-founder
Microsoft C++ MVP
|
|
|
|
|
I have mapper method which maps different types of property like bool,enum,string,int
But now I want to map my one of the custom class which is being inherited in several places
Back Story:
Earlier we were creating instance and assigning values in POM only, now we want to use Excel to pass value and map and then use in our methods
Object classes Which I want to map
namespace Framework.Model.PaymentOptions
{
public class PaymentOptions
{
public PaymentPortal portal;
public DeliveryMethod delivery = DeliveryMethod.Billing;
public PaymentOptions()
{
}
public PaymentOptions(Site site)
{
}
}
}
Which is inherited in below class
namespace Framework.Model.PaymentOptions
{
public class KlarnaOptions : PaymentOptions
{
//default - don't use card payment by deffault
public bool byCard = false;
public bool payLater = false;
public bool sliceIt = false;
public KlarnaOptions()
{
portal = PaymentPortal.Klarna;
}
}
}
Earlier we used to access and assign the values as below
Assigning Values
public class WorkflowParameters
{
public Site _site = default(Site);
public PaymentOptions paymentOptions = default(PaymentOptions);
}
var param = new WorkflowParameters()
{
paymentOptions = new KlarnaOptions()
{
delivery = DeliveryMethod.Billing,
payLater = true
}
};
Use in Methods would be as below
Checkout(param.paymentoptions); // Which would pass delivery and paylater values
Now we want to pass values from Excel Sheet
Mapper Method
public static class PropertyMapHelper
{
public static void Map(Type type, DataRow row, PropertyInfo prop, object entity)
{
List<string> columnNames = AttributeHelper.GetDataNames(type, prop.Name);
foreach (var columnName in columnNames)
{
if (!String.IsNullOrWhiteSpace(columnName) && row.Table.Columns.Contains(columnName))
{
var propertyValue = row[columnName];
if (propertyValue != DBNull.Value)
{
ParsePrimitive(prop, entity, row[columnName]);
break;
}
}
}
}
private static void ParsePrimitive(PropertyInfo prop, object entity, object value)
{
if (prop.PropertyType == typeof(string))
{
prop.SetValue(entity, value.ToString().Trim(), null);
}
else if (prop.PropertyType == typeof(bool) || prop.PropertyType == typeof(bool?))
{
if (value == null)
{
prop.SetValue(entity, null, null);
}
else
{
prop.SetValue(entity, ParseBoolean(value.ToString()), null);
}
}
else if (prop.PropertyType == typeof(long))
{
prop.SetValue(entity, long.Parse(value.ToString()), null);
}
else if (prop.PropertyType == typeof(int) || prop.PropertyType == typeof(int?))
{
if (value == null)
{
prop.SetValue(entity, null, null);
}
else
{
prop.SetValue(entity, int.Parse(value.ToString()), null);
}
}
else if (prop.PropertyType == typeof(decimal))
{
prop.SetValue(entity, decimal.Parse(value.ToString()), null);
}
else if (prop.PropertyType == typeof(double) || prop.PropertyType == typeof(double?))
{
double number;
bool isValid = double.TryParse(value.ToString(), out number);
if (isValid)
{
prop.SetValue(entity, double.Parse(value.ToString()), null);
}
}
else if (prop.PropertyType == typeof(DateTime) || prop.PropertyType == typeof(Nullable<DateTime>))
{
DateTime date;
bool isValid = DateTime.TryParse(value.ToString(), out date);
if (isValid)
{
prop.SetValue(entity, date, null);
}
else
{
isValid = DateTime.TryParseExact(value.ToString(), "MMddyyyy", new CultureInfo("en-US"), DateTimeStyles.AssumeLocal, out date);
if (isValid)
{
prop.SetValue(entity, date, null);
}
}
}
else if (prop.PropertyType.IsEnum)
{
var type = Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType;
var enumValue = Enum.Parse(type, value.ToString(), true);
prop.SetValue(entity, enumValue, null);
}
else if (prop.PropertyType == typeof(Guid))
{
Guid guid;
bool isValid = Guid.TryParse(value.ToString(), out guid);
if (isValid)
{
prop.SetValue(entity, guid, null);
}
else
{
isValid = Guid.TryParseExact(value.ToString(), "B", out guid);
if (isValid)
{
prop.SetValue(entity, guid, null);
}
}
}
else if(prop.PropertyType == typeof(PaymentOptions))
{
}
}
public static bool ParseBoolean(object value)
{
if (value == null || value == DBNull.Value) return false;
switch (value.ToString().ToLowerInvariant())
{
case "1":
case "y":
case "yes":
case "true":
return true;
case "0":
case "n":
case "no":
case "false":
default:
return false;
}
}
}
Test Data Model
namespace Framework.Model.Excel
{
public partial class TestDataModel
{
public TestDataModel() {
}
[DataNames("TestName")]
public string TestName { get; set; }
[DataNames("paymentOptions")]
public PaymentOptions paymentOptions { get; set; }
//public PaymentOptions paymentOptions = default(PaymentOptions);
[DataNames("SiteGroupId")]
public SiteGroup SiteGroupId { get; set; }
[DataNames("NickName")]
public string NickName { get; set; }
[DataNames("byCard")]
public bool byCard { get; set; }
[DataNames("payLater")]
public bool payLater { get; set; }
[DataNames("sliceIt")]
public bool sliceIt { get; set; }
[DataNames("portal")]
public PaymentPortal portal { get; set; }
[DataNames("delivery")]
public DeliveryMethod delivery{get;set;}
}
}
Currently, it is not mapping values of Payment Options, and also I do not know how will I access param.paymentoptions which will have delivery and payLater information
|
|
|
|
|
Before you start - do you have absolute control of the Excel format? If not you are in for a world of pain, you will need to locate the cell the values are in using interop or one of the excel libraries.
Never underestimate the power of human stupidity -
RAH
I'm old. I know stuff - JSOP
|
|
|
|
|
I am mapping excel values to test data model through mapper method, I am not using Interop
|
|
|
|
|
How are you getting the data from Excel?
Never underestimate the power of human stupidity -
RAH
I'm old. I know stuff - JSOP
|
|
|
|
|
 Here is the function:
public static IList<TestDataModel> GetAllTestData(string keyName)
{
DataSet ds = new DataSet();
DataNamesMapper<TestDataModel> mapper = new DataNamesMapper<TestDataModel>();
DataTable dataTableALL = new DataTable();
List<TestDataModel> testData = new List<TestDataModel>();
using (var connection = new
OdbcConnection(TestDataFileConnection()))
{
connection.Open();
OdbcCommand cmd = new OdbcCommand();
cmd.Connection = connection;
System.Data.DataTable dtSheet = null;
dtSheet = connection.GetSchema(OdbcMetaDataCollectionNames.Tables, null);
foreach (DataRow row in dtSheet.Rows)
{
string sheetName = row["TABLE_NAME"].ToString();
if (!sheetName.EndsWith("$"))
continue;
// Query each excel sheet.
var query = string.Format("select * from [{0}] where TestName = '{1}'", sheetName, keyName);
cmd.CommandText = query;
DataTable dt = new DataTable();
dt.TableName = sheetName;
OdbcDataAdapter da = new OdbcDataAdapter(cmd);
da.Fill(dt);
ds.Tables.Add(dt);
}
cmd = null;
connection.Close();
}
DataTable data= JoinExcelDatatoOneRow(ds);
testData = mapper.Map(data).ToList();
return testData.ToList();
}
|
|
|
|
|
So your data is coming in via ODBC which leaves you with a datatable. Make sure the source datatable has the same names as the destination object. Presumably excel does not have the payment options in the source so you need to load the default into the excel result set, simply return the default instead of the get/set property.
If Excel has the paymentoptions then it may be a complex type and will need specific processing to assign multiple values into your target object
Never underestimate the power of human stupidity -
RAH
I'm old. I know stuff - JSOP
|
|
|
|
|
So, if I browse to my site and login, I get a page with my personal images.
For example, I'm on-site with a client, take pictures, login and upload them. I want to retrieve them automatically from my laptop. To do so automatically, I need to login, get the HTML, grab the links then use WebClient.DownloadFile. Problem is, I can't see the HTML without a browser.
SO, more specifically - When I login with a browser and right click and "view HTML" I don't see the images in the markup, just the header and the footer HTML. The ONLY way to see the source is to use the inspector tool by right clicking on one of the images. So this code is generated by the browser, but not part of the original request.
I wrote a C# app that will log into my page and it works. I can login via SSL and my credentials, navigate pages BUT ... I can't see all the HTML.
I added a WebView2 form to my project (new Edge browser plugin for VS) and I can see the page, but I STILL can't get the source to get the path to the image.
So is there a way to get at this generated HTML programatically in C#? I can launch the dev tools (inspector) window with myWebView2Browser.CoreWebView2.OpenDevToolsWindow(); ... but I need a way to view the ELEMENTS tab in that tool where the true HTML is.
I tried myHTML = await myWebView2Browser.ExecuteScriptAsync("document.documentElement.outerHTML;"); ... and it gets the HTML, but NOT the FINAL rendered HTML that I can see in the inspector.
Hope this makes sense, it's making me crazy being so close! Thanks in advance!
|
|
|
|
|
Ask yourself - where are the images stored when I upload them?
They will either be in a database or the file system on the server. Instead of trying to use the HTML to get the file go directly to the source and download them from there, you should already have the credentials to access the server.
Never underestimate the power of human stupidity -
RAH
I'm old. I know stuff - JSOP
|
|
|
|
|
What you're looking for is called a headless browser, so google "c# headless browser". However I agree that if you can simply download the images directly that would be better.
|
|
|
|
|
While I was reading code produced by coworker of mine, I got confused.
What does
public void DoSomething(double someParameter)
{
_SomeComponent.Set(someParameter);
Task.Run(() => Check(someParameter)).Wait();
} actually do, i.e. how does it differ from
public void DoSomething(double someParameter)
{
_SomeComponent.Set(someParameter);
Check(someParameter);
} The Check function is synchronous. So, the two versions could be equivalent?
That Task.Run starts the Check function in a new Task . But because of the Wait , DoSomething won't be left immediately after starting that Task , but only when that Task ran to completion (or failure).
But I am too confused by that snippet now to be sure of my assumptions.
Oh sanctissimi Wilhelmus, Theodorus, et Fredericus!
|
|
|
|
|
Bernhard Hiller wrote: how does it differ from
Well, it uses more memory, and adds a thread to the system ...
But no, you are right. In essence starting a Task to call Check and then calling Wait is the same as calling the method directly: the calling method will not continue until Check is complete.
It's possible that the cow-orker doesn't know what he is doing, it's possible that the Check method was used elsewhere and called via a Task without the Wait, but that code caused problems when re-used. We don't know, and will probably never find out.
But it's pretty poor code in it's current form!
"I have no idea what I did, but I'm taking full credit for it." - ThisOldTony
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
Thanks.
When you say that the cow-orker did not know what he did, you may be right. Next to that code sits a TimeSpan.FromSeconds(SET_XY_MAX_WAIT_TIME_MS) (seconds vs. implied milliseconds ms)...
Or he just tries to obfuscate his code so that nobody else can deal with it - job security by obscurity!
Oh sanctissimi Wilhelmus, Theodorus, et Fredericus!
|
|
|
|
|
GO to QA - you'll meet a lot of that!
"I have no idea what I did, but I'm taking full credit for it." - ThisOldTony
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
Is it possible that there are other tasks / threads and the author is trying to allow them to have a turn?
|
|
|
|
|
Hi all, just revisiting some code that scans a local LAN for servers broadcasting on UDP port 3483 and I'm seeing strange characters in the response string when I run the code on Win 10
I expect
"ENAME0\LMSPi\JSON0\9000"
but I'm getting
"ENAME\u0005LMSPi0JSON\u00049000"
I'm sure the weren't there when I ran the code under Win 7
What I'm after is the port number the server is running on which is the 9000 after JSON ( without the u0004 )
Any idea why I'm seeing these characters ?
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace LMS
{
class ScanLAN
{
[STAThread]
static int Main()
{
ScanForServer();
return 0;
}
static void ScanForServer()
{
IPEndPoint ReceiveIP = null;
UdpClient listener = null;
Byte[] RequestData = null;
byte[] BytesReceived = null;
string RetVal = "No response from LAN";
int UDPPort = 3483;
try
{
Console.WriteLine($"Starting LAN broadcast on port {UDPPort}...");
ReceiveIP = new IPEndPoint(IPAddress.Any, UDPPort);
listener = new UdpClient(UDPPort);
RequestData = listener.Receive(ref ReceiveIP);
if (RequestData != null && RequestData.Length > 0)
RetVal = Encoding.UTF8.GetString(RequestData);
Console.WriteLine($"Initial response data {RetVal}");
listener.Send(RequestData, RequestData.Length, ReceiveIP);
BytesReceived = listener.Receive(ref ReceiveIP);
if (BytesReceived != null && BytesReceived.Length > 0)
RetVal = Encoding.UTF8.GetString(BytesReceived);
Console.WriteLine($"Final response data {RetVal}");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
finally
{
if (listener != null && listener.Client.Connected)
listener.Close();
}
}
}
}
"We can't stop here - this is bat country" - Hunter S Thompson - RIP
|
|
|
|
|
There seems to be an encoding problem.
The MSDN examples on UdpClient.Receive all use Encoding.ASCII.GetString, whereas your code uses Encoding.UTF8
Also when I run
string s1= "ENAME\u0005LMSPi0JSON\u00049000";
byte[] bytes=Encoding.UTF8.GetBytes(s1);
string s2 = Encoding.ASCII.GetString(bytes);
then s2 looks almost identical to what you expected.
If this would be the solution, then I can't explain why it worked well on Win7.
Beware, code working well does not imply code being correct...
|
|
|
|
|
Thanks Luc I'll give it a shot
"We can't stop here - this is bat country" - Hunter S Thompson - RIP
|
|
|
|
|
Hi Luc, I tried it with ASCII and no different I'll show you the entire string this time ( I abridged it in the original post )
"ENAME\u0005LMSPiJSON\u00049000UUID$0b5a576d-a276-4920-a9e8-134c41133102VERS\u00057.9.2"
Breaking it down
ENAME = LMSPi -- my server hostname
JSON = 9000 -- my server port
UUID = self explanatory
VERS = software version
If I print the byte array one byte at a time and convert it to a char I get
for (int i = 0; i < BytesReceived.Length; ++i)
{
char x = (char)BytesReceived[i];
Console.Write(x);
}
ENAMELMSPiJSON9000UUID$0b5a576d-a276-4920-a9e8-134c41133102VERS7.9.2
which is more like what I expect
Thanks very much for your help
"We can't stop here - this is bat country" - Hunter S Thompson - RIP
modified 5 days ago.
|
|
|
|
|
So it is a series of key-value pairs, where each value (except the GUID that has a fixed length) would need either a length or a separator; in this case a length is used, i.e. the 6 characters "\u000X" stand for a the length of X for the value that follows; normally a small length could be stored in a single byte.
As you receive the lengths in this escape format no matter what encoding is used to receive them (so you told me), it suggests something might be wrong at the transmitter, not the receiver.
ALERT
Wait a minute, where are you seeing those unexpected strings exactly?
You are aware Visual Studio sometimes uses escapes, quotes, etc in its immediate window and maybe elsewhere too. Use Console.WriteLine to make sure, and/or apply string.Length and count the characters manually to compare!!!
modified yesterday.
|
|
|
|
|
Hi Luc it was VS misleading me here is what I ended up doing to parse the values
Response from server - there are non printable characters in the response which I can't get to display here which contain the data lengths
ENAMELMSPiJSON9000UUID$0b5a576d-a276-4920-a9e8-134c41133102VERS7.9.2;
ResponseColumn ParseResponseString(string FieldName,string ResponseString)
{
ResponseColumn rc = new ResponseColumn();
int pos = ResponseString.IndexOf(FieldName);
string ColumnName = ResponseString.Substring(pos,5);
int DataLen = Convert.ToInt32(ColumnName[4]);
pos+= 5; // this moves the pointer along the string to the value part
string ColumnValue = ResponseString.Substring(pos,DataLen); // this reads the string starting at pos the length of DataLen
rc.ColumnName = FieldName;
rc.ColumnValue = ColumnValue;
return rc;
}
public class ResponseColumn
{
public string ColumnName {get;set;}
public string ColumnValue{get;set;}
}
and use it thus
ResponseColumn rcPortNumber = ParseResponseString("JSON",ResponseString); // Gets me the port number.
ResponseColumn rcServerName = ParseResponseString("NAME",ResponseString); // Gets me the server name.
ResponseColumn rcServerVer = ParseResponseString("VERS",ResponseString); // Gets me the server version.
Thanks for all your help
"We can't stop here - this is bat country" - Hunter S Thompson - RIP
modified 4 days ago.
|
|
|
|
|
You're welcome.
Two comments though:
1. you completely replaced the content of this message; I got an e-mail when you first created it. And obviously not when you replaced all of it, so it took a while before I accidentally noticed the new content. You should not replace/delete messages on CodeProject, you can create new ones, or add to or slightly edit existing ones.
2. your code is fine with the current data. It has a few weaknesses in general: it assumes field names are always 4 chars (see constants 4 and 5), and it locates field names by a string search, which could go wrong when one field name happens to appear as (part of) another field's value.
The universal way to handle this would be to scan the entire line and build a Dictionary<string,string> holding all the key-value pairs in the line; that too would rely on some restrictions on field names and/or values (e.g. field names are uppercase letters only, and field value lengths must be less than 'A').
Cheers
modified yesterday.
|
|
|
|
|
Hi Luc, just some answers to your observations
it assumes field names are always 4 chars
they have been 4 chars for the last 25 years
and it locates field names by a string search,
How else could it be done ?
which could go wrong when one field name happens to appear as (part of) another field's value.
The fields always end with a data length
Thanks again
"We can't stop here - this is bat country" - Hunter S Thompson - RIP
modified yesterday.
|
|
|
|
|
Let's assume your server was renamed ZVERSA (or MYJSONMACHINE) and you start getting the string
ENAMEZVERSAJSON9000UUID$0b5a576d-a276-VERS-a9e8-134c41133102VERS7.9.2
^^^^
And now you try to get the version (or the json) field...
|
|
|
|
|