Call c# method in linq query(entity framework) - c#

Is there any chance to run c# method in linq query?
I need to do something like that:
//...
class Match {
public double PrecentageMatching(string s1, string s2) {
//...
return result; // value from 0.0 to 1.0
}
}
//...
string pattern = "nameOfRecord";
var similarRecords = db.Table.Select(
r => Match.PrecentageMatching(r.name, pattern) > 0.5
);
I know that there linq wont know a method PrecentageMatching. But I'm wonder if there is any way to do it?
I'm using Entity framework.
I need to do it without stored procedures and assembly on database side. I dont have access to database.

You first need to fetch the data from the database and only then perform the transformation:
string pattern = "nameOfRecord";
var similarRecords = db.Table
.Select(r => r.name)
.ToList() // This call fetches the data from the DB
.Select(x => Match.PrecentageMatching(x, pattern) > 0.5);
That's not a problem at all, because you are only transforming the returned data with your method. If you want to use your method to filter the returned data using Where, you have a problem, because you would first need to return ALL data and filter the data on your client. This might be a problem if the table is big.

Change your PrecentageMatching method to a static method. It will then be able to find Match.PrecentageMatching method.
Also, what you're doing there will return a IEnumerable of boolean values. If you want to return records from the Table, you want "Where" instead of "Select".
Edit: as per comments, you need to call ToList() method.

Related

Entity Framework: Update inside LINQ query

I've came across this idea of updating a table inside of a LINQ query instead of making the query first, and updating each object returned by that query.
For instance, it is possible to change the value of any property associated with x inside of this query:
var Query = from x in EFContext.SomeTable
where x.id == 1
// SET X = Model or x.Name = "NewName"
select SaveChanges();
Could something like this be done at all?
From MSDN:
In a query that returns a sequence of values, the query variable itself
never holds the query results and only stores the query commands.
Execution of the query is deferred until the query variable is
iterated over in a foreach or for loop. This is known as deferred
execution; that is, query execution occurs some time after the query
is constructed. This means that you can execute a query as frequently
as you want to. This is useful when, for example, you have a database
that is being updated by other applications. In your application, you
can create a query to retrieve the latest information and repeatedly
execute the query, returning the updated information every time.
So, your query will be executed when you do the foreach to update your entities. As #recursive said, LINQ is useful when you need to do a query over a collection, not to update data specifically.
As an aditional info, you can also force immediate execution. This is useful when you want to cache the results of a query,for example, when you want to use some functionalities that Linq to Entities doesn't support. To force immediate execution of a query that does not produce a singleton value, you can call the ToList method, the ToDictionary method, or the ToArray method on a query or query variable.
I believe the best possible way to do so would be to write an extension method which can be done by creating a static class:
public static class Extensions
{
public static IEnumerable<T> Remove<T>(this DbSet<T> Input, Func<T, Boolean> Objects) where T : class
{
var I = Input.Where(Objects).ToList();
for (int i = 0; i < I.Count; i++)
{
Input.Remove(I[i]);
}
return Input;
}
public static IEnumerable<T> Update<T>(this DbSet<T> Input, Func<T, Boolean> Objects, Action<T> UpdateAction) where T : class
{
var I = Input.Where(Objects).ToList();
I.ForEach(UpdateAction);
return I;
}
}
Then you can do:
var Context = new EFContext();
Context.YourTable.Remove(x=> x.Id == 1);
Context.SaveChanges();
// OR
Context.Update((x=> x.Id == 1), (y)=> {y.Title = "something"});
Context.SaveChanges();
You could use method calls, and write a ForEach or ForEachWithContinue method that lets you modify each element, but EF wouldn't know what to do with it anyway, and you'd have to use ToList to pull the items out of EF before you could do anything to them.
Example of ForEach (functional purists won't like this of course):
public static void ForEach<T>(this IEnumerable<T> pEnumerable, Action<T> pAction) {
foreach (var item in pEnumerable)
pAction(item);
}
public static IEnumerable<T> ForEachWithContinue<T>(
this IEnumerable<T> pEnumerable,
Action<T> pAction
) {
foreach (var item in pEnumerable)
pAction(item);
return pEnumerable;
}
Then:
EFContext
.SomeTable
.Where(x => x .id == 1)
.ToList() // come out of EF
.ForEach(x => x.Name = "NewName");
EFContext.SaveChanges();
(Actually, List<T> even already has a ForEach method, too, so writing the IEnumerable extensions is not strictly necessary in this case.)
Basically, EF needs to pull the data into memory to know that you have changed anything, to know what your changes are, and to know what to save to back to the DB. I would also consider what it is you're trying to do, where you are overwriting data that neither the user nor the program has even looked at. How did you determine that this was data you wanted to overwrite in the first place?
Also, you can write direct SQL queries straight to the DB as well, using the ExecuteStoreCommand method, which would be the "normal" way of accomplishing this. Something like:
EFContext.ExecuteStoreCommand(
"UPDATE SomeTable SET Name = {0} WHERE ID = {1};",
"NewName",
1
);

Linq Extension method for Join

I am in the process of learning LINQ, ASP.NET, EF, and MVC via online video tutorials. I would love some help understanding Joins in LINQ extension method syntax.
For simplification, I have two tables (these map to a SQL DB):
User Table:
public int userID{get;set;}
public string firstName{get;set;}
...
Address
public int ownerID{get;set;}
public int value{get;set;}
public string Nickname{get;set;}
public string street{get;set;}
public string zip{get;set;}
...
Let's say I want to find all the property that a particular user owns. I believe I can do something like this:
var test = db.User
.Join(db.Address, user => user.userID, add => add.ownerID, (user, add) => new { user, add });
Source: http://byatool.com/c/linq-join-method-and-how-to-use-it/
This should be equivalent to
SELECT * FROM User a JOIN Address b on a.userID = b.ownerID
Please confirm that this is correct.
Now, what if I wanted to find all property that a particular user owns that has a value greater than x. Let's take it a step further and say x is a result from another LINQ query. How do I force execution of x inside of a second query? Do I even have to consider this, or will LINQ know what to do in this case?
Thanks
EDIT:
When I try to use the result of a query as a parameter in another, I am required to use a greedy operator to force execution. Many people like to use .Count() or .ToList(). I only expect x (from example above) to return 1 string by using .Take(1). If I append ToList() to the end of my first query, I am required to use x[0] in my second query. This seems like a messy way to do things. Is there a better way to force execution of a query when you know you will only have 1 result?
If I understand your question, you're trying to do a conditional on a joined model?
var query = db.Users.Where(x => x.Addresses.Where(y => y.Value >= yourValue).Any());
That will return all users who have a property value greater than yourValue. If you need to return the addresses with the query, you can just add Include to your query. For example:
query.Include(x => x.Addresses);
You don't need to manually do that Join that you have in your example.

Use a function within LINQ

I am using LINQ to create a list. But I want to use a function at the end to generate the object iself, something LINQ complains about
LINQ to Entities does not recognize the method 'WashroomStatusItem GetWashroomStatusForItem(WashroomStatus)' method, and this method cannot be translated into a store expression.
What am I doing wrong?
var query = (from c in context.WashroomStatus
where c.WashroomId == GroupItem.WashroomID
select GetWashroomStatusForItem(c));
private WashroomStatusItem GetWashroomStatusForItem(WashroomStatus item)
{
WashroomStatusItem temp = new WashroomMonitorWCF.WashroomStatusItem();
//do stuff with it
return temp;
}
The problem is that the SQL conversion can't convert your method into SQL. You should use AsEnumerable() to "switch" from the out-of-process provider to LINQ to Objects. For example:
var query = context.WashroomStatus
.Where(c => c.WashroomId == GroupItem.WashroomID)
.AsEnumerable()
.Select(c => GetWashroomStatusForItem(c));
Note that if GetWashroomStatusForItem only uses some properties, you may want to project to those separately first, to reduce the amount of information fetched from the server:
var query = context.WashroomStatus
.Where(c => c.WashroomId == GroupItem.WashroomID)
.Select(c => new { c.Location, c.Date };
.AsEnumerable()
.Select(p => GetWashroomStatusForItem(p.Location, p.Date));
Jon Skeet's answer is correct, but I'd add that depending on the nature of GetWashroomStatusForItem(), it should probably either be broken down into LINQ statements and added into the query itself, or it should be executed after the query has returned.
So, lets say GetWashroomStatusForItem() looks something like this: note that this is extremely oversimplified.
public static WashroomStatus GetWashroomStatusForItem(Item c)
{
return c.WashroomStatus;
}
it should just be added to the LINQ query like this:
var query = (from c in context.WashroomStatus
where c.WashroomId == GroupItem.WashroomID
select c.WashroomStatus);
But if it relies heavily on stuff not in the db, I'd just end the Linq statement before you get the WashroomStatus, and then call GetWashroomStatusForItem() on the results. It's not gonna a performance difference since Linq uses lazy evaluation, and you generally want to keep db operations separate from "programmatic" ones.

Can't Translate Extension Method Into Store Expression

I have an extension method as follows:
public static bool SatisfiesSomeCondition(this Post post, SomeObj someObj)
{
return post.SomeObjId == someObj.SomeObjId;
}
And i'm trying to use it like this:
var query = ctx.Posts.Where(p => p.SatisfiesSomeCondition(someObj)).ToList();
But i get the error:
LINQ to Entities does not recognize the method 'Boolean SatisfiesSomeCondition(xx.xx.xx.Post, xx.xx.xx.SomeObj)' method, and this method cannot be translated into a store expression.
If i change the query to:
var query = ctx.Posts.Where(p => p.SomeObjId == someObj.SomeObjId).ToList();
Which is identical to the method.
It works fine, and executes the expected T-SQL.
Why doesn't my first query work? It's a static method, can't it figure out how to create the expression tree? (e.g a WHERE filter). Surely i don't have to materialize the query first? (which means the records i don't want come back over the wire, and i'm doing paging/ordering here, so that's not an option).
Of course, i can just go with what works (e.g the above), but the method SatisfiesSomeCondition is an existing method used across the domain and i want to re-use that functionality, not duplicate it.
Any ideas?
Change it to:
public static IQueryable<Post> SatisfiesSomeCondition(this IQueryable<Post> query, SomeObj someObj)
{
int id = someObj.SomeObjId;
return query.Where(post => post.SomeObjId == id);
}
and use it like:
var query = ctx.Posts.SatisfiesSomeCondition(someObj)).ToList();
This way it should work. You can combine multiple Where conditions in single query so it should offer you at least basic reusablity.
The LINQ to Entities engine has no way of knowing what your static method does.
LINQ queries can only be translated from expression trees.

using nhibernate, how would i construct a query that wants the most recent 50 rows

i have a table called orders and i have a column called Last Update (and an order object with a LastUpdate property). I want to construct a query using nhibernate to get the last 50 rows so i don't go to the database and get everything and then have to filter results in my application.
is this possible in nhibernate. I am trying to use the LINQ api
Here's the LINQ version of this query.
var orders = session.Query<Order>()
.OrderByDescending(x => x.LastUpdate)
.Take(50);
Here's the screen shot of the code sample...
Here's the screen shot from NHibernate Profiler...
If you are using a Criteria then use SetMaxResults(50) and do a descending sort on the date time.
You can use SetMaxResults(50), although depending on which 50 rows you want (latest? first? last?) you'll probably also need to do a SortBy expression as well.
var orders = session.Query<Linq>()
.OrderByDescending(x => x.LastUpdate)
.Take(50);
In general case suggesing LastUdate can be nullable using Linq2SQL you may write extension method to your IQueriable:
public static partial class FooTable
{
public static IQueryable<FooTable> LastUpdated(this IQueryable<FooTable> queryable, int count)
{
return queryable.Where(x => (x.LastUdate != null))
.OrderByDescending(x => x.LastUdate)
.Take(count);
}
}

Categories

Resources