How can I write this statement using EF Core 2.1? - ef-core-2.1

I'm switching my code from EF6 to EF Core 2.1 and I'm stuck on this line:
var pinnedJobIds = ctx.Database.SqlQuery<int>("Select JobId From UserJobsPinned Where UserId=#p0", userId).ToList();
I know that EF Core allows for this:
ctx.Jobs.FromSql("Select * From Jobs").ToList();
However, I can't figure out how to simply return the Id field instead of the entire Job record.
I saw something like this, but I couldn't get it to work:
var pinnedJobIds = ctx.Query<int>()
but I didn't get any option for FromSql on that.
How can I do this in EF Core?

If Job is an entity type, you could write it like this:
var pinnedJobIds = ctx.Jobs
.FromSql($"Select * From UserJobsPinned Where UserId={userId}")
.Select(j => j.JobId)
.ToList();

Related

Bulk Update with LINQ to SQL

Is there a way to do a bulk update on a collection with LINQ? Currently if I have a List<myObject> and I want to update column1 to equal TEST for every row in the List I would setup a foreach loop and then for each individual object I would set the value and then save it. This works fine but I was just wondering if there was some LINQ method out there where I could do something like myOject.BulkUpdate(columnName, value)?
Your requirement here is entirely possible using Linq expressions and Terry Aney's excellent library on this topic.
Batch Updates and Deletes with LINQ to SQL
An update in the terms of the example you gave would be as follows:
using BTR.Core.Linq;
...
Context.myObjects.UpdateBatch
(
Context.myObjects.Where(x => x.columnName != value),
x => new myObject { columnName = value}
);
Edit (2017-01-20): It's worth nothing this is now available in the form of a NuGet package # https://www.nuget.org/packages/LinqPost/.
Install-Package LinqPost
Sounds like you're using LINQ To SQL, and you've got the basics laid out already.
LINQ To SQL is about abstracting tables into classes, and doesn't really provide the 'silver bullet' or one-liner you are looking for.
The only way to do that is to achieve your one-liner would be to make a stored proc to take that column name and new value, and implement that logic yourself.
db.MassUpdateTableColumn("Customer", "Name", "TEST");
....
CREATE PROC MassUpdateTableColumn
#TableName varchar(100), #ColumnName varchar(100), #NewVal varchar(100)
AS
/*your dynamic SQL to update a table column with a new val. */
Otherwise, it's as you describe:
List<Customer> myCusts = db.Customers.ToList();
foreach(Customer c in myCusts)
{
c.Name = "TEST";
}
db.SubmitChanges();
LINQ to SQL (or EF for that matter), is all about bringing objects into memory, manipulating them, and then updating them with separate database requests for each row.
In cases where you don't need to hydrate the entire object on the client, it is much better to use server side operations (stored procs, TSQL) instead of LINQ. You can use the LINQ providers to issue TSQL against the database. For example, with LINQ to SQL you can use context.ExecuteCommand("Update table set field=value where condition"), just watch out for SQL Injection.
EF Core 7.0 introduces Bulk Update and Bulk Delete.
For example, consider the following LINQ query terminated with a call to ExecuteUpdateAsync:
var priorToDateTime = new DateTime(priorToYear, 1, 1);
await context.Tags
.Where(t => t.Posts.All(e => e.PublishedOn < priorToDateTime))
.ExecuteUpdateAsync(s => s.SetProperty(t => t.Text, t => t.Text + " (old)"));
This generates SQL to immediately update the “Text” column of all tags for posts published before the given year:
UPDATE [t]
SET [t].[Text] = [t].[Text] + N' (old)'
FROM [Tags] AS [t]
WHERE NOT EXISTS (
SELECT 1
FROM [PostTag] AS [p]
INNER JOIN [Posts] AS [p0] ON [p].[PostsId] = [p0].[Id]
WHERE [t].[Id] = [p].[TagsId] AND [p0].[PublishedOn] < #__priorToDateTime_1)

Entity Framework 4.0 Code-First Dynamic Query

I would like to query a table based on a list of KeyValuePair. With a Model-First approach, I could do the following:
var context = new DataContext();
var whereClause = new StringBuilder();
var objectParameters = new List<ObjectParameter>();
foreach(KeyValuePair<string, object> pair in queryParameters)
{
if (whereClause.Length > 0)
whereClause.Append(" AND ");
whereClause.Append(string.Format("it.[{0}] = #{0}", pair.Key));
parameters.Add(new ObjectParameter(pair.Key, pair.Value));
}
var result = context.Nodes.Where(whereClause.ToString(), parameters.ToArray());
Now I'm using a Code-First approach and this Where method is not available anymore. Fortunately, I saw an article somewhere (I can't remember anymore) which suggested that I could convert the DbContext to a IObjectContextAdapter then call CreateQuery like this:
var result = ((IObjectContextAdapter)context)
.ObjectContext.CreateQuery<Node>(whereClause.ToString(), parameters.ToArray());
Unfortunately, this throws an error:
'{ColumnName}' could not be resolved in the current scope or context. Make sure that all referenced variables are in scope, that required schemas are loaded, and that namespaces are referenced correctly.
Where {ColumnName} is the column specified in the whereClause.
Any ideas how I can dynamically query a DbSet given a list of key/value pairs? All help will be greatly appreciated.
I think your very first problem is that in the first example you are using Where on the entity set but in the second example you are using CreateQuery so you must pass full ESQL query and not only where clause! Try something like:
...
.CreateQuery<Node>("SELECT VALUE it FROM ContextName.Nodes AS it WHERE " + yourWhere)
The most problematic is full entity set name in FROM part. I think it is defined as name of the context class and name of the DbSet exposed on the context. Another way to do it is creating ObjectSet:
...
.ObjectContext.CreateObjectSet<Node>().Where(yourWhere)

LINQ: select an object, but change some properties without creating a new object

I'm trying to select an object using values of another object in LINQ SQL,
I currently have this,
var result1 = (from s in pdc.ScanLogs
from ec in pdc.ExhibitsContacts
where s.ExhibitID == ec.ExhibitID
select ec.Contact);
I want to assign a value of ec.Contact.Note = ec.Comment;
Is there to a way to do this in LINQ SQL without writing multiple queries?
I read this blog article: http://blog.robvolk.com/2009/05/linq-select-object-but-change-some.html but it doesn't seem to work with LINQ SQL.
Basically you can't do this. LINQ is meant to be a query language, and what you want to do is mutate existing entities with your query. This means your query would have side effects and this is not something that is supported by LINQ to SQL.
While this won't work in a single query while returning LINQ to SQL entities, what will work is when you return simple DTO structues. For instance:
var result1 =
from s in pdc.ScanLogs
from ec in s.ExhibitsContacts
select new ContactDto
{
Id = ec.Contact.Id,
Note = ec.Comment,
SomeOtherFields = ec.Contact.SomeOtherFields
};
As a side note: also look at how I removed the where s.ExhibitID == ec.ExhibitID join from the query, by just using the ExhibitsContacts property of the ScanLog entity (which will be generated by LINQ to SQL for you when your database schema has the proper foreign keys defined).
Update:
When you need to return those DTO from several methods, you might consider centralizing the transformation from a collection of entities to a collection of DTO objects. What I tend to do is place this method on the DTO (which makes it easy to find). The code might look like this:
public class ContactDto
{
// Many public properties here
public static IQueryable<ContactDto> ToDto(
IQueryable<Contact> contacts)
{
return
from contact in contacts
select new ContactDto
{
Id = contact.Id,
Note = contact.ExhibitsContact.Comment,
ManyOtherFields = contact.ManyOtherFields
};
}
}
The trick with this static transformation method is that it takes an IQueryable and returns an IQueryable. This allows to to simply specify the transformation and let LINQ to SQL (or any other LINQ enabled O/RM) to efficiently execute that LINQ expression later on. The original code would now look like this:
IQueryable<Contact> contacts =
from s in pdc.ScanLogs
from ec in s.ExhibitsContacts
select ec.Contact;
IQuerable<ContactDto> result1 = ContactDto.ToDto(contacts);
the problem is that LINQ to SQL does not know how to interpret your extension method. The only way, other than using stored procedures from LINQ to SQL (which kind of defeats the ponit), is to get the object, update and then commit changes.

LINQ to SQL Projection: Func vs Inline

I am finding some unexpected behavior when using a projection in a LINQ to SQL query using a Func. Example code will explain better than words.
A basic L2S lambda query using projection:
db.Entities.Select(e => new DTO(e.Value));
It translates to the desired SQL:
SELECT [t1].[Value]
FROM [Entity] AS [t1]
However, when the projection is put into a Func like this:
Func<Entity, DTO> ToDTO = (e) => new DTO(e.Value);
And called like this:
db.Entities.Select(e => ToDTO(e));
The SQL is now pulling back all of the columns in the table, not just the one in the projection:
SELECT [t1].[Id], [t1].[Value], [t1].[timestamp], [t1].[etc...]
FROM [Entity] AS [t1]
So my question is, how do I encapsulate this projection without the LINQ to SQL instantiating the whole Entity?
Things to keep in mind, the DTO I am using has a protected default constructor, so I can't use an object initializer. And since the DTO class cannot be modified, I'd have to make a subclass to implement that behavior. Which is fine, if that's the only solution.
Thanks.
Edit:
Thanks to Brian for the solution. I had previously tried an Expression but couldn't figure out the syntax. Here's the working code:
Expression<Entity, DTO> ToDTO = (e) => new DTO(e.Value);
Then call it like this:
db.Entities.Select(ToDTO);
At first I was trying to call it like this, which wouldn't compile. This is the proper syntax for calling a Func, but not an Expression.
db.Entities.Select(e => ToDTO(e));
You probably need to create an Expression, not a Func
Expression<Func<Entity, DTO>> ToDTO = (e) => new DTO(e.Value);
IQueryable extension methods work with Expressions, not Funcs
By passing in a Func, you are probably invoking the IEnumerable extension method, which is why Linq2Sql is acting the way it is.

How to bind gridview through linq

I am using Linq-to-SQL.
Currently I am binding gridview through linq which query written in business logic call. I have extract record through query in business logic class and I want to bind that particular data to gridview and return data.
How to return data which type is array?
The code is here:
CMSBusiness.DataClasses1DataContext db = new DataClasses1DataContext();
var cate =
from p in db.categoryTables
select new
{
categoryId=p.categoryId,
categoryName=p.categoryName,
categoryDesc=p.categoryDesc
};
How to return value and bind gridview?
Try gridView.DataSource = cate;, this should work.
We also recommend you to take a look at this article.