I'm using selenium to automate some data entry tasks for work.
Problem I'm having:
I need to check if a select (dropdown) has an option, and if so select it, otherwise continue.
The select has around 200 options which I didn't think was a lot but it seems to take a long time to go through the list and I'm wondering how I can speed it up.
I'm assuming its related to the type of objects the list is comprised of being beefy? (list of IWebElement)
Tried a basic for loop, as well as using the .Any method on the list, both seem equally unusually slow.
SelectElement brokeragePayeeOfficeSelect = new SelectElement(webDriver.FindElement(By.Id("ContentPlaceHolder1_ddBrokeragePayee")));
IList<IWebElement> officeOptions = brokeragePayeeOfficeSelect.Options;
bool result = officeOptions.Any(o => o.Text == brokerageOfficeArray[i]);
if (result)
{
brokeragePayeeOfficeSelect.SelectByText(brokerageOfficeArray[i]);
}
else
{
Console.WriteLine("Missing:" + brokerageOfficeArray[i]);
continue;
}
The other option is to make the SelectElement.SelectByText method throw its exception faster. Not sure how to do that. Usually takes a minute or so for it to throw.
Ideas?
Your iteration with Any to check if the option is present is rather expensive. Instead try to select the option and catch the exception:
var brokeragePayeeOfficeSelect = new SelectElement(webDriver.FindElement(By.Id("ContentPlaceHolder1_ddBrokeragePayee")));
try {
brokeragePayeeOfficeSelect.SelectByText(brokerageOfficeArray[i]);
} catch (NoSuchElementException) {
Console.WriteLine("Missing:" + brokerageOfficeArray[i]);
continue;
}
Related
I have a section inside a method that does something similar too:
do
{
// some query
// another query
if another query != null
{
// return something
}
}while(some query != null)
Obviously this does not work, because some query is not declared until it is inside the loop. One solution I tried was
bool flag = false;
do
{
// some query
if some query == null
{
flag = true;
// or
break;
}
// another query
if another query != null
{
// return something
}
}while(flag != true)
Neither method really satisfies me, and quite honestly I am not sure if they would be considered good coding practice, which irks me. Moreover this has pretty much has been my go to solution in cases like this up until this point, but due to the garbage nature of the flag, I wanted to find out if there is a better way to handle this for future reference, instead of making a junk variable. I should note the other solution which I thought of would arguably be uglier. That solution being to run the query once outside the loop, and convert it into a while loop and recall the query again inside itself, rather than a do loop.
While the code works with the above solution, I was wondering if anyone had a better solution that does not require an arguably pointless variable.
Though I understand that such a better solution may not be possible, or really even needed, it could be ridiculous to even try.
Having a break or flag variable isn't what would make something inefficient, it's what inside the loop that should be your concern. In other words it's just a preference and either is fine.
I think you need
while(true)
{
// some query
if some query == null
{
break;
}
// another query
if another query != null
{
// return something
}}
You can try this:
do
{
// some query
if some query == null
{
break;
}
// another query
if another query != null
{
// return something
}
}while(true);
if anyone can explain to me what is wrong with this I would really appreciate it because its not throwing an exception so I can't see why its not updating the record.
Sorry I should of said I am trying to update an existing record in a database, "CompName" was used as the primary key, this isn't my database design or application or I would of used an int as a id and coded things a lot differently.
using (CompanyAndContactDataContext dc = new CompanyAndContactDataContext())
{
try {
Company c = (from datavalue in dc.Companies
where datavalue.CompanyName == CompName
select datavalue).First();
c.CompanyName = txtEditCompanyName.Text;
c.CompanyEmailAddress = txtEditCompanyEmail.Text;
c.CompanyTelephoneNumber = txtEditCompanyTelephone.Text;
c.CompanyFaxNumber = txtEditCompanyFax.Text;
c.CompanyAddress1 = txtEditAddress1.Text;
c.CompanyAddress2 = txtEditAddress2.Text;
c.CompanyAddress3 = txtEditAddress3.Text;
c.CompanyAddress4 = txtEditAddress4.Text;
c.PostCode = txtEditPostcode.Text;
dc.SubmitChanges();
} catch (Exception exep) {
}
}
At first guess, it's probably the First() call that's throwing the exception. You could make it FirstOrDefault, or try to pass an Id to your method (Make them select a value from a dropdown or auto complete, then get the Id of what they selected). That way you can be sure when this code hits you can update the entry.
If you pass an Id to the method you can even use "Single" or "SingleOrDefault"
Edit:
Say you enter "MyCompany" as CompName, and this doesn't exist in the Database table. If you call First() it'll throw an exception. If you call FirstOrDefault() it'll return "c" as null.
If you call "c.CompanyName" or anything else with a dot, it'll return an exception.
You need to check if c is null before calling any dot methods.
I'm querying the Google Books API, and I'm parsing the books into a custom object like this:
foreach (JToken item in items)
{
try
{
FoundBookViewModel viewModel = new FoundBookViewModel
{
Title = item.SelectToken("volumeInfo.title").ToString(),
Isbn13 = item.SelectToken("volumeInfo.industryIdentifiers[1].identifier").ToString(),
Authors = item.SelectToken("volumeInfo.authors").Select(x => x.ToString()).ToList().Aggregate((i, j) => i + ", " + j),
Pages = item.SelectToken("volumeInfo.pageCount").ToString(),
ImageUri = item.SelectToken("volumeInfo.imageLinks.smallThumbnail").ToString()
};
newList.Add(viewModel);
}
catch (Exception)
{
newList.Add(new FoundBookViewModel());
}
}
However, sometimes not all data is available. Sometimes there is no 'pageCount', sometimes there is no 'ISBN13', etc. In those cases an exception is thrown at the ToString() part.
So what I want is this: when an exception is thrown for one of the properties, I just want it to be an empty string. But I don't know a clean way to accomplish this.
I tried multiple things:
I wrapped the whole thing inside a try catch, but then I don't know what property was empty, so I can't 'fill' it with an empty string.
I tried using safe casting ('as string') but that operation is not available on SelectToken().
Couple of other things that didn't work anyway.
Of course I could put every operation inside its own try catch, but that will result in 5 try catch blocks in this situation, so that's not what I'm looking for.
Who knows a clean way to solve this? Btw. I choose to use LINQ to JSON instead of materializing it directly because the Google Books API doesn't neatly map to simple entities (the JSON contains lots of nesting).
If you don't want to throw exception - don't...
I.e. you can change item.SelectToken("volumeInfo.pageCount").ToString() into:
item.SelectToken("volumeInfo.pageCount") == null?
String.Empty : item.SelectToken("volumeInfo.pageCount").ToString()
this is somewhat tricky to figure out I think, perhaps I am missing something.
I am a newbie trying to rig a database mapped via Linq-to-SQL to my server. There is a function called by clients which retrieves UserAccount from the database:
public static explicit operator Dictionary<byte, object>(UserAccount a)
{
Dictionary<byte, object> d = new Dictionary<byte, object>();
d.Add(0, a.AuthenticationDatas.Username);
int charCount = a.Characters.Count;
for (int i = 0; i < charCount; i++)
{
d.Add((byte)(i + 1), (Dictionary<byte, object>)a.Characters[i]);
}
return d;
}
What this actually does is convert a UserAccount type to my server datatype of Dictionary. UserAccount itself is retrieved from database then converted via this function.
However when I run this function, I get InvalidCastException on line:
int charCount = a.Characters.Count;
Moreover, when VS breakpoints # this line, I can wait a few seconds and proceed and the excpetion will be gone! It retrieves Characters.Count correctly after that.
Here is my Characters mapping:
[global::System.Data.Linq.Mapping.AssociationAttribute(Name="UserAccount_Character", Storage="_CharactersTBs", ThisKey="UID", OtherKey="UID")]
public EntitySet<Character> Characters
{
get
{
return this._Characters;
}
set
{
this._Characters.Assign(value);
}
}
I believe whats happening is that request is somehow executed on another thread then the one that interacts with database, and it errors out before database can actually retrieve Characters table. I am not quite sure...
Does anyone know what the problem might be and how can I syncronize it (without adding some gimp delay)?
EDIT:
Ok I narrowed down the problem. It has nothing to do with different threads networking or what not... Its just me being stupid. Here is a simple databse query which throws InvalidCastException # line int count = UA.Characters.Count;
static void Main(string[] args)
{
IEnumerable<UserAccount> query = from p in PBZGdb.Instance.AuthenticationDatas
where p.Username == "Misha" && p.Password == "123"
select p.UserAccount;
UserAccount UA = query.ElementAt(0);
int count = UA.Characters.Count;
Console.WriteLine(count);
Console.ReadKey();
}
(p.s.) UA is NOT null it indeed finds a correct instance of userAccount and it has 2 Characters. If I wait few seconds and try again exception goes away..
What am I doing wrong? This is the first time I really use a database in VS please help! :)
It looks like you are running in to a problem with the deferred execution of the EntitySet. A simple way to check this and potentially work around it will be to try calling the .Count() method, instead of accessing the .Count property.
You could have a look in the debugger as soon as you hit that line, and look at the value of a.Characters.IsDeferred also.
edit
Another thing you could try would be to force execution of the query by implicitly calling it's .GetEnumerator() (and associated .MoveNext()) by replacing your loop with a foreach:
int i = 0;
foreach (var character in a.Characters)
{
d.Add( /* ... */ );
++i;
}
double edit
removed commentary about
d.Add((byte)(i + 1), (Dictionary<byte, object>)a.Characters[i]);
after clarification in the comments below
Hey just want anyone having the same problem know, I figured it out. What happened was I manualy renamed LINQ .dbml file when I added it to my project after it was geneerated by sqlmetal. And of course I did it inconsistently (it was renamed in designer but not in its .cs file). I just re-generated a new .dbml file with sqlmetal with a correct name this time and everything works like butter!
Thanks guys!
I have a MS SQL table that I don't have any control over and I need to write to. This table has a int primary key that isn't automatically incremented. I can't use stored procs and I would like to use Linq to SQL since it makes other processing very easy.
My current solution is to read the last value, increment it, try to use it, if I get a clash, increment it again and retry.
Something along these lines:
var newEntity = new Log()
{
ID = dc.Logs.Max(l => l.ID) + 1,
Note = "Test"
};
dc.Logs.InsertOnSubmit(newEntity);
const int maxRetries = 10;
int retries = 0;
bool success = false;
while (!success && retries < maxRetries)
{
try
{
dc.SubmitChanges();
success = true;
}
catch (SqlException)
{
retries++;
newEntity.ID = dc.Logs.Max(l => l.ID);
}
}
if (retries >= maxRetries)
{
throw new Exception("Bummer...");
}
Does anyone have a better solution?
EDIT: Thanks to Jon, I simplified the max ID calculation. I was still in SQL thinking mode.
That looks like an expensive way to get the maximum ID. Have you already tried
var maxId = dc.Logs.Max(s => s.ID);
? Maybe it doesn't work for some reason, but I really hope it does...
(Admittedly it's more than possible that SQL Server optimises this appropriately.)
Other than that, it looks okay (smelly, but necessarily so) to me - but I'm not an expert on the matter...
You didn't indicate whether your app is the only one inserting into the table. If it is, then I'd fetch the max value once right after the start of the app/webapp and use Interlocked.Increment on it every time you need next ID (or simple addition if possible race conditions can be ruled out).
You could put the entire operation in a transaction, using a TransactionScope class, like below:
using (TransactionScope scope = new TransactionScope()){
var maxId = dc.Logs.Max(s => s.ID);
var newEntity = new Log(){
ID = maxId,
Note = "Test"
};
dc.Logs.InsertOnSubmit(newEntity);
dc.SubmitChanges();
scope.Complete();
}
By putting both the retrieval of the maximum ID and the insertion of the new records within the same transaction, you should be able to pull off an insert without having to retry in your manner.
One problem you might face with this method will be transaction deadlocks, especially if the table is heavily used. Do test it out to see if you require additional error-handling.
P.S. I included Jon Skeet's code to get the max ID in my code, because I'm pretty sure it will work correctly. :)
Make the id field auto incrementing and let the server handle id generation.
Otherwise, you will run into the problem liggett78 said. Nothing prevents another thread from reading the same id in between the reading and submitting of max id for this thread.