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
Related
I have the following structure that I wan't to query using Linq, specifically Linq to Entities (Enitity Framework).
Table1: RouteMeta
Table2: SitePage
Multiple SitePages can link to the same RouteMeta.
I'm querying the Route Meta to select a number of rows. I'm using a generic repository, currently like this:
return r.Find().ToList();
There's nothing special about it - the Find method accepts an optional linq expression, so I could do something like this:
return r.Find(x => x.Status=1).ToList();
However, what I actually want to do is to select rows from RouteMeta where at least one linked row exists in SitePages with a property IsPublished = true.
return r.Find(x => x.SitePages("where y => y.IsPublished = true");
Obviously, the above isn't correct, I'm just trying to explain the scenario better.
Any advice appreciated.
try something like
return r.Find(x=>x.Sitepages.Any(y=>y.Published))?
I'd also suggesting using a profiler if possible to check that this translates properly into SQL. It probably should do but it depends on how your repository works.
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.
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.
C# NHibernate Issue - Im trying to return Distinct values.
This is what I have so far:
IList<TaskFor> objsResult = session.CreateCriteria(typeof(TaskFor)).setProjection(Projections.Distinct).List<TaskFor>();
return objsResult;
I am trying to return a group of values but I need to remove the duplicates.
I cant seem to get set projections to work properly for this instance.
Look forward to hearing any answers.
Thanks,
marc
I'd say this answer from Aidan Boyle could be very helpfull for you.
To perform a distinct query you can set the projection on the criteria to Projections.Distinct. You then include the columns that you wish to return. The result is then turned back into an strongly-typed object by setting the result transformer to AliasToBeanResultTransformer - passing in the type that the result should be transformed into. In this example I am using the same type as the entity itself but you could create another class specifically for this query.
ICriteria criteria = session.CreateCriteria(typeof(Person));
criteria.SetProjection(
Projections.Distinct(Projections.ProjectionList()
.Add(Projections.Alias(Projections.Property("FirstName"), "FirstName"))
.Add(Projections.Alias(Projections.Property("LastName"), "LastName"))));
criteria.SetResultTransformer(
new NHibernate.Transform.AliasToBeanResultTransformer(typeof(Person)));
IList people = criteria.List();
This creates SQL similar to (in SQL Server at least):
SELECT DISTINCT FirstName, LastName from Person
Please be aware that only the properties that you specify in your projection will be populated in the result.
The advantage of this method is that the filtering is performed in the database rather than returning all results to your application and then doing the filtering - which is the behaviour of DistinctRootEntityTransformer.
I'm trying to build a REST-ful API for my app. Currently I have something like this:
www.example.com/submissions/?format=json
This will return latest ten submissions in JSON. Each object has its details, such as submission name, date created, user, body, etc.
I'd like to do something such as:
www.example.com/submissions/?format=json&filter=name,user
The filter should make the request to return the same result but to only include the details mentioned, i.e. each object will only have a name and user.
This is fairly straightforward in terms of the JSON output. I can load all the columns from the database and create and serialize an object that will only include the columns in the filter. However, I do not want to load all the columns in the database - I want to bother my database with only the columns that I will include in the response.
I want to do something like this:
var result = from record in Submissions
select
{
Name,
Date,
User,
Body
};
Now I have the result object, which is IQueryable, so no call to database made yet.
Then, I should examine the filter querystring and exclude the columns that are not mentioned.
Finally, I can execute the select statement with something like
JavaScript.Serialize(result.ToList());
Is this possible with LINQ to SQL?
An alternative to building your Select expression tree by hand is Dynamic LINQ, which provides a Select method that takes a string:
var filter = "name,user";
var result = Submissions.Select("new(" + filter + ")");
The string is then translated into an expression tree and passed on to your query provider.
Yes. You are going to want to research Modifying Expression Trees. Specifically the MemberInit Expression.