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;
}
Related
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;
}
}
}
Having model with property type of object.
Model:
public class Employee
{
private object _BirthDate;
public object BirthDate
{
get { return this._BirthDate; }
set
{
this._BirthDate = value;
this.RaisePropertyChanged("BirthDate");
}
}
}
View Model:
Populating data for BirthDate like in below.
if (i % 5 == 0)
{
emp.BirthDate = "ABC";
// emp.Name = "NUll";
}
else if (i % 4 == 0)
{
emp.BirthDate = null;
// emp.Name = "DBNull";
}
else
emp.BirthDate = new DateTime(1 / 1 / 2013);
Having 100 birthdates. i am try to sort the data using LINQ query with LAMDA exressions.
private void GetSortedQuerableCollection_Click(object sender, RoutedEventArgs e)
{
var sourceType = new Employee().GetType();
var source = (this.datagrid.ItemsSource as IEnumerable).AsQueryable() as IQueryable;
var paramExpression = Expression.Parameter(source.ElementType, sourceType.Name);
var lambda = GetLambdaWithComplexPropertyNullCheck(source, "BirthDate", paramExpression, sourceType);
var data = OrderBy(source, "BirthDate", sourceType);
}
In data, having the results with "Object Must be a type of String".
Note: GetLambdaWithComplexPropertyNullCheck is the method having lots of code. if i include that also, its look inconsistency , and as per StackOverFlow norm its deletes the page. but it just a expression to calculate the sorting.
Let me have any idea to resolve this issue ?
If you have to mix dates, null, and non-dates, then use string:
public class Employee
{
private string = null;
public string BirthDate
{
get { return this._BirthDate; }
set
{
this._BirthDate = value;
this.RaisePropertyChanged("BirthDate");
}
}
}
and then:
emp.BirthDate = new DateTime(2013, 1 , 21).ToString("yyyy'-'MM'-'dd");
This is sortable, and you can easily parse BirthDate to a DateTime value:
DateTime trueDate = DateTime.Parse(emp.BirthDate);
My question is that is it possible to create a list that sorts the objects in it upon these object being placed in them?
After not getting anywhere, I made a new linked list. The only task is to make this list ordered by the string field of the objects it will containt while remaining foreachable.
I have the following code:
class LancoltLista<T> : IEnumerable
{
class ListaElem
{
public T tartalom;
public ListaElem kovetkezo;
}
ListaElem fej;
public void ElejereBeszuras(T elem)
{
ListaElem uj = new ListaElem();
uj.tartalom = elem;
uj.kovetkezo = fej;
fej = uj;
}
public void VegereBeszuras(T elem)
{
if (fej == null)
{
ElejereBeszuras(elem);
}
else
{
ListaElem e = fej;
while (e.kovetkezo != null)
{
e = e.kovetkezo;
}
ListaElem uj = new ListaElem();
uj.tartalom = elem;
e.kovetkezo = uj;
}
}
public IEnumerator GetEnumerator()
{
return new ListaBejaro(fej);
}
class ListaBejaro : IEnumerator<T>
{
ListaElem elso, jelenlegi;
public ListaBejaro(ListaElem elso)
{
this.elso = elso;
jelenlegi = null;
}
public bool MoveNext()
{
if (jelenlegi == null)
{
jelenlegi = elso;
}
else
{
jelenlegi = jelenlegi.kovetkezo;
}
return jelenlegi != null;
}
public void Reset()
{
jelenlegi = null;
}
object IEnumerator.Current
{
get { return this.jelenlegi.tartalom; }
}
public T Current
{
get { return this.jelenlegi.tartalom; }
}
public void Dispose()
{
elso = null;
jelenlegi = null;
}
}
}
The problem here is that I'm not able to compare p.kulcs and kulcs.
For real world applications you could use the built-in SortedList<T>.
For your homework, you will have to check every item that you get in your add method against the entire list and insert it into the correct place: between the last element that it's grater than or equal to, and the first element that it's smaller then.
Of course, if the list is empty, or if there is no element greater than the one you add, then you simply append the element to the last available location.
Since this is homework, I'll leave you to write the code yourself.
I have two drop down lists. Niether of them have a relation ship with each other. But I need to filter one drop down list based on the chosen value of another drop down list.
I can filter it in code. When I debug I can see the filtered results on the property. However when I run the app, it does not work. Here is my code so far:
private BindingList<Commodity> _AllocationCommodities;
[Browsable(false)]
public BindingList<Commodity> AllocationCommodities
{
get
{
if (_AllocationCommodities == null)
{
_AllocationCommodities = new BindingList<Commodity>();
ChangeCommodities();
}
return _AllocationCommodities;
}
}
private SourceEntity _SourceEntity;
[ImmediatePostData]
[Association("SourceEntity-LimitAllocations")]
[RuleRequiredField("RuleRequiredField_LimitAllocation_SourceEntity", DefaultContexts.Save)]
public SourceEntity SourceEntity
{
get
{
return _SourceEntity;
}
set
{
//New Code
if (SetPropertyValue<SourceEntity>("SourceEntity", value))
{
if (IsSaving || IsLoading) return;
ChangeCommodities();
}
}
}
private Commodity _Commodity;// This is the drop down to be filtered
[ImmediatePostData]
[DataSourceProperty("AllocationCommodities")] //// This Attribute Should filter Commodities
[RuleRequiredField("RuleRequiredField_LimitAllocation_Commodity", DefaultContexts.Save)]
public Commodity Commodity
{
get
{
return _Commodity;
}
set
{
SetPropertyValue("Commodity", ref _Commodity, value);
if (Commodity.Oid != Guid.Empty)
AllocationVolumeUnits.Reload();
}
}
private void ChangeCommodities()
{
if (!this.IsLoading && _SourceEntity != null)
{
_AllocationCommodities.RaiseListChangedEvents = false;
_AllocationCommodities.Clear();
OperandValue[] _params;
System.Collections.Generic.List<CMSBOD.SourceCommodity> _sc = new System.Collections.Generic.List<SourceCommodity>();
BindingList<Commodity> _Commodities = new BindingList<Commodity>();
foreach (SourceCommodityEntity _tempSCE in _SourceEntity.SourceCommodityEntities)
{
if (_tempSCE.SourceCommodity != null)
_sc.Add(_tempSCE.SourceCommodity);
}
foreach (SourceCommodity _tempSC in _sc)
{
if (_tempSC.Commodity != null && !_Commodities.Contains<Commodity>(_tempSC.Commodity) && _tempSC.Commodity.IsActive)
_Commodities.Add(_tempSC.Commodity);
}
_AllocationCommodities.RaiseListChangedEvents = true;
_AllocationCommodities = _Commodities;///This is where I can see the filtered list when debugging.
}
}
You can find a DataSourceCriteria useful in this scenario, instead of DataSourceProperty.
Assuming you have collection properties that associates Commodity back to SourceCommodityEntity, you can use this criteria:
[DataSourceCriteria("IsActive And SourceCommodities[SourceCommodityEntities[SourceEntity = '#SourceEntity'] ]")]
Even if its designed to be a 1x1 assocation, you can find that associations can be useful for filtering purposes.
Here's my property that determines if I should bind the other properties or not:
public string LotNumber {
get {
return lotNumber;
}
set {
using (var db = new DDataContext()) {
lotNumber = value;
// Check for duplicates
bool isDuplicate =
db.LotInformation.Any(r => r.lot_number == lotNumber);
if (isDuplicate == true) {
ComponentsList = null;
FamiliesList = null;
ExpirationDate = null;
LotNumber = null;
lotNumber = null;
// Inform user that the lot_number already exists
errorWindow.Message =
LanguageResources.Resource.Lot_Exists_Already;
dialogService.ShowDialog(
LanguageResources.Resource.Error, errorWindow);
logger.writeErrLog(
LanguageResources.Resource.Lot_Exists_Already);
return;
} else {
lotNumber = value;
}
RaisePropertyChanged("LotNumber");
}
}
}
My problem right now is if I upload a file and if the lot number already exists in the database, the boolean returns true and an error message is thrown. However, after that,it loops again and then the boolean is set to false since now the value is null and it still binds the data afterward. How can I break out of the loop and just make it stop running/clear/prevent binding when bool is true in the case above?
I assume you have some code like this:
LotNumber = "ABC5"; // ABC5 already exists in the database - uh oh!
And then you're trying to figure everything out in the "setter". It's already too late by that point. Instead, move your logic into separate methods:
private bool LotNumberExists(string lotNumber)
{
using (var db = new DDataContext())
{
return db.LotInformation.Any(r => r.lot_number == lotNumber);
}
}
private void ClearFields()
{
ComponentsList = null;
FamiliesList = null;
ExpirationDate = null;
LotNumber = null;
}
private void InformUserOfDuplicate()
{
// Inform user that the lot_number already exists
errorWindow.Message = LanguageResources.Resource.Lot_Exists_Already;
dialogService.ShowDialog(LanguageResources.Resource.Error, errorWindow);
logger.writeErrLog(LanguageResources.Resource.Lot_Exists_Already);
}
Then check the return value of that method before setting LotNumber.
private void SomeOtherMethod()
{
string someLotNumber = "ABC5";
if (LotNumberExists(someLotNumber)
{
ClearFields();
InformUserOfDuplicate();
return;
}
LotNumber = someLotNumber;
}
Turn your setter back into a simple setter without a ton of logic wrapped up in it:
public string LotNumber
{
get { return lotNumber; }
set
{
lotNumber = value;
RaisePropertyChanged("LotNumber");
}
}