What I am trying to understand is how to call a stored procedure using a DbContext and return an object that is not an entity, OK let me explain a little bit more in case I am not getting the terminology correct
I have a C# Code First Data Context, it has the entity models and mapping models for the tables in the database. I also have about ten stored procedures that return reporting data, none of these stored procedure map to any of the entities I have.
What I would like to do is create a class which represents a row generated by the stored procedure and some how call its corresponding stored procedure with parameters and get a List out.
I have tried this asan attempted to understand what is happening with a simple stored procedure returning 5 rows of data and only two columns
public class ReportA
{
public string Amount { get; set; }
public string Name { get; set; }
}
private MyContext _context = new MyContext();
public List<ReportA> GetReportA(DateTime startDate)
{
var parameters = new[]
{
new SqlParameter("#Startdate", SqlDbType.DateTime)
{
IsNullable = false,
Value = startDate
}
};
var result = _context.Database.SqlQuery<List<reportA>>(
"dbo.ReportA #Startdate", parameters);
return result
}
it fires but I get nothing back. Any advice or help would be appreciated
You should use the following code:
var result = _context.Database.SqlQuery<ReportA>(
"dbo.ReportA #Startdate", parameters);
instead of this code:
var result = _context.Database.SqlQuery<List<ReportA>>(
"dbo.ReportA #Startdate", parameters);
Notice I'm using SqlQuery<ReportA> and not SqlQuery<List<ReportA>>. The template of SqlQuery method should be the type that the entities will be mapped and not the type of your result.
Related
What I am trying to do is read a database, row by row, and use the data from each row to initialize an object of the type that data represents. In this case I am reading rows of the Device table and trying to create Device objects with that data. I saw this SO link:
and I tried this snippet:
using(var dc = new DataContext(connectionString))
{
List<Person> people = dc.ExecuteQuery(#"
SELECT Id, Name Address
FROM [People]
WHERE [Name] = {0}", name).ToList(); // some LINQ too
}
But it is telling me
The type arguments for this usage cannot be inferred from the usage
Is this in principal correct or should I be using the BondIO serializer/deserializer? as mentioned here
Also the order of the members in the object may not be the same as the order of the columns in the database, is this relevant?
Later that same day....
I now have a DBContext with all my database objects defined like this:
public class MyContext : DBContext
{
public dbSet<Device>{ get; set;}
etc...
}
And I now try to get object using this snippet:
using (var db = new MyContext(ConnectionString))
{
var res = db.Device.Find(ID);
}
However this gives an exception message
Could not load type 'System.Data.Entity.ModelConfiguration.Conventions.AttributeToColumnAnnotationConvention`2
I have checked the database and it should return 1 value based on the PrimaryKey ID that I am passing. Anybody have any hints what I'm still doing wrong.
You cannot, because ExecuteQuery is for executing statements, not for querying database. You should use SqlQuery instead
What you can do is, to create a new class with the properties you want to set in your query, means a simplified version of your query. In your case
public class Device
{
public int Id {get;set}
public string Name {get;set}
public string Address {get;set}
}
then use it as
var people = dc.ExecuteQuery<Device>(#"
SELECT Id, Name Address
FROM [People]
WHERE [Name] = {0}", name).ToList();
Actually, My project have a Entity Framework and i have a list of tables each one have different columns like one table have 3 columns and another one have 10 columns. When ever i send table name to query need to get values from database as dynamically. I have used below method to get values as dynamic,
public dynamic GetTableValuesUsingAnalysisData(AnalysisQueries queryName)
{
dynamic result = null;
try
{
using (var cases = new ApiRassiEntities())
{
result = cases.Database.SqlQuery<dynamic>(sqlQuery).ToList();
}
}
return result;
}
public class AnalysisQueries
{
public string QueryName { get; set; }
}
Above method is working correctly but the results showed as object in attached image.
I have tried lot of links but nothing helped me. Please give your suggestion.
Thanks..
I have an ASP.NET MVC site and I want to display some details from a stored procedure. I have done the exact same thing before with other stored procedures on different pages in the same app and it has worked, but this stored procedure call returns a null object instead of the expected value.
EF call to stored procedure:
public MostRecentOrderDetail GetMostRecentOrder(int userId)
{
var context = new myDatabaseContext();
var paramUserId = new SqlParameter { ParameterName = "viUserId", Value = userId };
var result = context.Database.SqlQuery<MostRecentOrderDetail>("usp#GetMostRecentOrder #viUserId", paramUserId).FirstOrDefault();
return result;
}
In my controller I call it using:
MostRecentOrderDetail latestOrder = myDB.GetMostRecentOrder(CurrentUser.UserId).FirstOrDefault();
When I call the stored procedure in SQL Server Management Studio it returns a single row table populated with the correct values, I just cannot seem to get my ASP.NET MVC site to see it.
The MVC site uses code-first, and the MostRecentOrderDetail object is correctly mapped to the columns of the stored procedure.
*EDIT
I have updated my code as #DanielDrews suggested below, and am now receiving an exception, which I think is a step forward:
The data reader is incompatible with the specified 'MyApp.Models.MostRecentOrderDetail'. A member of the type, 'OrderTypeCode', does not have a corresponding column in the data reader with the same name.
I believe my model is being mapped correctly.
myDatabaseContext.cs
public partial class myDatabaseContext : DbContext
{
// Database Initializer etc
public IDbSet<MostRecentOrderDetail> MostRecentOrderDetails { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// ... Other Maps
modelBuilder.Configurations.Add(new MostRecentOrderMap());
}
// GetMostRecentOrder() function from above
}
MostRecentOrderMap.cs
public class MostRecentOrderMap : EntityTypeConfiguration<MostRecentOrderDetail>
{
public MostRecentOrderMap()
{
// Table and Column Mappings
this.ToTable("usp#GetMostRecentOrder");
this.Property(t => t.OrderTypeCode).HasColumnName("order_type_code");
this.Property(t => t.OrderStatusCode).HasColumnName("order_status_code");
this.Property(t => t.OrderReceivedDate).HasColumnName("order_received_date");
this.Property(t => t.OrderShippedDate).HasColumnName("order_shipped_date");
}
}
MostRecentOrderDetail.cs
public class MostRecentOrderDetail
{
[Key]
public string OrderTypeCode { get; set; }
public string OrderStatusCode { get; set; }
public DateTime OrderReceivedDate { get; set; }
public DateTime OrderShippedDate { get; set; }
}
The stored procedure itself:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[usp#GetMostRecentOrder]
#viUserId int
AS
begin
SELECT TOP 1 o.order_type_code,
o.order_status_code,
o.order_received_date,
o.order_shipped_date
FROM ORDER_TABLE o
WHERE o.user_id = #viUserId
end
This could be a problem with the parameter viUserId (value is correct?), try to initialize like this:
var paramUserId = new SqlParameter { ParameterName = "viUserId", Value = userId };
in my applications i've call procedures a little bit diferent. Translating to your problems should be like that:
var paramUserId = new SqlParameter { ParameterName = "viUserId", Value = userId };
var result = this.DbContext.Database.SqlQuery<MostRecentOrderDetail>("usp#GetMostRecentOrder #viUserId", paramUserId).FirstOrDefault();
And one last thing. be sure they have the same types (mapping and database). this could lead to an exception
*EDIT
The procedure need to return all properties from your model (the same name). procedure returns 'order_type_code' but model expects OrderTypeCode.
There is a typo in your parameter code. Your missing # in "viUserId"
UserID = new SqlParameter("#viUserId", userId);
I am trying to execute a stored procedure this way:
var filterValues= context.Database.SqlQuery<FilterProcedureDTO>(
"[dbo].[sp_GetFilterValues] #FieldID", new SqlParameter("FieldID", filterID))
.ToList();
the issue is the filter values that come up have diffrent column name with each call as the user changes the filter on the view,though all come up as objects with an int column and string column,it seems they are bound to the specified model ie FilterProcedureDTO..which looks like
public class FilterProcedureDTO
{
public FilterProcedureDTO() {
//empty constructor
}
public int production_lineID { get; set; }
public string production_line_desc { get; set; }
}
so if the call produces taskID and task_desc this wont work anymore.
Another thing is some IDs are int32 and some are int16 so the code is not executing perfectly because of periodic exceptions
How can I get the stored procedure to return generic objects,just recognising the datatypes and not variable names too?
The SQLQuery method always attempts the column-to-property matching based on property name.
So you need to change your stored procedure so that it always returns the same name and datatype. You should be able to do that using aliases and casting in the sql.
Reference:
https://stackoverflow.com/a/9987939/150342
I am working on an existing application the uses the Generic Repo pattern and EF6 database first.
I am calling a stored proc that returns a complex type that is not an existing entity in my entity models and therefore I am not sure what type to give.
This is how my sp is being called from my service layer
_unitOfWork.Repository<Model>()
.SqlQuery("sp_Get #FromDateTime, #ToDateTime, #CountyId",
new SqlParameter("FromDateTime", SqlDbType.DateTime) { Value = Request.FromDateTime },
new SqlParameter("ToDateTime", SqlDbType.DateTime) { Value = Request.TripToDateTime },
new SqlParameter("CountyId", SqlDbType.Int) { Value = Convert.ToInt32(Request.County) }
).ToList();
Do I create an Entity in my data layer to map to or what is the best approach for stored procedures returning complex types.
If so is there custom mapping needed or is it just a case of creating the Entity class
thank you
If you have an entity with those fields you can call SqlQuery method as you show above, if not, then I suggest creating a new class to map the result:
public class Result
{
public int CountyId { get; set; }
public DateTime FromDateTime { get; set; }
public DateTime ToDateTime { get; set; }
}
I don't know how is implemented the UnitOfWork pattern in your case, but I assume that you have access to your Context. In your UnitOfWork class you could create a generic method like this:
public class UnitOfWork
{
private YourContext Context { get; set; }
public DbRawSqlQuery<T> SQLQuery<T>(string sql, params object[] parameters)
{
return Context.Database.SqlQuery<T>(sql, parameters);
}
}
This way, you can execute your store procedures as I show below:
var result= _unitOfWork.SqlQuery<Result>("sp_Get #FromDateTime, #ToDateTime, #CountyId",
new SqlParameter("FromDateTime", SqlDbType.DateTime) { Value = Request.FromDateTime },
new SqlParameter("ToDateTime", SqlDbType.DateTime) { Value = Request.TripToDateTime },
new SqlParameter("CountyId", SqlDbType.Int) { Value = Convert.ToInt32(Request.County) }
).ToList();
The purpose of the Repository Pattern is to abstract away the storage & retrieval of data to protect your client code e.g. business layer (service layer in your case) from needing to know anything about how data is persisted. SQL statements, for example, would only exist inside your Repository classes, and not ripple throughout your code.
If you expose SQL, Stored Procedure names and parameters to your client code your are not getting much benefit from the Repository Pattern, and if fact you can't really call it a Repository at all. You lose the benefit of being able to mock the repository and test your business layer independently of your data access layer. This means integration tests (requiring a full database instance) are required to verify business logic.
Consider re-factoring so that you have a CountryRepository class which has a GetCountry(int CountryId, DateTime fromDate, DateTime toDate) method that returns a Country entity, or similar. I think you'll agree the readability of your code will be much improved compared to the code in your question.
public class CountryRepository
{
public Country GetCountry(int CountryId, DateTime fromDate, DateTime toDate)
{
// EF or ADO.NET code here
}
}
Client code would then be e.g.
var c = unitOfWork.CountryRepository.GetCountry(1, DateTime.Now.AddYears(-1), DateTime.Now);
See also this SO question
public virtual IEnumerable<T> GetWithRawSql(string query, params object[] parameters)
{
return DbSet.SqlQuery(query, parameters).ToList();
}
Interface
IEnumerable<T> GetWithRawSql(string query, params object[] parameters);
IQueryable<Cm_Customer> customerQuery = _uow.SqlQuery<Cm_Customer>(#" DECLARE #UserId INT = {0}
EXEC Cm_GetCustomersByUserId #UserId", filter.UserId).AsQueryable();
IQueryable<Cm_Customer> custs = customerQuery.IncludeMultiple(k => k.Cm_CustomerLocations,
k => k.Cm_CustomerSalesmans,
k => k.Cm_CustomerMachineparks,
k => k.Cm_CustomerAuthenticators,
k => k.Cm_CustomerInterviews,
k => k.Cm_CustomerRequest,
k => k.Cm_MachineparkRental).AsQueryable();