Drop 'Where' and move the condition into the 'First' in LINQ - c#

I have a windows application which has some similar code as below.
As class named Order.
class Order{
public string Orderid { get; set; }
public string CustName { get; set; }
}
Now, in another class in this application, object for Order class is created and value is assigned to it.
Order order = new Order();
order = JObject.Parse(some JSON data).ToObject<Order>();
Now I want to extract the CustName based on Orderid from order. For this I have used LINQ.
string custName = order.Where(s => s.Key == "o123").First().Value;
I'm using Sonarqube to check the code quality. When I run the SonarQube tool , it is showing that I need to refactor my code where I have used LINQ. This is the exact line it shows.
Drop 'Where' and move the condition into the 'First'.
I have searched for it a lot but couldn't understand what it is trying to say. Can anyone explain me how to refactor this line , so that it passes the SonarQube expectations.
Any input is highly helpful.Thanks.

You are perfoming an operation in two steps when you could do that by using the First lambda expression
string custName = order.First(s => s.Key == "o123").Value;
Linq method First definition:
First<TSource>(this IEnumerable<TSource>, Func<TSource, Boolean>)
First parameter is the IEnumerable you are using (Linq are extension methods)
Second parameter allows you to set the filter declaring a Func<TSource, Boolean> as parameter, that you could define as s => s.Key == "o123"

What it is telling you is that the Where is unnecessary, and the code can be expressed as this:
string custName = order.First(s => s.Key == "o123").Value;
The logic of the original code is this:
"look through the list to find any matches, and take the first"
Which is the same as the changed code:
"take the first match in the list"
Note though that this code will throw an exception if there is no match. If there's ever a possibility that there will not be a matching customer, use FirstOrDefault instead:
string custName;
var customer = order.FirstOrDefault(s => s.Key == "o123");
if (customer != null) {
custName = customer.Value;
}
else {
// code to handle no match
}

Related

C# - Parsing Data inside Lambda Linq Block | Splitting .Where()

I want to filter Entities from a Database, but I am kinda stuck on how to correctly chain the Methods together....
The User can search from one Textfield the Id, Title and Description. This SearchString will be bound to SearchString in SearchData.
I have a method:
public List<Movies> Search(SearchData search)
{
var movies = from m in entities select m;
if (!String.isNullOrEmpty(search.SearchString))
{
movies = movies.Where(x => x.Title.Contains(search.SearchString)).Where(//descritpion);
}
return movies;
}
This works, but I also need to check for the Id
.Where(x=>x.Id == search.SearchString)
This won´t work since Id is a int and SearchString a String.
I have tried multiple ways to do so:
I did use "Convert.ToInt32" on the SearchString or "Convert.ToString" on the Id, but out of some reason I won´t get anything back with this and an Error when I search for a String.
I tried to use a block with in the where : .Where(x => {if(Tryparse(Searchstring) {}else{}}), but it doesn´t like it when I try to return the Movie object or null.
I also tried to split the clause up:
if (int.tryparse(searchstring))
movies = movies.where(x=>x.id ==Int32.Parse(SearchString));
movies = movies.where(//title and desc)
,but with this all the Movies I have found in the if will be filtered out due to the title and desc.
My questions are:
1.)Is it possible to "split" those Methods so that they behave like an OR instead of an AND?.... Or that they will not be executed anymore after one worked since the User will only be allowed to enter an Int OR a String. I have more values I am filtering against for which I would need this too.
2)How can I test against more "complex" Logic inside the Lambdas?
I'm not sure if I understand it correctly but If you want to search where id == search.SearchString, and also any other condition with OR then you should do something like this:
.Where(x=>x.Id == search.SearchString && (x.description.Contains(search.Description) || x.Title.Contains(search.Title) || x.Whatever.Contains(search.Whatever)));
You can use an OR in the Where clause.
public List<Movies> Search(SearchData search)
{
var movies = from m in entities select m;
if (!String.IsNullOrEmpty(search.SearchString))
{
movies = movies.Where(x => x.Title.Contains(search.SearchString) || x.Description.Contains(search.SearchString));
}
return movies;
}
I don't understand why the movie Title should contain the movie Id, for my point of view it's bad practice,
i think you need to update the SearchData and add a separate field for the Id, and use it for filtering, this will make things more clear and easy for debugging
In order to respect both conditions, I'd first check whether the search string can be parsed to an integer. If so, you can compare the id against it, if not, the Id comparison should be omitted:
public List<Movies> Search(SearchData search)
{
var movies = from m in entities select m;
if (!String.isNullOrEmpty(search.SearchString))
{
int? searchId;
if (int.TryParse(search.SearchString, out i)
searchId = i;
else
searchId = null;
movies = movies.Where(x =>
(searchId.HasValue && x.Id == searchId.Value)
|| x.Title.Contains(search.SearchString)).Where(//descritpion);
}
return movies;
}
In the comparison, the first part checks whether the searchId is set and - if so - compares the id of the row against it. In addition, it checks whether the title contains the search string. If any of the two conditions are met, the row is returned. However, if the user enters 123 and this is both a valid id and part of a title, both rows will be returned.
If you want to search for id (exclusive) or a part of a text, you could use the following approach:
public List<Movies> Search(SearchData search)
{
var movies = from m in entities select m;
if (!String.isNullOrEmpty(search.SearchString))
{
Expression<Func<Movie, bool>> whereClause;
if (int.TryParse(search.SearchString, out searchId)
whereClause = (x) => x.Id == searchId;
else
whereClause = (x) => x.Title.Contains(search.SearchString);
movies = movies.Where(whereClause).Where(//descritpion);
}
return movies;
}
When entering 123, above sample searches for the id, when entering anything that cannot be parsed into an integer, it looks for a part of the title.
As for your second question: when using Entity Framework, the conditions in your lambda expressions are translated to SQL. This limits the available options. However, as shown in the first example, you can prepare your data and adjust the conditions accordingly in many cases. Nevertheless, not every valid lambda expression can be translated to SQL.

A lambda expression with a expression body cannot be converted to an expression tree C# entity framework

I have an entity table with below model
public class VulnerabilityImportFileLog
{
public Collection<Vulnerability> Vulnerability { get; set; }
}
Trying to return an string value using the select statement in the entity framework query
var vulnerabilityImportFileLogSelect = vulnerabilityImportFileLogQuery
.Select(vulnerabilityImportFileLog =>
new VulnerabilityImportedScansListViewDto
{
AssetName = vulnerabilityImportFileLog.Vulnerability.Select(y =>
{
if (y.AssetHostIPOrDomain.HostIPAssetId.HasValue)
return y.AssetHostIPOrDomain.HostIPAsset.Title;
else if (y.AssetHostIPOrDomain.DomainAssetId.HasValue)
return y.AssetHostIPOrDomain.DomainAsset.Title;
else
return y.AssetHostIPOrDomain.HostIPOrDomain;
}
).Distinct().ToList()
});
Where AssetName is of type List<string> and getting an exception as A lamda expression with a statement body cannot be converted to an expression tree
An suggested answer was to use AsEnumerable(), but even that not solved the issue.
You can solve the problem by using linq query syntax with 'let' keyword:
AssetName = (from y in vulnerabilityImportFileLog.Vulnerability
let resultString = y.AssetHostIPOrDomain.HostIPAssetId.HasValue
? y.AssetHostIPOrDomain.HostIPAsset.Title
: y.AssetHostIPOrDomain.DomainAssetId.HasValue
? y.AssetHostIPOrDomain.DomainAsset.Title
: y.AssetHostIPOrDomain.HostIPOrDomain
select resultString
)
.ToList()
.Distinct();
Assuming vulnerabilityImportFileLogQuery is an IQueryable<vulnerabilityImportFileLog> then a typical way to fetch details from the associated Vulnerability relations would be via a SelectMany. Given you need to do substitution depending on what is available, one option would be to do a double-projection. The first pass selects and materializes just the details we want to inspect, then the second pass composes the results.
vulnerabilityImportFileLogQuery.SelectMany(x => new
{
x.Vulnerability.AssetHostIpOrDomain.HostIpOrDomain,
HostIpAssetTitle = x.Vulnerability.AssetHostIpOrDomain.HostIpAsset.Title,
DomainAssetTitle = x.Vulnerability.AssetHostIpOrDomain.DomainAsset.Title
}).ToList()
.Select(x => x.HostIpAssetTitle ?? DomainAssetTitle ?? HostIpOrDomain)
.Distinct()
.ToList();
What this essentially does is fetch the 3 values, our default HostIpOrDomain value, and the Titles from the HostIpAsset and DomainAsset if they are available. EF will return #null if the entities aren't associated or the Title is #null. This query is materialized using the ToList, then from that we Select the title to use based on a null check, before taking our Distinct set.
Hopefully that gives you a few ideas as a starting point.

error in calling and calculate function with int? as input

I have a function like that :
public int? calculateContractPrice(int? comid)
{
int? sum = 0;
var q = from i in dbconnect.tblMaterialGroups
where i.tenderId == _tenderId
select i.id;
foreach (int i in q )
{
var q2 = from g in dbconnect.tblMaterialTenderAnnouncePrices
where g.MaterialGroupId == i && g.companyId == comid
select g;
sum = q2.First().amount*q2.First().price + q2.First().amount*q2.First().PriceForElse + sum;
}
return sum ;
}
When i try to execute this :
List<presentationcontract> q = (from i in dbconnect.tblContracts
where i.tender == _tenderId
select new presentationcontract()
{
tax =(calculateContractPrice(i.companyId)*(6/100)).ToString()
}).ToList();
Tax is string .after executing i got this error :
couldn't translate expression calculateContractPrice(i.companyId)*(6/100),invoke(value(system.Func1[system.nullable1[system.Int32]]))).ToString() into SQL and could not treat it as a local expression
Your edit makes clear the issue. You're trying to do
tax =(calculateContractPrice(i.companyId)*(6/100)).ToString()
in a sql statement but calculateContractPrice is in c#! To understand what's going on you really need to understand a bit how LINQ works.
First of all, stop using the silly sql-style syntax for LINQ. It is less powerful than the lambda syntax and hides what is going on under the hood in a way that makes it hard to understand.
Second consider a LINQ statement
users.Where(u => u.Name == "George").ToList();
where users is IEnumerable<User>. What happens here is that the lambda part is of type Func<User, bool> and gets compiled to a method that gets run against every single instance of User.
Now consider this LINQ statement
db.Users.Where(u => u.Name == "George").ToList();
where db.Users is IQueryable<T>. This looks the same but what happens is VERY different. What happens is that lambda is actually of type Expression<Func<User, bool>> this doesn't get compiled to a method, instead it gets compiled into something called an expression tree. This gets passed to the LINQ provider (in your case Entity Framework I'm guessing) which examines it and converts that into a SQL statement
SELECT Id, Name, Address FROM users WHERE Name = 'George'
What is happening in your case is that it sees the call to calculateContractPrice and simply has no way of converting that to SQL.
What you should therefore do is ensure the query runs first, then use the IEnumerable<T> form of LINQ that runs in c# to call your method.
var contracts = dbconnect.tblContracts.Where(i => i.tender == _tenderId)
.ToList() //Query executes here, now you have IEnumerable<T>
.Select(i => new PresentationContract {
Tax = ...
}).ToList(); //this ToList is only necessary if you want to prevent multiple iteration
You will want to solve all the other problems everyone else pointed out as well of course.
A few other notes - you will want to read up on .Net naming conventions. Usually, anything public,protected, or internal (classes, fields, properties, etc.) is recommended to be PascalCase. Also you probably want to move the division portion into the PresentationContract class. This class that has a Tax property should probably be the one that knows how to generate it.
Try this:
int? ret = calculateContractPrice(i.companyId);
if(ret.HasValue)
{
tax =(ret.Value*(6/100)).ToString();
}
You should make sure that the function indeed returned a value and then you use that integer value in calculation.

C# How do i return iGrouping to a list using lambda expression

I am using entity framework to do this , i have this lambda expression :
public IList<Model.questionhint> GetRecordsPlease(int listTask, int listActivity)
{
IList<Model.questionhint> lstRecords = context.questionhints.ToList();
return lstRecords.GroupBy(x => new {
x.QuestionNo,
x.ActivityID,
x.TaskID })
.Where(a => a.Key.TaskID == listTask
&& a.Key.ActivityID == listActivity)
.ToList();
}
but it says :
Cannot implicitly convert type
'System.Collections.Generic.List<System.Linq.IGrouping<AnonymousType#1,iStellar.Model.questionhint>>'
to 'System.Collections.Generic.IList<iStellar.Model.questionhint>'. An
explicit conversion exists (are you missing a cast?)
This codes is in my CRUD class file named DAOQuestionHint .
How do i return this because i need to call it in my xaml.cs ( code behind ) like this :
private DAO.DAOQuestionHint qh = new DAO.DAOQuestionHint();
IList<Model.questionhint> lstQuestionHints = qh.GetRecordsPlease(taskID, activityID);
This is how my database table looks like :
what i wanted is to group records of the same activityID , taskID and questionNo together in a line , now every record is seperated line by line like this :
i want it to be like
I [answer] to have a nap every afternoon
The sun [answer] not move round the earth.
So i tried to use alternative in code behind by using for loop but that doesn't work perfectly if there are 3 or more records of the same activityID , taskID and questionNo , so i tot i need to group them in the query first .
It seems to me that you don't need to group at all - you just need to filter. Unless you really have more than one hint per question (with the same task and activity), you just need:
public IList<Model.questionhint> GetRecordsPlease(int listTask, int listActivity)
{
return context.questionhints
.Where(a => a.TaskID == listTask && a.ActivityID == listActivity)
.ToList();
}
Note that unlike your current code, this will perform the filtering in the database, rather than pulling the whole table to the client and then grouping/filtering. You need to be aware that any time you use context.SomeTable.ToList(), that will pull the entire contents of that table.
I'd also strongly suggest that you change your model to use type names which follow .NET naming conventions, so QuestionHint instead of questionhint.
(Finally, I'd remove the Please from the method name - computers don't need you to be polite in your naming like this.)
EDIT: If you really do want groups, you need to change the return value. For example, you could just return a sequence of groups:
public IList<IGrouping<int, Model.questionhint>>
GetRecordsPlease(int listTask, int listActivity)
{
return context.questionhints
.Where(a => a.TaskID == listTask && a.ActivityID == listActivity)
.ToList()
.GroupBy(a => a.QuestionNo)
.ToList();
}
I've deliberately called ToList() here before the grouping and then again afterwards so that the data is fetched once and you can iterate over those groups however you like without it going back to the database. (It's possible that just the final ToList would be enough, but I wouldn't like to say for sure.)

which design pattern to use for filtering query? c#

I have a database table with a list of products (clothing). The products belong to categories and are from different stores.
Sample categories: tops, bottoms, shoes
Sample stores: gap.com, macys.com, target.com
My customers can request to filter products in the following ways:
all products (no filter)
by category
by store
by category and store
Right now I have ONE method in my "Products" class that returns the products depending on the type of filter requested by the user. I use a FilterBy enum to determine which products need to be returned.
For example, if the user wants to view all products in the "tops" category I call this function:
Products.GetProducts(FilterBy.Category, "tops", "");
I have the last parameter empty because it's the string that contains the "store" to filter by but in this case there is no store. However, if the user wants to filter by category AND store I'd call the method this way:
Product.GetProducts(FilterBy.CategoryAndStore, "tops", "macys.com");
My question is, what's a better way to do this? I just learned about the strategy design pattern. Could I use that to do this in a better (easier to extend and easier to maintain) way?
The reason I'm asking this question is because I figure this must be a pretty common problem that people are repeatedly solving (filtering products in various ways)
According to Eric Evan's "Domain Drive Design" you need the specification pattern. Something like this
public interface ISpecification<T>
{
bool Matches(T instance);
string GetSql();
}
public class ProductCategoryNameSpecification : ISpecification<Product>
{
readonly string CategoryName;
public ProductCategoryNameSpecification(string categoryName)
{
CategoryName = categoryName;
}
public bool Matches(Product instance)
{
return instance.Category.Name == CategoryName;
}
public string GetSql()
{
return "CategoryName like '" + { escaped CategoryName } + "'";
}
}
Your repository can now be called with specifications
var specifications = new List<ISpecification<Product>>();
specifications.Add(
new ProductCategoryNameSpecification("Tops"));
specifications.Add(
new ProductColorSpecification("Blue"));
var products = ProductRepository.GetBySpecifications(specifications);
You could also create a generic CompositeSpecification class which would contain sub specifications and an indicator as to which logical operator to apply to them AND/OR
I'd be more inclined to combine LINQ expressions though.
Update - Example of LINQ at runtime
var product = Expression.Parameter(typeof(Product), "product");
var categoryNameExpression = Expression.Equal(
Expression.Property(product, "CategoryName"),
Expression.Constant("Tops"));
You can add an "and" like so
var colorExpression = Expression.Equal(
Expression.Property(product, "Color"),
Expression.Constant("Red"));
var andExpression = Expression.And(categoryNameExpression, colorExpression);
Finally you can convert this expression into a predicate and then execute it...
var predicate =
(Func<Product, bool>)Expression.Lambda(andExpression, product).Compile();
var query = Enumerable.Where(YourDataContext.Products, predicate);
foreach(Product currentProduct in query)
meh(currentProduct);
Probably wont compile because I have typed it directly into the browser, but I believe it is generally correct.
Another update :-)
List<Product> products = new List<Product>();
products.Add(new Product { CategoryName = "Tops", Color = "Red" });
products.Add(new Product { CategoryName = "Tops", Color = "Gree" });
products.Add(new Product { CategoryName = "Trousers", Color = "Red" });
var query = (IEnumerable<Product>)products;
query = query.Where(p => p.CategoryName == "Tops");
query = query.Where(p => p.Color == "Red");
foreach (Product p in query)
Console.WriteLine(p.CategoryName + " / " + p.Color);
Console.ReadLine();
In this case you would be evaluating in memory because the source is a List, but if your source was a data context that supported Linq2SQL for example I think this would evaluate using SQL.
You could still use the Specification pattern in order to make your concepts explicit.
public class Specification<T>
{
IEnumerable<T> AppendToQuery(IEnumerable<T> query);
}
The main difference between the two approaches is that the latter builds a known query based on explicit properties, whereas the first one could be used to build a query of any structure (such as building a query entirely from XML for example.)
This should be enough to get you started :-)
The strategy pattern doesn't necessarily knit well with the common interface-based repository approach. Personally, I'd probably go one of two ways here:
One search method that supports combinations of options:
IList<Product> GetProducts(string category, string store, ...);
(then selectively apply the combinations of filters (i.e. null means "any") - either when building a command, or pass down to a SPROC that does something similar.
With LINQ, maybe a predicate expression?
IList<Product> GetProducts(Expression<Func<Product,bool>> predicate);
Of course, with LINQ you could also use composition by the caller, but that is harder to write a closed / fully-tested repository for:
`IQueryable<Product> Products {get;}`
(and have the caller use .Where(x=>x.Category == "foo")) - I'm not so sure about this last one long-term...
I think I'd make a Category class and a Store class, instead of just strings:
class Category
{
public Category(string s)
{
...
}
...
}
And then maybe:
Product.GetProducts(
Category category, //if this is null then don't filter on category
Store store //if this is null then don't filter on store
)
{
...
}
The Category and Store classes might be related (they might both be subclasses of a Filter class).
I am answering this based on my little knowledge of patterns.
Decorator pattern might help here (considering you can add a filter & get results. Apply new filters on it & get new results)
I would go with something like a strategy for the filters themselves and write CategoryFilter and StoreFilter classes. Then I would use a composite or decorator to combine the filters.
Can't you just add Where stuff as you go here?
var products = datacontext.Products;
if(!String.IsNullOrEmpty(type))
products = products.Where(p => p.Type == type);
if(!String.IsNullOrEmpty(store))
products = products.Where(p => p.Store == store);
foreach(var p in products)
// Do whatever
or something like that...

Categories

Resources