How do I sort on a column that isn't in the database with LINQ to entities? - linq

LINQ to Entities 3.5 doesn't support String.Join so I'm binding my gridview to a property I define outside the Select statement. Obviously it's not letting me sort on RecipientNames because it's just an IEnumerable and that doesn't make sense. How can I use LINQ to Entities to sort on my new column? If possible I'd like to get rid of RecipientNamesList altogether and create something that LINQ will be able to handle for sorting.
IQueryable<NotificationDetail> resultsFlattened = results.Select(n => new NotificationDetail()
{
..
RecipientNames = n.NotificationRecipients.Select(nr => nr.Recipient.RecipientNameFirst + " " + nr.Recipient.RecipientNameLast).Where(s => s.Trim().Length > 0)});
});
IQueryable<NotificationDetail> resultsPaged = ApplySortingPaging(resultsFlattened,SortPageOptions);
return resultsPaged.ToEntityList(results.Count()); //blows up here, obviously
public string RecipientNamesList
{
get
{
return String.Join(", ", RecipientNames.ToArray());
}
}

It blows up because the sort cannot be translated into SQL. The easy solution is to make sure you perform the sort locally and not in SQL Server. Just add a ToList or AsEnumerable after the first query.
resultsFlattened = resultsFlattened.ToList();
You'll want to ensure that you do your paging before this, otherwise you could be pulling down a large number of rows from the database.
Or you use the Linq.Translations library developed by by Damien Guard and others. It enables you to use local, calculated properties just as you require.

Related

NHibernate delete from LINQ results

I'm wondering about the best usage of the delete method in nhibernate.
If you go the entity than just call delete and send it, but if not you need to query it or write a query and send it to delete method.
I'm wondering if its possible to write a linq expression and send it to delete.
Is it possible to perform a Linq transformation to hql and than call session.Delete(query)
with the generated hql?
I want to call Session.Delete, and give it a linq so it can know what to delete without selecting the data. Do you know a class that can convert linq expression to hql?
You now can directly in linq with NHibernate 5.0
//
// Summary:
// Delete all entities selected by the specified query. The delete operation is
// performed in the database without reading the entities out of it.
//
// Parameters:
// source:
// The query matching the entities to delete.
//
// Type parameters:
// TSource:
// The type of the elements of source.
//
// Returns:
// The number of deleted entities.
public static int Delete<TSource>(this IQueryable<TSource> source);
Exemple:
var tooOldDate = System.DateTime.Now.AddYears(5);
session.Query<User>()
.Where(u => u.LastConnection <= tooOldDate)
.Delete();
The Q in LINQ stands for "Query". So, no, you can't use a LINQ expression for delete.
That said, NH's query language, HQL, does support that.
In the same way that you can say "from Foo where Bar = :something" to get all the foos matching a condition, you can do this:
session.CreateQuery("delete Foo where Bar = :something")
.SetParameter("something", ...)
.ExecuteUpdate();
I have submitted a pull request for NH-3659 - Strongly Typed Delete. The link is available at nhibernate.jira.com/browse/NH-3659.
I know this is an old question but for those reading this now. NHibernate 5 released Oct 10, 2017 has added a Delete Linq extension
from documentation 17.6.3. Deleting entities
Delete method extension expects a queryable defining the entities to delete. It immediately deletes them.
session.Query<Cat>()
.Where(c => c.BodyWeight > 20)
.Delete();
I'm sure it would be possible to do what you want but the bottom line is that it doesn't make a lot of sense (not sure why you want to give NHibernate the select criteria when you can do it in a single statement, your approach would end up causing 2 hits to the database), having said that, one easy option you could do, is query the IDs using LINQ and pass those to NHibernate
int[] deleteIds = (from c in Customer where {some condition} select c.Id).ToArray<int>();
session.CreateQuery("delete Customer c where c.id in (:deleteIds)")
.SetParameterList("deleteIds", deleteIds)
.ExecuteUpdate();

linq problem with distinct function

I am trying to bind distinct records to a dropdownlist. After I added distinct function of the linq query, it said "DataBinding: 'System.String' does not contain a property with the name 'Source'. " I can guarantee that that column name is 'Source'. Is that name lost when doing distinct search?
My backend code:
public IQueryable<string> GetAllSource()
{
PromotionDataContext dc = new PromotionDataContext(_connString);
var query = (from p in dc.Promotions
select p.Source).Distinct();
return query;
}
Frontend code:
PromotionDAL dal = new PromotionDAL();
ddl_Source.DataSource = dal.GetAllSource();
ddl_Source.DataTextField = "Source";
ddl_Source.DataValueField = "Source";
ddl_Source.DataBind();
Any one has a solution? Thank you in advance.
You're already selecting Source in the LINQ query, which is how the result is an IQueryable<string>. You're then also specifying Source as the property to find in each string in the databinding. Just take out the statements changing the DataTextField and DataValueField properties in databinding.
Alterantively you could remove the projection to p.Source from your query and return an IQueryable<Promotion> - but then you would get distinct promotions rather than distinct sources.
One other quick note - using query syntax isn't really helping you in your GetAllSources query. I'd just write this as:
public IQueryable<string> GetAllSource()
{
PromotionDataContext dc = new PromotionDataContext(_connString);
return dc.Promotions
.Select(p => p.Source)
.Distinct();
}
Query expressions are great for complicated queries, but when you've just got a single select or a where clause and a trivial projection, using the dot notation is simpler IMO.
You're trying to bind strings, not Promotion objects... and strings do not have Source property/field
Your method returns a set of strings, not a set of objects with properties.
If you really want to bind to a property name, you need a set of objects with properties (eg, by writing select new { Source = Source })

Entity Framework LINQ Query using Custom C# Class Method - Once yes, once no - because executing on the client or in SQL?

I have two Entity Framework 4 Linq queries I wrote that make use of a custom class method, one works and one does not:
The custom method is:
public static DateTime GetLastReadToDate(string fbaUsername, Discussion discussion)
{
return (discussion.DiscussionUserReads.Where(dur => dur.User.aspnet_User.UserName == fbaUsername).FirstOrDefault() ?? new DiscussionUserRead { ReadToDate = DateTime.Now.AddYears(-99) }).ReadToDate;
}
The linq query that works calls a from after a from, the equivalent of SelectMany():
from g in oc.Users.Where(u => u.aspnet_User.UserName == fbaUsername).First().Groups
from d in g.Discussions
select new
{
UnReadPostCount = d.Posts.Where(p => p.CreatedDate > DiscussionRepository.GetLastReadToDate(fbaUsername, p.Discussion)).Count()
};
The query that does not work is more like a regular select:
from d in oc.Discussions
where d.Group.Name == "Student"
select new
{
UnReadPostCount = d.Posts.Where(p => p.CreatedDate > DiscussionRepository.GetLastReadToDate(fbaUsername, p.Discussion)).Count(),
};
The error I get is:
LINQ to Entities does not recognize the method 'System.DateTime GetLastReadToDate(System.String, Discussion)' method, and this method cannot be translated into a store expression.
My question is, why am I able to use my custom GetLastReadToDate() method in the first query and not the second? I suppose this has something to do with what gets executed on the db server and what gets executed on the client? These queries seem to use the GetLastReadToDate() method so similarly though, I'm wondering why would work for the first and not the second, and most importantly if there's a way to factor common query syntax like what's in the GetLastReadToDate() method into a separate location to be reused in several different other LINQ queries.
Please note all these queries are sharing the same object context.
I think your better of using a Model Defined Function here.
Define a scalar function in your database which returns a DateTime, pass through whatever you need, map it on your model, then use it in your LINQ query:
from g in oc.Users.Where(u => u.aspnet_User.UserName == fbaUsername).First().Groups
from d in g.Discussions
select new
{
UnReadPostCount = d.Posts.Where(p => p.CreatedDate > myFunkyModelFunction(fbaUsername, p.Discussion)).Count()
};
and most importantly if there's a way to factor common query syntax like what's in the GetLastReadToDate() method into a separate location to be reused in several different places LINQ queries.
A stored procedure would probably be one way to store that 'common query syntax"...EF, at least 4.0, works very nicely with SP's.

Can you sort Typed DataSet DataTables with Linq OrderBy?

I have a Typed DataSet DataTable which inherits TypedTableBase<T>, which in turn implements IEnumerable<T>. I can't seem to get this to work.
myDataTable.OrderBy(x => x.ID).ThenBy(y => y.ID2);
Instead I have to assign this statement to an IEnumerable(or List), then refill my DataTable manually with the newly ordered IEnumerable before I commit. Is this how it is intended to be? I've thought about creating my own extension method that will empty/refill my DataTables, but would this be wise?
Note: Typically I only need to sort for viewing purposes using DataView. But in this case I have a custom routine that must create a new access database with sorting requirements, which means I need to sort the actual DataTable so that I may re-commit it.
Thank you.
In order to do what you want, you must add the following reference to your project:
System.Data.DataSetExtensions
Once you have that added, you can order your DataTable like this:
var query = myDataTable.OrderBy(x => x.ID).ThenBy(y => y.ID2);
// use the DataView generated from the LINQ query
DataView dtv = query.AsDataView();
In order to iterate through the DataView, you can do the following:
var dtv = query.AsDataView();
foreach(DataRowView rw in dtv)
{
// you can also cast back to the typed data...
MyCustomRowType typedRow = (MyCustomRowType) rw.Row;
// do something here...
}
Alternatively you can typecast via LINQ this way:
var dtv = query.AsDataView().Cast<MyCustomRowType>();
// rowItem is of type MyCustomRowType...
foreach(var rowItem in dtv)
{
// do something here...
}
Linq extension methods do not alter the source enumerable.
var numbers = new int[]{1,2,3};
var reversed = numbers.OrderByDescending(x=>x);
foreach(var number in reversed)
Console.Write(number); // 321
foreach(var number in numbers)
Console.Write(number); // 123
If you want to sort a DataTable, you should be using DataViews. You create a view on your DataTable, then apply a Sort or Filter to it, then bind against it. Keep in mind DataSets are an older technology and not quite up to date on the latest and the greatest. A "newer" approach would be to use the Entity Framework.

Linq to Nhibernate Bulk Update Query Equivalent?

Not sure if I'm missing anything here. Basically, I am looking for Linq to Nhibernate to do the following SQL statement:
update SomeTable
set SomeInteger = (SomeInteger + 1)
where SomeInteger > #NotSoMagicNumber
Is there any way to do that?
Thanks!
Late answer but it now exists in Nhibernate 5.0.
//
// Summary:
// Update all entities selected by the specified query. The update operation is
// performed in the database without reading the entities out of it.
//
// Parameters:
// source:
// The query matching the entities to update.
//
// expression:
// The update setters expressed as a member initialization of updated entities,
// e.g. x => new Dog { Name = x.Name, Age = x.Age + 5 }. Unset members are ignored
// and left untouched.
//
// Type parameters:
// TSource:
// The type of the elements of source.
//
// Returns:
// The number of updated entities.
public static int Update<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, TSource>> expression);
In your case :
session.Query<SomeObject>()
.Update(i => new SomeObject { SomeInteger = i.SomeInteger + 1 });
Thanks NHibernate team!
Linq (not Linq to NHibernate, Linq in general) does not have a bulk update verb like SQL has. If you need the efficiency of the bulk update statement like yours, I'd just stick to SQL.
Like most (if not all) LINQ providers, LINQ to NHibernate only comes in useful in reading data.
To achieve what you want to do in NHibernate with the help of LINQ, you will want to fetch all of the relevant objects & update each one. Something like:
//NHibernate session initialisation & finalisation skipped for brevity
var relevantObjects = from i in session.Linq<SomeObject>()
where i.SomeInteger > notSoMagicNumber
select i;
foreach (SomeObject item in relevantObjects)
{
i.SomeInteger++;
session.Update(item);
}
Make sure you flush your session after all this, & wrap it all in a transaction to minimise the number of database updates.
All this said, depending on the size of your data, you may run into performance issues in using NHibernate for bulk operations. Using an IStatelessSession may help for that purpose, but I haven't tried it out myself.
UPDATE Turns out if you wrap it up in a transaction, you don't need to do session.Update or flush the session.

Resources