Entity framework use between with strings - c#

I realy stucked with my function. I have table and I'm using EF. Representation for this table in ORM is:
public partial class ProductAttributes
{
public long Id { get; set; }
public System.Guid AttrId { get; set; }
public System.Guid ProdId { get; set; }
public string Value { get; set; }
public virtual Attributes Attributes { get; set; }
public virtual Products Products { get; set; }
}
This table contains FK to products and Attributes. Main responsibility of this table is to keep value for specified product and attribute. As you can see value represented as string.
Users on web site using sliders sets range for values. Now I should get all products from DB where values between users choice. Also a choose can be not range, just single value represented as a string. And here I got stuck.
Here function for selecting values from DB:
// Dictionaries Guid - attributeID , string[] - values from slider
public IEnumerable<Products> GetFilteredProducts(Dictionary<Guid, string[]> searchParam)
{
List<Products> list = new List<Products>();
try {
entityContext = new SiteDBEntities();
IQueryable<Products> query = entityContext.Products;
foreach (var item in searchParam)
{
//check do we have single value, it's mean that we whould search equals value
if (item.Value.Length == 1)
{
var r = item.Value[0];
query = query.Where(x => x.ProductAttributes
.Any(y => y.AttrId == item.Key && y.Value.Equals(r)));
}
else
{
double a = Double.Parse(item.Value[0]); // min value
double b = Double.Parse(item.Value[1]); // max value
//AND HERE BECOMES BIG PROBLEMS
// this code will throw error becuase ToDouble cannot be converted to T-SQl
// query = query.Where(x => x.ProductAttributes
// .Any(y => y.AttrId == item.Key
// && Convert.ToDouble(y.Value) > a
// && Convert.ToDouble(y.Value) < b));
//this will return error cannot conver string to double
// query = query.Where(x => x.ProductAttributes
// .Any(y => y.AttrId == item.Key
// && (double)y.Value > a
// && (double)y.Value < b));
// this will return error 'cannot apply ">" to IEnumerable<double>'
// query = query.Where(x => x.ProductAttributes.
// Any(y => y.AttrId == item.Key
// && y.Value.Cast<double>() > a
// && y.Value.Cast<double>() < b));
// }
// this monster that I found on stackoverflow will return an error 'a lambda expression with body cannot be converted to expression tree'
// query = query.Where(x => x.ProductAttributes.Any(p =>
// {
// double val = 0;
// if (double.TryParse(p.Value, out val))
// {
// return p.AttrId == item.Key &&
// val >= a &&
// val <= b;
// }
// else
// {
// return false;
// }
// });
}
}
catch(Exception){}
}
Maybe someone alredy faced with this issue and can help me? Here my slider:

Related

Get Range on nested collection

I have method that gets data from Db with nested collection
Here is model
public class FilterOptionDto
{
public Guid Id { get; set; }
public string FilterName { get; set; }
public ICollection<OptionDto> Options { get; set; }
}
Here is method that get's data
public async Task<List<FilterOptionDto>?> SetFilterOptions(SetFilterOptionsInputDto input)
{
var useCase = await _dbContext.UseCases.FirstOrDefaultAsync(x => x.Id == input.UseCaseId);
var dimensionsConfiguration =
_analysisDimensionsProvider.AnalysisDimensionsSetting.FirstOrDefault(x => x.FieldNameDomain == input.Name);
if (dimensionsConfiguration != null)
{
var filters = _mapper.Map<List<FilterOptionDto>>(await _dbContext.VwAnalysisUseCaseFilters.Where(x =>
x.JobId == useCase!.JobId && x.IsUseCaseFilter == true && x.FilterType == "multiselect" &&
x.IsAvailable && x.FieldNameDomain == input.Name)
.ToListAsync());
foreach (FilterOptionDto item in filters)
{
item.Options = await GetOptions(useCase?.JobId, dimensionsConfiguration.Source,
dimensionsConfiguration.FieldNameDomain);
}
if (!string.IsNullOrEmpty(input.SearchInput))
{
filters = filters.Where(x => x.Options.Any(x => x.Value.Contains(input.SearchInput))).ToList();
}
// filters = filters.Where(x => x.FilterName.Contains(input.Name)).ToList()
// .GetRange(input.Offset, input.Offset + 3);
return filters;
}
return null;
}
on this line I need to get range on nested collection Options from model, how I can do this?
// filters = filters.********
// .GetRange(input.Offset, input.Offset + 3);
You can chain Select, Skip, and Take.
filters.Select(x => x.Options.Skip(input.Offset).Take(3));

filtering data by excluding particular data based on condition

I want to check the record in list based on trackId and InUse property because I want the unique data which has Inuse property = false but I want to exclude the record of Track="S" how do I exlude that so my condition does not check the record that has Track="S" because SidList also contains the record with track="S". Below is my condition
if (SidList.Any(q => q.TrackId == item.TrackId && q.InUse == true))
{
//Not unique logic goes here
}
I tried this condtion but it does not work
if (SidList.Any(q => q.TrackId == item.TrackId && q.InUse == true && !q.Track.Contains('S')))
{
}
string.Contains is case sensitive.If Track has data with lower case s,the statement !q.Track.Contains('S') will return true.
If you want to ignore the case,change like below:
!q.Track.Contains('S', StringComparison.InvariantCultureIgnoreCase)
Here is the whole working demo:
Model:
public class TrackModel
{
public int TrackId { get; set; }
public bool InUse { get; set; }
public string Track { get; set; }
}
Controller:
var SidList = new List<TrackModel>() {
new TrackModel(){TrackId=1,InUse=false,Track="dfdfds"}
};
if (SidList.Any(q => q.TrackId == 1 && q.InUse == false&& !q.Track.Contains('S', StringComparison.InvariantCultureIgnoreCase)))
{
//...
}
Result:
var filteredList = SidList.Where(q => q.TrackId == item.TrackId && q.InUse && !q.Track.Contains('S'))
foreach ( var filteredElement in filteredList)
{
// not unique action goes here
}

LINQ searching a List that contains a Class with objects

I'm new at C#, I know how to do a LINQ search to a List with one field/type, but not with many types of an object. I created a List
List<Reader> results = new List<Reader>();
That contain this class:
public class Reader
{
public int ID { get; set; }
public string Name { get; set; }
public string Course { get; set; }
public int Grade { get; set; }
public Reader(int id, string name, string course, int grade)
{
ID = id;
Name = name;
Course = course;
Grade = grade;
}
}
I want to search it with LINQ and match the ID and Name of a user that entered the site.
If this two fields are the same I want to take from the List the users Course and Grade.
Any suggestion how to do it ?
A simple Where for condition(s) and Select for representation should do:
List<Reader> results = ...
var data = results
.Where(item => item.ID == userID && item.Name == userName)
// .OrderBy(item => item.Course) // uncomment if you want to order by course
.Select(item => $"Course: {item.Course} Grade: {item.Grade}");
foreach (var record in data)
Console.WriteLine(record);
First, let's assume that you have two variables that hold the values introduced by the user. Those variables are userName of type string and id of type integer. If you just want a variable that holds the course and the Grade you could select a new anonymous type and do the query like this:
var values= results
.Where(item => item.ID == userID && item.Name == userName)
.Select(item => new { Course = item.Course, Grade = item.Grade });
then you could use the values like:
values.Grades
values.Course
var Selecteduser = results.Where(x => x.Name == selectedname && x.ID == ID).ToList();
if (Selecteduser.Count != 0)
{
//found match ..(Selecteduser[0])
string selectedcourse = Selecteduser[0].Course;
int selectedgrade = Selecteduser[0].Grade;
}
else
{
//coudlnt find match
}

Best Practice to find best matching instance in a List C#

For sure very simple question for most of you.
But I am struggling with a solution at the moment.
Imagine you have a list of cats (List) where each cat has a list of babys (Kitten)
public class Cat
{
public string Name { get; set; }
public int Age { get; set; }
public string Race { get; set; }
public bool Gender { get; set; }
public List<Kitten> Babys { get; set; }
}
public class Kitten
{
public string Name { get; set; }
public double Age { get; set; }
public bool Gender { get; set; }
}
now I want to find the Cat that has the most matches for given requirements. It could easily be the case that a cat matches only 2 of 3 requirements. I simple want to find the cat that has the most matches to my requirements.
where my requirements could be:
Name has to be "Micky"
Age is 42
Has a Kitten named "Mini"
My actual solution would be to compare all properties and take the one with the highest count of matching properties. But this is not generic and I am sure there are mutch better ways to do it.
Thanks in advance
Well, I have no opportunity to test this solution, but you can try this:
Assume that you have a list of cats:
var cats = new List<Cat>();
Now you have defined what are your criteria:
var desiredName = "Micky";
var desiredAge = 42;
var desiredKitten = "Mini";
And then you have to get your desired cat:
var desiredCat = cats
.Select(c => new {
Rating =
Convert.ToInt32(c.Age == desiredAge) + // Here you check first criteria
Convert.ToInt32(c.Name == desiredName) + // Check second
Convert.ToInt32(c.Babys.Count(b => b.Name == desiredKitten) > 0), // And the third one
c })
.OrderByDescending(obj => obj.Rating) // Here you order them by number of matching criteria
.Select(obj => obj.c) // Then you select only cats from your custom object
.First(); // And get the first of them
Please check if this works for you.
And if you need more specific answer or some edits for me to add.
If you really will compare 2 ou 3 requirements you can simplify using Linq by:
// try to find with 3 requirements
var foundCats = catList.Where(t => t.Name == desiredName &&
t.Age == desiredAge &&
t.Babys.Any(k => k.Name == desiredKitten)
).ToList();
if (foundCats.Any())
{
// you found the desired cat (or cats)
return foundCats;
}
// try to find with 2 requirements
foundCats = catList.Where(t =>
(t.Name == desiredName && t.Age == desiredAge) ||
(t.Name == desiredName && t.Babys.Any(k => k.Name == desiredKitten)) ||
(t.Age == desiredAge && t.Babys.Any(k => k.Name == desiredKitten)
).ToList();
if (foundCats.Any())
{
// you found the desired cat (or cats)
return foundCats;
}
// try to find with only 1 requirement
foundCats = catList.Where(t => t.Name == desiredName ||
t.Age == desiredAge ||
t.Babys.Any(k => k.Name == desiredKitten)
).ToList();
return foundCats;
So, I see that the problem is you don't know if in any near future you will have more properties, so I will suggest going to the hardway and make reflection, the following is ugly af but you can probably (you will) make it better and hopefully serves you well as guiadance:
public static List<Cat> CheckProperties(List<Cat> inCatList, Cat inQueryCat)
{
Dictionary<Cat, List<PropertyInfo>> dict = new Dictionary<Cat, List<PropertyInfo>>();
foreach (PropertyInfo pI in inQueryCat.GetType().GetProperties())
{
var value = pI.GetValue(inQueryCat);
if (value != null)
{
var cats = inCatList.Where(cat => cat.GetType().GetProperty(pI.Name).GetValue(cat).Equals(value));
foreach (Cat cat in cats)
{
if (dict.ContainsKey(cat))
{
dict[cat].Add(pI);
}
else
{
dict.Add(cat, new List<PropertyInfo>() {pI});
}
}
}
}
int max = Int32.MinValue;
foreach (KeyValuePair<Cat, List<PropertyInfo>> keyValuePair in dict)
{
if (keyValuePair.Value.Count > max)
{
max = keyValuePair.Value.Count;
}
}
return dict.Where(pair => pair.Value.Count == max).Select(pair => pair.Key).ToList();
}
While this is the most generic solution there is (need some edge case improvements):
public class ReflectCmpare
{
public PropertyInfo PropertyInfo { get; set; }
public dynamic Value { get; set; }
}
public Cat GetBestCat(List<Cat> listOfCats, List<ReflectCmpare> catParamsToCompare, List<ReflectCmpare> kittensParamsToCompare)
{
var bestScore = 0;
var ret = listOfCats[0];
foreach (var cat in listOfCats)
{
var score = catParamsToCompare.Sum(param => param.PropertyInfo.GetValue(cat, null) == param.Value ? 1 : 0);
foreach (var baby in cat.Babys)
{
score+= kittensParamsToCompare.Sum(param => param.PropertyInfo.GetValue(baby, null) == param.Value ? 1 : 0);
}
if (score <= bestScore) continue;
bestScore = score;
ret = cat;
}
return ret;
}
You should really think about just doing simple compare function
considering this objects is not dynamic this is the way to go:
public Cat GetBestCat(List<Cat> listOfCats, string name , int? age , bool? gender, string race ,string babyName,int? babyAge,bool? babyGender )
{
var ret = listOfCats[0];
var highestScore = 0;
foreach (var cat in listOfCats)
{
var score = 0;
score += name != null && cat.Name.Equals(name) ? 1 : 0;
score += age.HasValue && cat.Age.Equals(age.Value) ? 1 : 0;
score += gender.HasValue && cat.Gender.Equals(gender.Value) ? 1 : 0;
score += race != null && cat.Race.Equals(race) ? 1 : 0;
score += name != null && cat.Name.Equals(name) ? 1 : 0;
score += cat.Babys
.Where(k => babyName==null || k.Name.Equals(babyName))
.Where(k => !babyAge.HasValue || k.Age.Equals(babyAge.Value))
.Any(k => !babyGender.HasValue || k.Gender.Equals(babyGender.Value))?1:0;
if (score <= highestScore) continue;
highestScore = score;
ret = cat;
}
return ret;
}

Join 2 tables to retrieve name and count linq Entity Framework

I have 2 tables Orders and Items and I am trying to query these 2 tables for statistics retrieval.
Orders has columns OrderID[PK], ItemID[FK], OrderStatus etc.
Items has columns ItemID[PK], ItemName, ItemPrice etc.
I am fetching list of orders based on date range and then I am returning their counts based on their status.
Below is my StatisticsResponse.cs to return the response.
public class StatisticsResponse
{
public int CancelledOrderCount { get; set; }
public int CompletedOrderCount { get; set; }
public int InProgressOrderCount { get; set; }
public int TotalOrders { get; set; }
public Dictionary<string,int> ItemOrders { get; set;}
}
This is how I am retrieving Orders between 2 dates.
var orders = _unitOfWork.OrderRepository
.GetMany(x => (x.OrderStatus == "Pending"
&& x.OrderDate.Value.Date >= dtStartDate
&& x.OrderDate.Value.Date < dtEndDate) ||
((x.OrderStatus == "Completed" || x.OrderStatus == "Cancelled")
&& x.DeliveryDate.Date >= dtStartDate || x.DeliveryDate.Date < dtEndDate) || (x.LastUpdated.Value.Date >= dtStartDate || x.LastUpdated.Value.Date < dtEndDate)).ToList();
if (orders != null)
{
return new StatisticsResponse()
{
TotalOrders = orders.Count(),
CancelledOrderCount = orders.Where(x => x.OrderStatus == "Cancelled").Count(),
CompletedOrderCount = orders.Where(x => x.OrderStatus == "Completed").Count(),
InProgressOrderCount = orders.Where(x => x.OrderStatus != "Completed" && x.OrderStatus != "Cancelled").Count()
}
}
Now, in the ItemOrders property, which is of type Dictionary<string,int>, I want to group each item with their name and count. I have ItemID in my orders list, and I would like to join 2 tables to get the name before storing.
I have tried to use GroupBy as below but am totally stuck on how to get the name for the Item after grouping
ItemOrders = new Dictionary<string, int>
{
orders.GroupBy(x=>x.ItemID)// Stuck here
}
I also read about GroupJoin but couldn't quite make sure whether it can fit in here.
Could someone please let me know how I can join these 2 tables to get their name based on their ID?
You can use something along this:
using System.Entity.Data; //to use Include()
...
Dictionary<string,int> itemOrders = dbContext.Orders.Include(o=> o.Item)
.GroupBy(o=> o.Item)
.ToDictionary(g => g.Key.Name, g => g.Count());
This is assuming:
There is a navigation property set up from Order to Item
Each Order has one Item
So, I was able to achieve this with GroupJoin through various online example as below:
if (orders != null)
{
var items = _unitOfWork.ItemRepository.GetAll();
var itemOrders = items.GroupJoin(orders,
item => item.ItemID,
ord => ord.ItemID,
(item, ordrs) => new
{
Orders = ordrs,
itemName = item.ItemName
});
StatisticsResponse statsResponse = new StatisticsResponse()
{
//...
};
foreach (var item in itemOrders)
{
statsResponse.ItemOrders.Add(item.itemName, item.Orders.Count());
}
return statsResponse;
}

Categories

Resources