Pass in a field name at run time using Dynamic LINQ library? - c#

This is a small application used on a company intranet by 4 people at the most, and I am new to C# and .NET, so the solution does not have to be grandiose.
I am trying to create a search field for simple .NET C.R.U.D app where the user can pick a category they wish to search from (such as Application Name, or Manager) and then a text-box where they can filter the results based on that field name. The items in the drop down menu are the class field members and I would like for all of them to be searchable. I'm using the Dynamic Linq Library to create a string so that I can pass the column name at run-time but for some reason my queries return no results.
Here is my current query
dr_details = dr_details.Where("#0 == #1",searchType, searchString);
So for instance, searchType & searchString get their values from the query string (We'll say "Manager" and "Joe", respectively) so that the query should be substituted as:
dr_details = dr_details.Where("Manager == Joe");
this gives me no results. However if I hard code the string "Manager == Joe" into the query runs fine.
Any suggestions? This problem would make me yank out my hair if it was long enough! :p

You don't necessarily have to use dynamic linq for this if you don't want.
Try something like this:
var query = myList.AsQueryable();
switch(searchType)
{
case "Manger":
query = query.where(x=> x.Manager == searchString);
break;
case "....":
break;
default:
// No search type match in string... an exception? your call
break;
}
I find this more intuitive since it "builds" the query as it goes...
Edit:
If that is too painful try following this post

Related

How do I programmatically set the sort field on a MongoDB query using the C# driver?

I have a collection of Tool objects, which I want to optionally filter, and return into a paginated table on a web page. I've got it working with the filter and the pagination, but I'm having trouble with the sort. I'm using an Angular Material table, which lets the user chose the sort field and direction at run time.
Using the MongoDB C# driver, I built a collection of tools which match theFilter, (fo = find options = case insensitive). Skip and Limit provide the pagination - I do know that's not necessarily efficient for big collections, that is not a concern here - and ToList sends it to the API.
tools = _tools.Find<Tool>(theFilter, fo)
.Sort(Builders<Tool>.Sort.Descending(x => x.Description))
.Skip(pageNo * pageSize)
.Limit(pageSize)
.ToList();
In that example, the Sort call correctly sorts the collection in descending order by the description field. I need to be able, at run time, to chose a different field (e.g. x.id, x.Name, x.location, x.whatever), and to be able to switch between descending and ascending order.
Attempts to use MongoDB's syntax:
.Sort("{ description: -1}")
fail, as does attempting to build a SortDefinition object using the field's name:
private SortDefinition<T> BuildSortDefinition<T>(string fieldName, string sortDirection)
{
FieldDefinition<T> theField = new StringFieldDefinition<T>(fieldName);
SortDefinition<T> theSort;
if (sortDirection.ToLower() == "desc")
theSort = Builders<T>.Sort.Descending(theField);
else
theSort = Builders<T>.Sort.Ascending(theField);
return theSort;
}
I've only been able to make Sort work if I use a lambda expression. How can I either fix the lambda expression to use a configurable field; or use the .Sort properly in order to use a configurable field, in this scenario?
My problem was using the wrong casing for the search field - "description" instead of "Description". Once I passed the correct case, it worked fine.

How can I do case insensitive and concatenated field searches using nhibernate / linq?

I have an asp.net-mvc website that uses Fluent Nhibernate Linq / SQL Server. I have a textbox where someone can enter a name and that generate the following query that I am using now to search my Person table:
return Session.Query<Person>()
.Where(r => (r.LastName.Contains(s) || r.FirstName.Contains(s)));
This works as expected in terms of translating to a "SQL like query"
Select * from Person where FirstName like '%%' or LastName like '%%'
but I have 2 new requirements that I am not sure that nhibernate linq supports.
In some cases people are entering the name in upper or lower case so I want to be able to do a case insensitive search.
Since it's a single textbox, in some cases people type in both the first and last name (something like "Smith, Joe" and that fails to find a result given that overall string doesn't exist in either the first or last name fields. Besides breaking the UI up into separate fields (which I can't do for some other reasons) is there any suggestion on how I could support the query to include the following combination of user's search string
[First] [Last]
[Last], [First]
in the above search code.
To solve issue with mixed upper/lower, we can just convert both sides into .ToLower()
return Session.Query<Person>()
.Where(r => (r.LastName.ToLower().Contains(s.ToLower())
|| r.FirstName.ToLower().Contains(s.ToLower())));
Check this link for more details how the NHibernate native InsensitiveLikeExpression.cs is working (for almost every dialect it is doing the same) :
NHibernate IsInsensitiveLike treats strings as case sensitive
The second part, here is some super simple algorithm (if it is one at all)
var first = searched.Split(' ')[0].ToLower();
var last = searched.Split(' ')[1].ToLower();
var emps = session.Query<Person>()
.Where(e =>
(e.FirstName.ToLower().StartsWith(first)
&& e.LastName.ToLower().StartsWith(last))
||
(e.FirstName.ToLower().StartsWith(last)
&& e.LastName.ToLower().StartsWith(first))
)
Very similar solution could be used (and I do) for combobox searching... Where "Ra Ko" will also find Radim Kohler...
Your first point-
1). In some cases people are entering the name in upper or lower case
so I want to be able to do a case insensitive search.
Answer is already given by Radim Köhler - that convert both sides into .ToLower()
Now for your second point, below might be useful.
return session.Query<Person>()
.Where(e =>
((e.FirstName.ToLower() + " " + e.LastName.ToLower()).Contains(s))
||
((e.LastName.ToLower() + ", " + e.FirstName.ToLower()).Contains(s))
)
There might be any syntax error as i have not tested in VS.

What is the best practice to implement advanced search using Entity Framework?

I am using two different ways to implement an advanced search where each approach has their advantages and disadvantages.
The user can perform an advanced search using 3 parameters (name, familyname, and mobile).
The first way I tried is to check which of those parameters are provided by the user; that requires me to do 8 if & else checks (2 ^ number of parameters) and in each conditions I write a separate query which accepts the corresponding parameters, for example if the user has entered name & family name the where clause of the query will look like this:
where(x=>x.name.contains(name) && x.familyname.contains(familyname))
or in another case if the user has entered only the mobile the where clause of the query will look like this :
where(x=>x.mobile==mobile)
the advantage of this way is that I hit the databse once but the disadvantage is that I have to write a lot more code.
The second way I tried is that I declared an IQueryable<> object and I feed data into It with no condition at first and then I check search parameters one by one and if any of them has value I filter the IQueryable with that value and at last I perform a .ToList(). This way has the advantage of much less code but hits the database twice which is a problem.
Here is a code sample of the second way:
List<ShoppingCardForGridView> list_ShoppingCardForGridView =
new List<ShoppingCardForGridView>();
IQueryable<ShoppingCardForGridView> outQuery =
from x in db.ShoppingCards
orderby x.TFDateBackFromBank descending
where x.TFIsPaymentCompleted == true
select new ShoppingCardForGridView
{
Id = x.Id,
TFCustomerFullName =
x.Customer.TFName + " " + x.Customer.TFFamilyName,
TFDateBackFromBank = x.TFDateBackFromBank.Value,
TFIsDelivered = x.TFIsDelivered,
TFItemsPriceToPay = x.TFItemsPriceToPay,
TFDateBackFromBankPersian = x.TFDateBackFromBankPersian
};
if (!string.IsNullOrEmpty(CustomerFullName))
{
outQuery = outQuery.Where(x =>
x.TFCustomerFullName.Contains(CustomerFullName));
}
if (!string.IsNullOrEmpty(IsDelivered))
{
bool deliveryStatus = Convert.ToBoolean(IsDelivered);
outQuery = outQuery.Where(x => x.TFIsDelivered == deliveryStatus);
}
list_ShoppingCardForGridView = outQuery.ToList();
I wonder if there is any better way or a best practice to perform an advanced search using entityframework?
You're not hitting the database multiple times with your second solution. Remember that an IQueryable object is a query itself, not the results of a query. Execution is deferred until the query is actually iterated. Conditionally appending multiple Where clauses based on various if checks is changing the query itself, not processing the results of the query (since there are no results at that point in time).
You can use a profiler on your database to verify that only one query is being executed, and that the one query contains all of the filtering in that one go.
This is a reasonably common pattern; I've used it on a number of instances for making custom search pages.

How to hide some fields using NHIbernate ICriteria?

Here's a scenario that I am working on : Right now we have a SQL statement that reads like this :
SELECT a.ID,a.MsgNumber,CASE WHEN #HasAccess=1 THEN Title ELSE '*********' END AS Title FROM Messages
We want that operators be able to see if a message registered in system but can't see the title if they are not authorized.
I'm changing this part of code so we can use a NHibernate criteria to generate the same result (so we can produce dynamic queries according to filters that user selects).
I know that I can use projections to get some fields or constant values from a criteria but can not figure out how I should combine them to do what I want.
It looks like #HasAccess is a parameter passed in by your code, not a value determined by the database. If so, then the easiest way to do what you want is to modify the criteria in code based on the value that you would pass through in the query, eg:
var projections = Projections.ProjectionList()
.Add(Projections.Id())
.Add(Projections.Property("MsgNumber"))
.Add(hasAccess ? Projections.Property("Title") : Projections.Constant("*********"));
var criteria = session.CreateCriteria<Message>()
.Add(... your restrictions ...)
.SetProjection(projections)
.List<object[]>();
If however #HasAccess is determined by your database somehow, then you could use:
Projections.Conditional(Restrictions.Eq("HasAccess", 1),
Projections.Property("Title"),
Projections.Constant("*********"))
assuming that you can get HasAccess into your criteria somehow

SQL and C# retrieving a single record from database

I have a webpage built with a dropdown that has a list of books. These books are stored on the sql sever. Using the MVC and aspx pages i am trying to figure out how to retrieve information about about a book such that when the user selects a book it passes the price of the book. I am new to sql sever and var statements.
I am able to retrieve the books name from the webpage and send it to my controller
In my model i am trying to get that data here is my thought. I want to get the price and store into a string. But the only way i have seen to pull information is using the var statement.
such that
var price = from p in BooksDB.Price
where p.Book_Name==bookName
select new {p.Book_Price}
but how do i get that value and store it into a string based on the Books_Name that I have retrieved from the dropdown box
by the way my table looks like this
Id_Num Book_Name Book_Price
1 Pro C# 29.99
2 Beg C++ 10.99
First a terminology correction.
var is just a keyword the compiler lets you use to subsitute for the type. During compiletime, the compiler will figure out what the type is based on usage.
var myString = "hihihi";
string myString = "hihihi";
The var statement has nothing to do with accessing a database, although it was added to make using LINQ easier on us lazy developers.
Instead what you are doing is creating a LINQ to SQL query. I've modified it slightly (you don't need to create an anonymous object). After you create the statement, you need to execute it by calling "ToList(), First(), or FirstOrDefault() etc"
LINQ typically employs lazy or deferred evaluation for queries, and isn't executed until you trigger execution.
var price = from p in BooksDB.Price
where p.Book_Name==bookName
select p.Book_Price;
//assuming Book_Price is stored as a string datatype.
string bookPrice = price.FirstOrDefault();
//otherwise
string bookPrice = (price.FirstOrDefault() ?? "").ToString();
if(!String.IsNullOrEmpty(bookPrice))
{
//do something with the string.
}
If I understand correctly something like below should work.
var record = BooksDB.Price.FirstOrDefault(r => r.Book_Name == bookName);
If record is not null at this point then record.Book_Price should contain the data you are looking for (not accounting for ambiguity in the database.)
Instead of select new {p.Book_Price}, use something like select p.Book_Price. You could add .ToString() to the end to force it to come out as a String rather than the data type from the table.
You'll also want to wrap the entire LINQ statement in () and append .FirstOrDefault() to get just one value. The "OrDefault" part protects you from an exception when the result set us empty.

Categories