SQL to Entity Framework - entity-framework

I'm using Azure SQL database and Entity Framework for performing database operations on it.
How can I convert this SQL query to Entity Framework ?
begin tran set transaction isolation level serializable
go
select top 1 * from Employee with (UPDLOCK) where EmpID = #id;
......
commit
I want to lock a row from other threads when some thread is reading it and perform some operation on it.
I cannot use stored procedures as I'm using Azure SQL database.

I don't know why you could not use a stored procedure for this. As marc_s mentioned Azure SQL DB supports stored procedures. That said you can always execute the query from EF if you want. EF does not support specifying query hints for LINQ queries, so the easiest would be to use raw SQL execution APIs. It would look like this using EF6:
using (var context = new MyContext())
{
using (var transaction = context.Database.BeginTransaction())
{
try
{
var employee = context.Employees.SqlQuery(
"select top 1 * from Employee with (UPDLOCK) where EmpID = #id", id);
// ...
transaction.Commit();
}
catch (Exception)
{
transaction.Rollback();
}
}
}

Related

How to write subquery in select list in EF Core?

Select *,
(Select DefaultStartDay from Scheduler.ProgramSettings ps where ps.DefaultStartDay = s.Id ) [DefaultStartDay]
from Scheduler.Schedules s
where ScheduleType = 2;
I want to write above SQL query in EF Core, Specially I need subquery in select list to get data from another table with specific condition.
please refer image.Sample Data with SQL Query
I have tried below EF Core but getting wrong result.
var model = _context.Schedules
.Where(s => s.ScheduleType == 2)
.Select(rv => new ProgramSetting
{
Id = rv.Id,
ProgramTemplateId = rv.ProgramTemplateId,
IsActive = rv.IsActive,
DefaultStartDay = rv.Id
}).ToArray();
The SQL query is wrong and this is a misuse of EF Core.
First, that SQL will fail if there's more than 1 result from the subquery. Even in SQL you'd need a different query. An INNER JOIN would return the same results without failing if there are multiple matches.
Select s.*,ps.DefaultStartDay
from Scheduler.Schedules s
inner join Scheduler.ProgramSettings ps on ps.DefaultStartDay = s.Id
where ScheduleType = 2;
Second, using LINQ to emulate SQL is a misuse of both EF Core and LINQ. EF isn't a replacement for SQL, it's an ORM. Its job is to give the impression of working with in-memory objects instead of tables, not allow you to write SQL queries in C#
It's the ORM's job to generate JOINs as needed from the relations between entities (not tables). In this case, if Schedule has a ProgramSettins property, EF would generate the necessary joins automatically. Loading an entire schedule object could be as simple as :
var schedules=_context.Schedules
.Incule(sch=>sch.ProgramSettings)
.Where(s => s.ScheduleType == 2)
.ToArray();
Include is used to eagerly load the settings, not to force a JOIN.
If a Select clause is used that requires a property from ProgramSettings, the JOIN will be generated automatically, eg :
var namesAndDays=_context.Schedules
.Where(s => s.ScheduleType == 2)
.Select(s=>new {
Name = s.Name,
StartDay = s.ProgramSettings.DefaultStartDay
})
.ToArray();

Reduce number of queries for JPQL POJO containing an entity

Entity relation: Transaction(#ManyToOne - eager by default) -> Account
String sql = "SELECT new com.test.Pojo(t.account, SUM(t.value)) FROM Transaction t GROUP BY t.account";
List list = entityManager.createQuery(sql).getResultList();
By default JPA using Hibernate implementation will generate 1 + n queries. The n queries are for lazy loading of the account entities.
How can I make this query eager and load everything with a single query? The sql equivalent would be something like
SELECT account.*, SUM(t.value) FROM transactions JOIN accounts on transactions.account_id = accounts.id GROUP BY account.id
, a syntax that works well on PostgreSQL. From my findings Hibernate is generating a query that justifies the lazy loading.
SELECT account.id, SUM(t.value) FROM transactions JOIN accounts on transactions.account_id = accounts.id GROUP BY account.id
Try marking the #ManyToOne field as lazy:
#ManyToOne(fetch = FetchType.LAZY)
private Account account;
And change your query using a JOIN FETCH of the account field to generate only one query with all you need, like this:
String sql = "SELECT new com.test.Pojo(acc, SUM(t.value)) "
+ "FROM Transaction t JOIN FETCH t.account acc GROUP BY acc";
UPDATE:
Sorry, you're right, the fetch attribute of #ManyToOne is not required because in Hibernate that is the default value. The JOIN FETCH isn't working, it's causing a QueryException: "Query specified join fetching, but the owner of the fetched association was not present".
I have tried with some other approaches, the most simple one that avoids doing n + 1 queries is to remove the creation of the Pojo object from your query and process the result list, manually creating the objects:
String hql = "SELECT acc, SUM(t.value)"
+ " FROM " + Transaction.class.getName() + " t"
+ " JOIN t.account acc"
+ " GROUP BY acc";
Query query = getEntityManager().createQuery(hql);
List<Pojo> pojoList = new ArrayList<>();
List<Object[]> list = query.getResultList();
for (Object[] result : list)
pojoList.add(new Pojo((Account)result[0], (BigDecimal)result[1]));
Well PostgreSQL (And any other SQL database too) will block you from using mentioned query: you have to group by all columns of account table, not by id. That is why Hibernate generates the query, grouping by ID of the account - That is what is intended to be, and then fetching the other parts. Because it cannot predict in general way, what else will be needed to be joined and grouped(!!!), and in general this could produce situation, when multiple entities with the same ID are fetched (just create a proper query and take a look at execution plan, this will be especially significant when you have OneToMany fields in your Account entity, or any other ManyToOne part of the Account entity) that is why Hibernate behaves this way.
Also, having accounts with mentioned IDs in First level cache, will force Hibernate to pick them up from that. Or IF they are rarely modified entities, you can put them in Second level cache, and hibernate will not make query to database, but rather pick them from Second level cache.
If you need to get those from database in single hint, but not use all the goodness of Hibernate, just go to pure JPA Approach based on Native queries, like this:
#NamedNativeQuery(
name = "Pojo.groupedInfo",
query = "SELECT account.*, SUM(t.value) as sum FROM transactions JOIN accounts on transactions.account_id = accounts.id GROUP BY account.id, account.etc ...",
resultClass = Pojo.class,
resultSetMapping = "Pojo.groupedInfo")
#SqlResultSetMapping(
name = "Pojo.groupedInfo",
classes = {
#ConstructorResult(
targetClass = Pojo.class,
columns = {
#ColumnResult(name = "sum", type = BigDecimal.class),
/*
* Mappings for Account part of entity.
*/
}
)
}
)
public class Pojo implements Serializable {
private BigDecimal sum;
/* .... */
public Pojo(BigDecimal sum, ...) {}
/* .... */
}
For sure this will work for you well, unless you will use the Account, fetched by this query in other entities. This will make Hibernate "mad" - the "entity", but not fetched by Hibernate...
Interesting, the described behaviour is as if t instances are returned from the actual query and t.account association in the first argument of Pojo constructor is actually navigated on t instances when marshalling results of the query (when creating Pojo instances from the result rows of the query). I am not sure if this is a bug or intended feature for constructor expressions.
But the following form of the query should work (no t.account navigation in the constructor expression, and no join fetch without the owner of the fetched association because it does not make sense to eagerly initialize something that is not actually returned from the query):
SELECT new com.test.Pojo(acc, SUM(t.value))
FROM Transaction t JOIN t.account acc
GROUP BY acc
EDIT
Very good observation by Ilya Dyoshin about the group by clause; I completely oversaw it here. To stay in the HQL world, you could simply preload all accounts with transactions before executing the query with grouping:
SELECT acc FROM Account acc
WHERE acc.id in (SELECT t.account.id FROM Transaction t)

JPQL In clause error - Statement too complex

Following is the code which is blowing up if the list which is being passed in to "IN" clause has several values. In my case the count is 1400 values. Also the customer table has several thousands (arround 100,000) of records in it. The query is executing against DERBY database.
public List<Customer> getCustomersNotIn(String custType, List<Int> customersIDs) {
TypedQuery<Customer> query = em.createQuery("from Customer where type=:custType and customerId not in (:customersIDs)", Customer.class);
query.setParameter("custType", custType);
query.setParameter("customersIDs", customersIDs);
List<Customer> customerList = query.getResultList();
return customerList;
}
The above mentioned method perfectly executes if the list has less values ( probably less than 1000 ), if the list customersIDs has more values since the in clause executes based on it, it throws an error saying "Statement too complex"
Since i am new to JPA can any one please tell me how to write the above mention function in the way described below.. * PLEASE READ COMMENTS IN CODE *
public List<Customer> getCustomersNotIn(String custType, List<Int> customersIDs) {
// CREATE A IN-MEMORY TEMP TABLE HERE...
// INSERT ALL VALUES FROM customerIDs collection into temp table
// Change following query to get all customers EXCEPT THOSE IN TEMP TABLE
TypedQuery<Customer> query = em.createQuery("from Customer where type=:custType and customerId not in (:customersIDs)", Customer.class);
query.setParameter("custType", custType);
query.setParameter("customersIDs", customersIDs);
List<Customer> customerList = query.getResultList();
// REMOVE THE TEMP TABLE FROM MEMORY
return customerList;
}
The Derby IN clause support does have a limit on the number of values that can be supplied in the IN clause.
The limit is related to an underlying limitation in the size of a single function in the Java bytecode format; Derby currently implements IN clause execution by generating Java bytecode to evaluate the IN clause, and if the generated bytecode would exceed the JVM's basic limitations, Derby throws the "statement too complex" error.
There have been discussions about ways to fix this, for example see:
DERBY-6784
DERBY-6301, or
DERBY-216
But for now, your best approach is probably to find a way to express your query without generating such a large and complex IN clause.
Ok here is my solution that worked for me. I could not change the part generating the customerList since it is not possible for me, so the solution has to be from within this method. Bryan your explination was the best one, i am still confuse how "in" clause worked perfectly with table. Please see below solution.
public List<Customer> getCustomersNotIn(String custType, List<Int> customersIDs) {
// INSERT customerIds INTO TEMP TABLE
storeCustomerIdsIntoTempTable(customersIDs)
// I AM NOT SURE HOW BUT, "not in" CLAUSE WORKED INCASE OF TABLE BUT DID'T WORK WHILE PASSING LIST VALUES.
TypedQuery<Customer> query = em.createQuery("select c from Customer c where c.customerType=:custType and c.customerId not in (select customerId from TempCustomer)");
query.setParameter("custType", custType);
List<Customer> customerList = query.getResultList();
// REMOVE THE DATA FROM TEMP TABLE
deleteCustomerIdsFromTempTable()
return customerList;
}
private void storeCustomerIdsIntoTempTable(List<Int> customersIDs){
// I ENDED UP CREATING TEMP PHYSICAL TABLE, INSTEAD OF JUST IN MEMORY TABLE
TempCustomer tempCustomer = null;
try{
tempCustomerDao.deleteAll();
for (Int customerId : customersIDs) {
tempCustomer = new TempCustomer();
tempCustomer.customerId=customerId;
tempCustomerDao.save(tempCustomer);
}
}catch(Exception e){
// Do logging here
}
}
private void deleteCustomerIdsFromTempTable(){
try{
// Delete all data from TempCustomer table to start fresh
int deletedCount= tempCustomerDao.deleteAll();
LOGGER.debug("{} customers deleted from temp table", deletedCount);
}catch(Exception e){
// Do logging here
}
}
JPA and the underlying Hibernate simply translate it into a normal JDBC-understood query. You wouldn't write a query with 1400 elements in the IN clause manually, would you? There is no magic. Whatever doesn't work in normal SQL, wouldn't in JPQL.
I am not sure how you get that list (most likely from another query) before you call that method. Your best option would be joining those tables on the criteria used to get those IDs. Generally you want to execute correlated filters like that in one query/transaction which means one method instead of passing long lists around.
I also noticed your customerId is double - a poor choice for a PK. Typically people use long (autoincremented/sequenced, etc.) And I don't get the "temp table" logic.

Get Record ID in Entity Framework 5 after insert

I realize this must be a relatively simple thing to do, but I'm not getting what I'm looking for with Google.
I need to get the record ID of the record I just saved using the Entity Framework. With SQL queries we used "Select ##IDENTITY as 'Identity';"
If anyone can help it would be greatly appreciated.
The default behavior of Entity Framework is it sets identity fields on entities from the database right after SaveChanges is called.
In the following sample code, before SaveChanges is called, my employee has a default ID of 0. After SaveChanges my employee has a generated ID of 1.
using (TestDbEntities context = new TestDbEntities())
{
Employee e = new Employee ();
e.FirstName = "John";
e.LastName = "Doe";
context.Employee.Add(e);
context.SaveChanges();
Console.WriteLine("Generated ID: {0}", e.ID);
Console.ReadKey();
}

Entity Framework Access multiple select statements

my stored procedure returns 3 select statements. using entity framework how can access the particular select statement.
In ADO.Net using "DATASET" we can access particular table. like
DataSet ds = GetApplicationSummary(appId);
DataTable dt = ds.Tables[0];
now we can access the Table[0] of data. same as next two tables.
Using EntityFramework how can access 3 select statements data. please can any help this.
You can access multiple result sets by GetNextResult. Here's a code sample from Microsoft.
using (var db = new BlogEntities())
{
var results = db.GetAllBlogsAndPosts();
foreach (var result in results)
{
Console.WriteLine("Blog: " + result.Name);
}
var posts = results.GetNextResult<Post>();
foreach (var result in posts)
{
Console.WriteLine("Post: " + result.Title);
}
Console.ReadLine();
}
Check out this by Microsoft for more information.