How do I code the Select clause in my LINQ satament to select column aliases so I can sort on them basically I want to accomplish this SQL statement in LINQ:
select
type_id as id,
type_desc as description
from
dbo.equip_type_avt
order by
description
What do I replace the ????? in my .Select clause in my LINQ statement?
public IQueryable<equip_type_avt> GetGridEquipmentTypes(string sidx, string sord)
{
try
{
return
ulsDB.equip_type_avts
.Select(?????)
.OrderBy(sidx + " " + sord);
}
catch (Exception ex)
{
string strErr = ex.Message;
return null;
}
}
You can use an anonymous type:
table.Select(x => new
{
ID = x.type_id,
Description = x.type_desc
});
However, you can't access the properties of an anonymous type outside of the scope where it is declared (without reflection or other dirty hackery, anyway) so if you want to use the result outside of that function you just create a class and create an instance of it in the query using a type initializer:
public class Foobar
{
public int ID { get; set; }
public string Description { get; set; }
}
...
table.Select(x => new Foobar() // Note the difference here
{
ID = x.type_id,
Description = x.type_desc
});
Question though: if you want to name the columns differently, why don't you change it in the place where the column-property mapping is declared? In LINQ-to-SQL you can have the database column be named whatever you like but give the property the name "ID" or "Description".
I'm not sure i understand your question, how does sidx and sord relate to your query?
Isn't your problem rather that you have to end your query with OrderBy(...).ThenBy(...) instead of a combined OrderBy?
If you want to sort by a string in the easy way, download the Dynamic LINQ library.
However, that's 2000 lines of code, most of which are entirely redundant for just the purpose of sorting.
Doing it yourself shouldn't be too hard, but requires a fair bit of knowledge on expression trees. I can't really help you there though.
EDIT: I've added another answer, that hopefully answers your actual question :)
Related
I have a table I'm displaying in a site which is pulling data from a few different SQL tables. For reference, I'm following this guide to set up a sortable table. Simplify the model, say I have a main class called "Data" which looks like this (while the Quotes class stores the DataID):
namespace MyProject.Models
{
public class Data
{
public int Id { get; set; }
public string Name { get; set; }
public int LocationId { get; set; }
public Models.Location Location { get; set; }
public IList<Models.Quote> Quotes { get; set; }
}
}
Then I retrieve the IQueryable object using this code:
IQueryable<Models.Data> dataIQ = _context.Data
.Include(d => d.Quotes)
.Include(d => d.Location);
The "Quotes" table is a one-to-many mapping while the location is a one-to-one. How do I order the IQueryable object by a value in the Quotes table? Specifically I'm trying to do this when the user clicks a filter button. I tried just doing this on the first item in the list (which is guaranteed to be populated) but that throws the client-evaluation error I mentioned in the title. This is the code I'm using to apply the sorting:
//This one throws the client-evaluation error
dataIQ = dataIQ.OrderByDescending(d => d.Quotes[0].QuoteName);
//This one works as expected
dataIQ = dataIQ.OrderByDescending(d => d.Location.LocationName);
So you have a table called Models, filled with objects of class DataItems. and you have a table Quotes. There is a on-to-many relations between DataItems and Quotes: Every DataItem has zero of more Quotes, every Quote belongs to exactly one DataItem, namely the DataItem that the foreign key DataItemId refers to.
Furthermore, every Quote has a property QuoteName.
Note that I changed the identifier of your Data class, to DataItem, so it would be easier for me to talk in singular and plural nouns when referring to one DataItem or when referring to a collection of DataItems.
You want to order your DataItems, in ascending value of property QuoteName of the first Quote of the DataItem.
I see two problems:
What if a DataItem doesn't have any quotes?
Is the term "First Quote` defined: if you look at the tables, can you say: "This is the first Quote of DataItem with Id == 4"?
This is the reason, that it usually is better to design a one-to-many relation using virtual ICollection<Quote>, then using virtual IList<Quote>. The value of DataItem[3].Quotes[4] is not defined, hence it is not useful to give users access to the index.
But lets assume, that if you have an IQueryable<Quote>, that you can define a "the first quote". This can be the Quote with the lowest Id, or the Quote with the oldest Date. Maybe if it the Quote that has been Quoted the most often. In any case, you can define an extension method:
public static IOrderedQueryable<Quote> ToDefaultQuoteOrder(this IQueryable<Quote> quotes)
{
// order by quote Id:
return quotes.OrderBy(quote => quote.Id);
// or order by QuoteName:
return quotes.OrderBy(quote => quote.QuoteName);
// or a complex sort order: most mentioned quotes first,
// then order by oldest quotes first
return quotes.OrberByDescending(quote => quote.Mentions.Count())
.ThenBy(quote => quote.Date)
.ThenBy(quote => quote.Id);
}
It is only useful to create an extension method, if you expect it to be used several times.
Now that we've defined a order in your quotes, then from every DataItem you can get the first quote:
DataItem dataItem = ...
Quote firstQuote = dataItem.Quotes.ToDefaultQuoteOrder()
.FirstOrDefault();
Note: if the dataItem has no Quotes at all, there won't be a firstQuote, so you can't get the name of it. Therefore, when concatenating LINQ statements, it is usually only a good idea to use FirstOrDefault() as last method in the sequence.
So the answer of your question is:
var result = _context.DataItems.Select(dataItem => new
{
DataItem = dataItem,
OrderKey = dataItem.Quotes.ToDefaultQuoteOrder()
.Select(quote => quote.QuoteName)
.FirstOrDefault(),
})
.OrderBy(selectionResult => selectionResult.OrderKey)
.Select(selectioniResult => selectionResult.Data);
The nice thing about the extension method is that you hide how your quotes are ordered. If you want to change this, not order by Id, but by Oldest quote date, the users won't have to change.
One final remark: it is usually not a good idea to use Include as a shortcut for Select. If DataItem [4] has 1000 Quotes, then every of its Quote will have a DataItemId with a value of 4. It is quite a waste to send this value 4 for over a thousand times. When using Select you can transport only the properties that you actually plan to use:
.Select(dataItem => new
{
// Select only the data items that you plan to use:
Id = dataItem.Id,
Name = dataItem.Name,
...
Quotes = dataItem.Quotes.ToDefaultQuoteOrder().Select(quote => new
{
// again only the properties that you plan to use:
Id = quote.Id,
...
// not needed, you know the value:
// DataItemId = quote.DataItemId,
})
.ToList(),
});
In entity framework always use Select to select data and select only the properties that you really plan to use. Only use include if you plan to change / update the included data.
Certainly don't use Include because it saves you typing. Again: whenever you have to do something several times, create a procedure for it:
As an extension method:
public static IQueryable<MyClass> ToPropertiesINeed(this IQueryable<DataItem> source)
{
return source.Select(item => new MyClass
{
Id = item.Id,
Name = item.Name,
...
Quotes = item.Quotes.ToDefaultQuoteOrder.Select(...).ToList(),
});
}
Usage:
var result = var result = _context.DataItems.Where(dataItem => ...)
.ToPropertiesINeed();
The nice thing about Select is that you separate the structure of your database from the actually returned data. If your database structure changes, users of your classes won't have to see this.
Ok, I think I figured it out (at least partially**). I believe I was getting the error because what I had was really just not correct syntax for a Linq query--that is I was trying to use a list member in a query on a table that it didn't exist in (maybe?)
Correcting the syntax I was able to come up with this, which works for my current purposes. The downside is that it's only sorting by the first item in the link. I'm not sure how you'd do this for multiple items--would be interested to see if anyone else has thoughts
dataIQ = dataIQ.OrderByDescending(d => d.Quotes.FirstOrDefault().QuoteName);
**Edit: confirmed this is only partially fixing my issue. I'm still getting the original error if I try to access a child object of Quotes. Anyone have suggestions on how to avoid this error? The below example still triggers the error:
IQueryable<Models.Data> dataIQ = _context.Data
.Include(d => d.Quotes).ThenInclude(q => q.Owner)
.Include(d => d.Location);
dataIQ = dataIQ.OrderByDescending(d => d.Quotes.FirstOrDefault().Owner.OwnerName);
i have an entity which contains some attributes and i want to write a like (contains) query for all of its attributes with one single parameter from front controller. i have successfully achieved it for the String values but for the numeric (long, double) values, i cannot use containing keyword as it throws an exception (Parameter value ['%10%'] did not match expected type java.lang.Double()... something...).
my entity fields
private String firstName;
private double rating;
my repository query method
List<MobileUser> findByFirstNameIgnoreCaseContainingOrRatingContaining(String value, double value2);
my service layer method which takes only one value
public List<MobileUserDTO> getMobileUsersLike(String value) {
// parses the value and if it is not a numeric value it will be -1 (this is also a bad logic)
Double parseDouble = (double) -1;
try { parseDouble = Double.parseDouble(value); } catch (NumberFormatException ignored) { }
// calls repository
List<MobileUser> allUsersLike = mobileUserRepository.findByFirstNameIgnoreCaseContainingOrRatingContaining(value, parseDouble);
return getMobileUserDtoList(allUsersLike);
}
how do i achieve this? thank you.
You can try to use JPQL query:
#Query("FROM MobileUser WHERE firstName like %:firstName% OR CAST(rating AS TEXT) LIKE %:rating% ")
List<MobileUser> findByNameAndRating(#Param("name") String firstName, #Param("rating") String rating);
You can't use SQL like for double unless you cast it to String.
I want to implement a function that accepts a DbSet (non-generic), a string, and object, and returns DbSet. something like the following pseudu:
public static DbSet Any(DbSet set, string propertyName, objectParameter)
{
var tableName = set.TableName;
var columnName = set.GetColumnNameForProperty(propertyName);
var query = string.Format("SELECT TOP(1) {0} FROM {1} WHERE {0} = {2}",
columnName,
tableName,
objectParameter);
}
I think that SQL query is enough since I'll be able to execute it directly on the Database (context.Database.ExecuteSql).
What I want to do is get the table name from the given DbSet, then the column name in the database.
It is not possible from non generic DbSet but this problem can be easily solved by using:
public static IEnumerable<T> Any(DbSet<T> set, string property, objectParameter)
where T : class
{ ... }
Returning DbSet doesn't make sense because once you query data it is not DbSet anymore.
The bigger problem is getting table name from generic DbSet / ObjectSet because this information is not available from those classes. It is almost impossible to get it at all because it requires accessing non public members of items from MetadataWorkspace.
I have a scenario where I need to pull approximately 7500 database records where each records has 6 child entity lists. Each of those child lists could be 0 to approximately 125 records.
My class structure kind of looks like this:
public class Entity
{
public int ID { get; set;
public string Name { get; set; }
public ICollection<ChildEntity1> Children1 { get; set; }
public ICollection<ChildEntity2> Children2 { get; set; }
public ICollection<ChildEntity3> Children3 { get; set; }
public ICollection<ChildEntity4> Children4 { get; set; }
... 2 more collections
}
After I retrieve all of the Entities, I need to iterate over each one and perform some calculations, call some web services and other various things and ultimately export to a file.
What are the best strategies for retrieving this data from MS Sql Server 2008 using c# 4? Is a DataSet with DataAdapters the best way? ORM?
I want to stay away from select N+1 scenarios for obvious reasons.
DECLARE CURSOR on the Entity.
OPEN CURSOR.
For each FETCH CURSOR
SELECT the child rows for the current Entity.
Write the output.
CLOSE CURSOR.
So what I ended up doing was having a nested DataReader, one outer one for the parent entity to get all of the parents, and then one inner one that reads all of the children in one statement using the reader.NextResult() method similar to this:
var exampleSql = "select * from child1Table; " +
"select * from child2Table; " +
"select * from child3Table";
// and so on for the other child tables
using (var outerReader = cmd.ExecuteReader())
{
while (outerReader.Read())
{
var entity = new Entity();
entity.Prop1 = outerReader[0];
entity.Prop2 = outerReader[1];
//.... etc.
using (var cmdInner = new SqlCommand(exampleSql))
using (var innerReader = cmdInner.ExecuteReader())
{
while (innerReader.Read())
{
var child = new Child1();
child.Prop1 = innerReader[0];
// ... etc.
entity.Children1.Add(child);
}
innerReader.NextResult();
while (innerReader.Read())
{
var child = new Child2();
child.Prop1 = innerReader[0];
// ... etc.
entity.Children2.Add(child);
}
innerReader.NextResult();
// and so on for the other child entities
}
}
}
At least this way, I'm only sending one sql statement to the database to retrieve all of my child entities per parent instead of a separate statement per child per parent.
If anyone has a better way, please feel free to let me know.
Btw, my example code is just pseudo code. The real thing is using parameterized queries and no select stars, just the columns i need. The intent is to show the approach, not the actual implementation.
I need to translate the following Code to an Expression and I will explain why:
results = results.Where(answer => answer.Question.Wording.Contains(term));
results is IQueryable<ISurveyAnswer>
Question is ISurveyQuestion
Wording is String
The problem is, Question is not always the name of the LINQ to SQL property.
This will give me the PropertyInfo for the actual ISurveyQuestion property
private static PropertyInfo FindNaturalProperty<TMemberType>(Type search)
{
IDictionary<string,PropertyInfo> properties = new Dictionary<string,PropertyInfo>();
search.GetProperties().Each(prop =>
{
if (null != prop.PropertyType.GetInterface(typeof(TMemberType).Name))
properties.Add(prop.Name, prop);
});
if (properties.Count < 1) throw new ArgumentException(String.Format("{0} has no properties of type {1}", search.Name, typeof(TMemberType).Name));
if (properties.Count == 1) return properties.Values.First();
search.GetInterfaces().Each(inter =>
{
inter.GetProperties().Each(prop =>
{
if (null != prop.PropertyType.GetInterface(typeof(TMemberType).Name))
properties.Remove(prop.Name);
});
});
if (properties.Count < 1) throw new ArgumentException(String.Format("{0} has no properties of type {1} that are not members of an interface", search.Name, typeof(TMemberType).Name));
if (properties.Count > 1) throw new AmbiguousMatchException(String.Format("{0} has more than one property that are of type {1} and are not members of an interface", search.Name, typeof(TMemberType).Name));
return properties.Values.First();
}
Once I have the PropertyInfo how do I translate that to an Expression Tree?
EDIT:
What I basically need is:
results = results.Where(answer => answer.GetQuestionProperty().GetValue(answer).Wording.Contains(term));
But that doesn't work so I need to build the Expression Tree myself, for linq-to-sql.
Reading the question I think what you're after is Dynamic Linq - which is a helper library to let you build Linq queries dynamically (!) using strings as opposed to at design time. That means that if you can get your property name you should be able create your query on the fly.
ScottGu has an article here
What your trying to do is create a dynamic query and you want the action tables / properties your query against to be dynamic as well. I am not sure if this is easily possible based on how you want to use it.
Check out ScottGu's blog post:
http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
and
Check out Rick Strahl's blog post:
http://www.west-wind.com/Weblog/posts/143814.aspx
http://www.linqpad.net/
linqpad will convert it for you.