Get return value from ExecuteSqlRaw in EF Core - entity-framework

I have an extremely large table that I'm trying to get the number of rows for. Using COUNT(*) is too slow, so I want to run this query using EF Core:
int count = _dbContext.Database.ExecuteSqlRaw(
"SELECT Total_Rows = SUM(st.row_count) " +
"FROM sys.dm_db_partition_stats st " +
"WHERE object_name(object_id) = 'MyLargeTable' AND(index_id < 2)");
The only problem is that the return value isn't the result of the query, but the number of records returned, which is just 1
Is there a way to get the correct value here, or will I need to use a different method?

Since you only need a scalar value you can also use an output parameter to retrieve the data, eg
var sql = #"
SELECT #Total_Rows = SUM(st.row_count)
FROM sys.dm_db_partition_stats st
WHERE object_name(object_id) = 'MyLargeTable' AND(index_id < 2)
";
var pTotalRows = new SqlParameter("#Total_Rows", System.Data.SqlDbType.BigInt);
pTotalRows.Direction = System.Data.ParameterDirection.Output;
db.Database.ExecuteSqlRaw(sql, pTotalRows);
var totalRos = (long?)(pTotalRows.Value == DBNull.Value ? null:pTotalRows.Value) ;

If one let's me to recreate a correct answer based on this blog: https://erikej.github.io/efcore/2020/05/26/ef-core-fromsql-scalar.html
We need to create a virtual entity model for our database, that will contain our needed query result, at the same time we need a pseudo DbSet<this virtual model> to use ef core FromSqlRaw method that returns data instead of ExecuteSqlRaw that just returns numbers of rows affected by query.
The example is for returning an integer value, but you can easily adapt it:
Define a return value holding class
public class IntReturn
{
public int Value { get; set; }
}
Fake a virtual DbSet<IntReturn> it will not be really present in db:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
...
modelBuilder.Entity<IntReturn>().HasNoKey();
base.OnModelCreating(modelBuilder);
}
Now we can call FromSqlRaw for this virtual set. In this example the calling method is inside MyContext:DbContext, you'd need to instantiate your own context to use it instead of this):
NOTE the usage of "as Value" - same name as IntReturn.Value property. In some wierd cases you'd have to do the opposite: name your virtual model property after the name of the value thre database funstion is returning.
public int ReserveNextCustomerId()
{
var sql = $"Select nextval(pg_get_serial_sequence('\"Customers\"', 'Id')) as Value;";
var i = this.Set<IntReturn>()
.FromSqlRaw(sql)
.AsEnumerable()
.First().Value;
return i;
}

Related

Update integers (or numbers) by difference in EF Core

Does EntityFramework Core provide a way to create an update query in such way, it will create an UPDATE that calculates the change of an numeric column instead of an absolute value?
Context about the question
Consider the following DbContext:
public class MyValue {
public int Id {get;set;}
public int Value {get;set;}
}
public class MyContext : DbContext {
public DbSet<MyValue> Values {get;set;}
}
A simple command could do the following:
var newValue = new MyValue { Value = 1 };
_dbContext.Add(newValue);
_dbContext.SaveChanges();
var value = _dbContext.Values.First();
value = value + 5;
_dbContext.SaveChanges();
The resulting statement that'll be send to the database during SaveChanges()
Will be something like this:
UPDATE SET [Value] = 6 WHERE ID = 1
But I'd like it to formulate an update statement like this:
UPDATE SET [Value] = [Value] + 5 WHERE ID = 1
WHY?
If an update would be created like the second one, it'd not be neccesary for me to take precautions about concurrency. The database should be able to handle that for me.
I am aware, that I could create an StoredProcedure handling the update that way, but that seems not neccesary, if EFCore would just split the Update commands, making (all) numeric columns update by difference.
So - is there an integrated way to do that, or is there a feasible way to implement it during SaveChanges()?

Linq to select top 1 related entity

How can I include a related entity, but only select the top 1?
public EntityFramework.Member Get(string userName)
{
var query = from member in context.Members
.Include(member => member.Renewals)
where member.UserName == userName
select member;
return query.SingleOrDefault();
}
According to MSDN:
"Note that it is not currently possible to filter which related entities are loaded. Include will always bring in all related entities."
http://msdn.microsoft.com/en-us/data/jj574232
There is also a uservoice item for this functionality:
http://data.uservoice.com/forums/72025-entity-framework-feature-suggestions/suggestions/1015345-allow-filtering-for-include-extension-method
The approach to use an anonymous object works, even though it's not clean as you wish it would be:
public Member GetMember(string username)
{
var result = (from m in db.Members
where m.Username == username
select new
{
Member = m,
FirstRenewal = m.Renewals.FirstOrDefault()
}).AsEnumerable().Select(r => r.Member).FirstOrDefault();
return result;
}
The FirstRenewal property is used just to make EF6 load the first renewal into the Member object. As a result the Member returned from the GetMember() method contains only the first renewal.
This code generates a single Query to the DB, so maybe it's good enough for You.

Group By Multiple Columns dynamically

Is there any way how to Group By multiple columns dynamically?
Eg. group x by new { x.Column1, x.Column2 }
but the x.Column1 etc. I want to set dynamically (from UI)
The way to achieve this dynamically on db site is quite complicated as we cannot dynamically create anonymous types. To replace them I would suggest to create a class:
public class CustomTuple<T1, T2>
{
public T1 Item1 { get; set; }
public T2 Item2 { get; set; }
}
We cannot use Tuple here as it does not have default constructor. In CustomTuple class place as much parameters T and as much properties as you would need at max. If you will define in that class 5 properties but for the query you will use only 3 you just set only 3 properties to proper values and the remaining 2 properties you keep null - the query will still work. Alternatively you may dynamically at run time generate proper class with CodeDOM. Then comes query logic:
Type[] parameterTypes = new Type[] { typeof(int), typeof(object) };
Type tupleType = typeof(CustomTuple<,>).MakeGenericType(parameterTypes);
ParameterExpression x = Expression.Parameter(typeof(Entity));
NewExpression body = Expression.New(tupleType.GetConstructor(new Type[0]), new Expression[0]);
MemberBinding binding1 = Expression.Bind(
typeof(CustomTuple<,>).MakeGenericType(parameterTypes).GetProperty("Item1"),
Expression.Property(x, "Value"));
MemberInitExpression memberInitExpression =
Expression.MemberInit(
body,
binding1);
Expression<Func<Entity, object>> exp = Expression.Lambda<Func<Entity, object>>(memberInitExpression, x);
using (MyDbContext context = new MyDbContext())
{
var list = context.Entities.GroupBy(exp).ToList();
}
The above code groups Entities by Value property. parameterTypes may be dynamically build during program execution - this is list of types of properties anonymous type for key selection in group by would have. Basing on that we create proper CustomTuple type. Then we dynamically create at run time binding1 elements - one per each property to be set for grouping key. In the example above I create only one. With use of the NewExpression and MemberBinding expression we may build initialization expression with MemberInit method. Finally you build lambda expression from that and execute it against db.

How do I get Optimistic Concurrency to fail with detached objects in EF?

Our project is currently migrating to EF(and away from Stored Procs), and one of the enhancements(we're adding to the architecture) is using Optimistic Concurrency when users save data to the database(we currently don't have this feature). I'm having problems getting EF to fail when it should. In other words when two users open the same record, each make changes and attempts to save those changes the first to save update the record, and the second would get an error message. I created a simple example to illustrate my problem.
In the database I have the following table(and insert test data):
Create Table Work
(
Id int identity(1,1) Primary Key
,UserIdAssignTo int null
,RowVer RowVersion not null
)
Insert Into Work(UserIdAssignTo)Values(1)
I created an EF file (.edmx) and drag/drop the table, above, onto the canvas. I updated the properties on the property/column RowVer as follows:
RowVer Property/Column
Concurrency Mode: Fixed
Getter/Setter are both Public
Nullable: False
Store Generated: Computed
Type: Binary
I have an object that will retrieve and update the table like below:
public class Work
{
public int Id { get; set; }
public int? UserIdAssignTo { get; set; }
public byte[] Version { get; set; }
private string _conn = String.Empty;
public WorkData()
{
_conn = GetConnectionsString();
}
public void GetById(int WorkID)
{
using (SQL context = new SQL(_conn))
{
Work fromDb = context.Works.FirstOrDefault(db => db.Id == WorkID);
if (fromDb != null)
{
Id = fromDb.Id;
UserIdAssignTo = fromDb.UserIdAssignTo;
Version = fromDb.RowVer;
}
}
}
public void Update()
{
using (SQL context = new SQL(_conn))
{
Work fromDb = context.Works.FirstOrDefault(db => db.Id == Id);
if (fromDb != null)
{
fromDb.UserIdAssignTo = UserIdAssignTo;
fromDb.RowVer = Version;
context.SaveChanges();
UserIdAssignTo = fromDb.UserIdAssignTo;
Version = fromDb.RowVer;
}
}
}
}
I developed a test case to expose the error I'm getting:
[Test]
public void ConcurencyDataTest()
{
WorkData first = new WorkData();
first.GetById(1);
WorkData second = new WorkData();
second.GetById(1);
first.UserIdAssignTo = null;
first.Update();
second.UserIdAssignTo = 1;
second.Update(); // I should get an exception b/c the object is outdated
}
After both "first" and "second" object call the GetById(1) method, their RowVer property is the same for both objects(as expected).
I ran SQL profiler when I executed this test
The below is when the "first" object called Update method
exec sp_executesql N'update [dbo].[Work]
set [UserIdAssignTo] = null
where (([Id] = #0) and ([RowVer] = #1))
select [RowVer]
from [dbo].[Work]
where ##ROWCOUNT > 0 and [Id] = #0',N'#0 int,#1 binary(8)',#0=1,#1=0x00000000024E6E2
Note the #1 parameter, both the "first" and "second" object should have that in memory and use it when update
When second.Update was called, the SQL profiler recorded this:
exec sp_executesql N'update [dbo].[Work]
set [UserIdAssignTo] = #0
where (([Id] = #1) and ([RowVer] = #2))
select [RowVer]
from [dbo].[Work]
where ##ROWCOUNT > 0 and [Id] = #1',N'#0 int,#1 int,#2 binary(8)',#0=1,#1=1,#2=0x00000000024E6E2F
Note the #1 parameter has changed to the new value(after "first" updated), when it should be the old value that was held by the object "second"(the old value is 0x00000000024E6E2). I don't understand how it got changed and I'm a little confused on how to properly implement first write concurrency through EF.
The results I'm actually getting is the "second" object is successfully updating the table, when it should be failing.
Edit: This to simulate using an N-tier architecture. I'm trying to update with detached objects.
I think it is because in your update method you retrieve the object again from the context which would get the current value of RowVer. Since it is computed I don't think setting it back to the previous version would work. So when it updates it does have the current value of RowVer that is in the table.
I think you instead would need to attach or add the object to the context.
http://msdn.microsoft.com/en-us/library/bb896271.aspx
http://blogs.msdn.com/b/adonet/archive/2011/01/29/using-dbcontext-in-ef-feature-ctp5-part-4-add-attach-and-entity-states.aspx

Entity Framework 4: Selecting Single Record

I'm currently planning on switching my "manual query-writing" code to a nice SQL framework, so I can leave the queries or sql things to the framework, instead of writing the queries myself.
Now I'm wondering how I can get a single record from my table in Entity Framework 4?
I've primarily used SQL like SELECT * FROM {0} WHERE Id = {1}. That doesn't work in EF4, as far as I'm concerned.
Is there a way I can select a single ID-Based record from my Context?
Something like:
public Address GetAddress(int addressId)
{
var result = from Context.Addresses where Address.Id = addressId;
Address adr = result as Address;
return Address;
}
Thank you!
var address = Context.Addresses.First(a => a.Id == addressId);
You can use Single or First methods.
The difference between those methods is that Single expects a single row and throws an exception if it doesn't have a single row.
The usage is the same for both of them
(Based on VS 2015) If you create an .edmx (Add --> ADO.NET Entity Data Model).
Go through the steps to created the ".edmx" and use the following to run the stored procedure. emailAddress is the parameter you are passing to the stored procedure g_getLoginStatus. This will pull the first row into LoginStatus and status is a column in the database:
bool verasity = false;
DBNameEntities db = new DBNameEntities(); // Use name of your DBEntities
var LoginStatus = db.g_getLoginStatus(emailAddress).FirstOrDefault();
if ((LoginStatus != null) && (LoginStatus.status == 1))
{
verasity = true;
}