return custom object from stored procedure executed in EntityFramework, can't see object results - entity-framework

I am trying to execute a stored procedure using Entity Framework. I've tried the below and it returns the correct amount of rows, but when I look at the data in the debug window, it just shows my TYPE, it won't let me drill into to see what the actual values are.
SqlParameter param1 = new SqlParameter("#targetDate", filedate);
var result = db.Database.SqlQuery<PositionSheetCompResults>("dbo.comparePositionSheet #targetDate", param1);
Can anyone tell me how to do this?
here are the results it SHOULD return
here is the class
public class PositionSheetCompResults
{
public string AcctNum { get; set; }

You should write ToList<PositionSheetCompResults>();
var result = db.Database.SqlQuery<PositionSheetCompResults>("dbo.comparePositionSheet #targetDate", param1).ToList<PositionSheetCompResults>();

Related

Get return value from ExecuteSqlRaw in EF Core

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;
}

Stored procedures with ef 6 and code first

I am quite new to EF and MVC. But I have managed to create scaffolded pages with MCV5 and views.
I understand there is difference in concept between webforms and EF. But is it possible for me to execute a stored procedure that is created in SSMS.
I want the ability of have a webpage with two text boxes that take in the two in parameters the stored procedure requires, both of the parameters being strings and then display the result in a webgrid.
Is this doable with EF code first and MVC?
The code for my stored procedure is:
CREATE PROCEDURE [dbo].[sp_FindRoutes]
#DepCity nvarchar(max),
#ArvCity nvarchar(max)
AS
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
SELECT dbo.Airlines.AirlineName, dbo.Routes.DepCity, dbo.Routes.ArvCity, dbo.Routes.FlightNr, dbo.AirlineRoutes.Fare, dbo.AirlineRoutes.AircraftAllocated
FROM dbo.Routes INNER JOIN
dbo.AirlineRoutes ON dbo.Routes.RouteId = dbo.AirlineRoutes.RouteId INNER JOIN
dbo.Airlines ON dbo.AirlineRoutes.AirlineId = dbo.Airlines.AirlineId
WHERE dbo.Routes.DepCity LIKE #DepCity AND dbo.Routes.ArvCity LIKE #ArvCity;
GO
Let's say your SP is like this (Just a simple version of your SP).
CREATE PROCEDURE [dbo].[sp_FindRoutes]
#DepCity string
AS
BEGIN
SET NOCOUNT ON;
SELECT dbo.Airlines.AirlineName, dbo.Routes.DepCity, dbo.Routes.ArvCity
FROM dbo.Routes
WHERE dbo.Routes.DepCity= #DepCity
END
You have to create an object that has the same property names as the results returned by the stored procedure.It's like this.
public class RouteForAirline
{
public string AirlineName { get; set; }
public string DepCity { get; set; }
public string ArvCity { get; set; }
}
Then call your SP as shown below.
Note : You can put this on the class file if you can access the DatabaseContext().
using(var context = new DatabaseContext())
{
var depCityParameter = new SqlParameter("#DepCity", "colombo");
var arvCityParameter = new SqlParameter("#ArvCity", "matara");
var results = context.Database.SqlQuery<RouteForAirline>("sp_FindRoutes #DepCity, #ArvCity",depCityParameter,arvCityParameter).ToList();
}
You can read more about this using below mentioned links.
Code First Insert/Update/Delete Stored Procedures
Stored Procedures with Multiple Result Sets

Filehelpers and Entity Framework

I'm using Filehelpers to parse a very wide, fixed format file and want to be able to take the resulting object and load it into a DB using EF. I'm getting a missing key error when I try to load the object into the DB and when I try and add an Id I get a Filehelpers error. So it seems like either fix breaks the other. I know I can map a Filehelpers object to a POCO object and load that but I'm dealing with dozens (sometimes hundreds of columns) so I would rather not have to go through that hassle.
I'm also open to other suggestions for parsing a fixed width file and loading the results into a DB. One option of course is to use an ETL tool but I'd rather do this in code.
Thanks!
This is the FileHelpers class:
public class AccountBalanceDetail
{
[FieldHidden]
public int Id; // Added to try and get EF to work
[FieldFixedLength(1)]
public string RecordNuber;
[FieldFixedLength(3)]
public string Branch;
// Additional fields below
}
And this is the method that's processing the file:
public static bool ProcessFile()
{
var dir = Properties.Settings.Default.DataDirectory;
var engine = new MultiRecordEngine(typeof(AccountBalanceHeader), typeof(AccountBalanceDetail), typeof(AccountBalanceTrailer));
engine.RecordSelector = new RecordTypeSelector(CustomSelector);
var fileName = dir + "\\MOCK_ACCTBAL_L1500.txt";
var res = engine.ReadFile(fileName);
foreach (var rec in res)
{
var type = rec.GetType();
if (type.Name == "AccountBalanceHeader") continue;
if (type.Name == "AccountBalanceTrailer") continue;
var data = rec as AccountBalanceDetail; // Throws an error if AccountBalanceDetail.Id has a getter and setter
using (var ctx = new ApplicationDbContext())
{
// Throws an error if there is no valid Id on AccountBalanceDetail
// EntityType 'AccountBalanceDetail' has no key defined. Define the key for this EntityType.
ctx.AccountBalanceDetails.Add(data);
ctx.SaveChanges();
}
//Console.WriteLine(rec.ToString());
}
return true;
}
Entity Framework needs the key to be a property, not a field, so you could try declaring it instead as:
public int Id {get; set;}
I suspect FileHelpers might well be confused by the autogenerated backing field, so you might need to do it long form in order to be able to mark the backing field with the [FieldHidden] attribute, i.e.,
[FieldHidden]
private int _Id;
public int Id
{
get { return _Id; }
set { _Id = value; }
}
However, you are trying to use the same class for two unrelated purposes and this is generally bad design. On the one hand AccountBalanceDetail is the spec for the import format. On the other you are also trying to use it to describe the Entity. Instead you should create separate classes and map between the two with a LINQ function or a library like AutoMapper.

Support for Table Valued Functions in EF6 Code First?

Is it possible to call a TVF in EF6 Code First?
I started a new project using EF6 Database first and EF was able to import a TVF into the model and call it just fine.
But updating the model became very time consuming and problematic with the large read-only db with no RI that I'm stuck dealing with.
So I tried to convert to EF6 code first using the Power Tools Reverse Engineering tool to generate a context and model classes.
Unfortunately the Reverse Engineering tool didn't import the TVFs.
Next I tried to copy the DBFunctions from my old Database First DbContext to the new Code First DbContext, but that gave me an error that my TVF:
"cannot be resolved into a valid type or function".
Is it possible to create a code first Fluent mapping for TVFs?
If not, is there a work-around?
I guess I could use SPs instead of TVFs, but was hoping I could use mostly TVFs to deal with the problematic DB I'm stuck with.
Thanks for any work-around ideas
This is now possible. I created a custom model convention which allows using store functions in CodeFirst in EF6.1. The convention is available on NuGet http://www.nuget.org/packages/EntityFramework.CodeFirstStoreFunctions. Here is the link to the blogpost containing all the details: http://blog.3d-logic.com/2014/04/09/support-for-store-functions-tvfs-and-stored-procs-in-entity-framework-6-1/
[Tested]
using:
Install-Package EntityFramework.CodeFirstStoreFunctions
Declare a class for output result:
public class MyCustomObject
{
[Key]
public int Id { get; set; }
public int Rank { get; set; }
}
Create a method in your DbContext class
[DbFunction("MyContextType", "SearchSomething")]
public virtual IQueryable<MyCustomObject> SearchSomething(string keywords)
{
var keywordsParam = new ObjectParameter("keywords", typeof(string))
{
Value = keywords
};
return (this as IObjectContextAdapter).ObjectContext
.CreateQuery<MyCustomObject>(
"MyContextType.SearchSomething(#keywords)", keywordsParam);
}
Add
public DbSet<MyCustomObject> SearchResults { get; set; }
to your DbContext class
Add in the overriden OnModelCreating method:
modelBuilder.Conventions.Add(new FunctionsConvention<MyContextType>("dbo"));
And now you can call/join with
a table values function like this:
CREATE FUNCTION SearchSomething
(
#keywords nvarchar(4000)
)
RETURNS TABLE
AS
RETURN
(SELECT KEY_TBL.RANK AS Rank, Id
FROM MyTable
LEFT JOIN freetexttable(MyTable , ([MyColumn1],[MyColumn2]), #keywords) AS KEY_TBL
ON MyTable.Id = KEY_TBL.[KEY]
WHERE KEY_TBL.RANK > 0
)
GO
I was able to access TVF with the code below. This works in EF6. The model property names have to match the database column names.
List<MyModel> data =
db.Database.SqlQuery<MyModel>(
"select * from dbo.my_function(#p1, #p2, #p3)",
new SqlParameter("#p1", new System.DateTime(2015,1,1)),
new SqlParameter("#p2", new System.DateTime(2015, 8, 1)),
new SqlParameter("#p3", 12))
.ToList();
I actually started looking into it in EF6.1 and have something that is working on nightly builds. Check this and this out.
I have developed a library for this functionality. You can review my article on
UserTableFunctionCodeFirst.
You can use your function without writing SQL query.
Update
First of all you have to add reference to the above mentioned library and then you have to create parameter class for your function. This class can contain any number and type of parameter
public class TestFunctionParams
{
[CodeFunctionAttributes.FunctionOrder(1)]
[CodeFunctionAttributes.Name("id")]
[CodeFunctionAttributes.ParameterType(System.Data.SqlDbType.Int)]
public int Id { get; set; }
}
Now you have to add following property in your DbContext to call function and map to the property.
[CodeFunctionAttributes.Schema("dbo")] // This is optional as it is set as dbo as default if not provided.
[CodeFunctionAttributes.Name("ufn_MyFunction")] // Name of function in database.
[CodeFunctionAttributes.ReturnTypes(typeof(Customer))]
public TableValueFunction<TestFunctionParams> CustomerFunction { get; set; }
Then you can call your function as below.
using (var db = new DataContext())
{
var funcParams = new TestFunctionParams() { Id = 1 };
var entity = db.CustomerFunction.ExecuteFunction(funcParams).ToList<Customer>();
}
This will call your user defined function and map to the entity.

how to avoid creating entities for returning small data from database

Oftentimes I need to return a list of two or three columns from a stored proc in the data layer. I usually use entities to push data back to the business/UI layer. However, I don't want to create an entity for something that is not really an entity.
Lets say I have to return a set of "date, string, string", is there any other way to do this.. for two columns, i can perhaps get away with a dictionary. I always end up creating a dummy entity but thought I'll ask this time.
Create a new model to store the data:
public class ResultModel
{
public DateTime DateTime1 { get; set; }
public string String1 { get; set; }
public string String1 { get; set; }
}
then just store your data into this like so:
IEnumerable<ResultModel> results =
Entities.Table.Select(r => new ResultModel
{
DateTime1 = r.DateTime,
String1 = r.String1,
String2 = r.String2
});
Not verified the code but is a guide as to how to achieve this.
You could probably use a Tuple for this kind of data however I've never found any real detriment to creating more understandable types, even for simple data.