Stored Procedure Multiple-Table Output With LINQ and ASP.NET MVC - asp.net-mvc

Quite often our systems call stored procedures which output multiple tables worth of results. Previously we used XML outputs to get each table and relate them correctly using XSLT. If I were using ASP.NET MVC with LINQ calling a stored procedure, how do I get each of the tables and then output the data as necessary?

this article here explains everything. This is the same article which i linked, in your previous SO question.

There's an article here about LINQ to SQL and stored procedures, especially the section 'Handling Multiple Result Shapes from SPROCs':
LINQ to SQL - Retrieving Data Using Stored Procedures.
Is that useful in your case?
Otherwise, not using LINQ to SQL, maybe use SqlDataReader's NextResult to go through the results, for example:
IList<Employee> employees = new List<Employee>();
IList<Customer> customers = new List<Customer>();
using (SqlConnection connection = new SqlConnection
(Properties.Settings.Default.NorthwindConnectionString))
using (SqlCommand command = new SqlCommand
("GetEmployeesAndCustomers", connection))
{
command.CommandType = CommandType.StoredProcedure;
connection.Open();
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
Employee e = new Employee{EmployeeID = (int)reader["EmployeeID"]};
employees.Add(e);
}
reader.NextResult();
while (reader.Read())
{
Customer c = new Customer{CustomerID = (string)reader["CustomerID"]};
customers.Add(c);
}
}
}
Edit: Example of how to handle custom data combinations that are not easily fit into domain model objects; in this case retrieving orders along with the customers for the orders:
namespace Company.Application.ViewModel
{
public class CustomerOrder
{
public string CustomerID { get; set; }
public string CustomerName { get; set; }
public int OrderID { get; set; }
public DateTime? OrderDate { get; set; }
}
}
namespace Company.Application.Repository
{
public class CustomerOrderRepository
{
public IList<CustomerOrder> GetCustomerOrders()
{
NorthwindDataContext db = new NorthwindDataContext();
var custorders = from customer in db.Customers
join order in db.Orders
on customer.CustomerID equals order.CustomerID
select new CustomerOrder
{
CustomerID = customer.CustomerID,
CustomerName = customer.CompanyName,
OrderID = order.OrderID,
OrderDate = order.OrderDate
};
return custorders.ToList();
}
}
}
Inspiration for this: In the chapter about NerdDinner, Scott Guthrie talks about creating custom 'ViewModel' objects to hold data from for example joins that are not easily fit into the domain model objects.

Related

How to execute SQL query in a new database?

My problem is like this... I'm creating a software where you have different areas of a business and those areas have a property name ConnectionString.
So when the administrator of the system wants to know certain specific information of the area all he does is register a SQL query.
My problem is in the logic of how to do execute the query. I'm creating a new DbContext and pass the connection string but I can't find a function of DbContext to run the query.
Does someone know any function of DbContext to run a query and get the result?
If you require to execute raw query in Entity Framework Core using DbContext object you can access like
For access raw query
var students = context.Students
.FromSql("SELECT * FROM dbo.Students Where id = 1")
.ToList();
If your requirement is access or get data from a stored procedure:
var students = context.Students
.FromSql("EXECUTE dbo.GetTopperStudents")
.ToList();
if passing with parameter then
var name = new SqlParameter("name", "abc");
var students = context.Students
.FromSql("EXECUTE dbo.GetTopperStudents #name",name )
.ToList();
if execute command insert/update/delete then
var commandText = "INSERT Students (name) VALUES (#name)";
var name = new SqlParameter("#name", "Test");
context.Database.ExecuteSqlCommand(commandText, name);
if you are execute query or stored procedure in not your model or you add custom model then you can execute as query like
my studentFees Model is not any model related to database.
public class StudentFees
{
public int StudentId { get; set; }
public string StudentName { get; set; }
public decimal Fees { get; set; }
public DateTime FeesDate { get; set; }
}
and just add your query into your DbContext OnModelCreating() method
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Query<StudentFees>();
}
now if you can access and execute your query or your stored procedure in custom model
var studentId = new System.Data.SqlClient.SqlParameter("#studentId", 1);
var studentsFees = _dbContext.Query<StudentFees>.FromSql("GetStudentFees #studentId", studentId).ToList();
in this way you can access or set your custom model also and execute query or stored procedure.
Let me know require more information.

Cannot map LINQ to Entities

I'm not very clear with writing linq queries. I write a query to select only certain columns from a table using linq lambda expression and I get the error that linq cannot be constructed to entities. The same query when I write using linq to select all columns I don't get any errors and I get all the columns, which i later filter out in the view. But I want to use the lambda to select only certain columns.
Code snippet:
ViewModel:
public class StaggingInternalCashExceptionViewModel
{
public OutputCash OutputCash { get; set; }
public IEnumerable<StaggingInternalException> StaggingInternalException { get; set; }
//list of results of Stagginginternalcashexception
}
Controller:
public ActionResult Exceptionstest(string dd1, string dd2, string dd3)
{
StaggingInternalExceptionViewModel _app = new StaggingInternalExceptionViewModel();
_app.StaggingInternalException = db2.StaggingInternalExceptions.Where(x => x.Level1 == dd1 && x.Level2 == dd2 ).Select(i => new StaggingInternalException
{
StaggingInternalRowID = i.StaggingInternalRowID,
Category = i.Category,
EnterText1 = i.EnterText1,
InternalAmount = i.InternalAmount,
ExternalAmount = i.ExternalAmount
});
_app.StaggingInternalException = (from p in db2.StaggingInternalExceptions
where p.LoadID==loadid && p.Level1 == dd1 && p.Level2 == dd2 select p);
}
In the above code, the lambda expression throws an error when I'm trying to select only certain columns from the table or if we are speaking in terms of entity classes, only certain properties. But the query returns all the columns. Should I be using DTOS? I'm not sure what the use of data transfer objects is. Some explanation on this would be great. Thanks.
You need to use a DTO.
A dto is just an object that you map your result to. In your case it would be
public class StaggingInternalExceptionViewModel
{
public int StaggingInternalRowID { get; set; }
public int Category { get; set; }
... //rest of properties
}
You need to change your StaggingInternalCashExceptionViewModel to use the StaggingInternalException DTO
public class StaggingInternalCashExceptionViewModel
{
public OutputCash OutputCash { get; set; }
public IEnumerable<StaggingInternalExceptionViewModel> StaggingInternalException { get; set; }
//list of results of Stagginginternalcashexception
}
Then your expression stays the basically the same but you select a new StaggingInternalExceptionViewModel instead of StaggingInternalException
StaggingInternalExceptionViewModel _app = new StaggingInternalCashExceptionViewModel();
_app.StaggingInternalException = db2.StaggingInternalExceptions.Where(x => x.Level1 == dd1 && x.Level2 == dd2 ).Select(i => new StaggingInternalExceptionViewModel
{
StaggingInternalRowID = i.StaggingInternalRowID,
Category = i.Category,
EnterText1 = i.EnterText1,
InternalAmount = i.InternalAmount,
ExternalAmount = i.ExternalAmount
});
Linq to Entities doesn't let you project a query using an entity type because you can end up losing information at loading an entity partially and trying later to save that entity to your DB. So, you must project your queries when you need partial information of an entity whether using a DTO or an anonymous type.
If you need to use the entity type, then don't project using Select method, the only thing is you're going to load all the properties, but I think this is not the case because you don't need all the data ;).

Asp.net mvc call entity db data using Id for that particular row

Hi and thank you for taking your time to read. I am having trouble calling from a db using entity framework for a particular row. Here is my code for controller.
public ActionResult MyAccount(CurrentAccount ca, SaverAccount sa, int id)
{
var model = db.CurrentAccounts.FirstOrDefault(_ => _.Id == id);
Session["Id"] = ca.Id;
Session["CurrentAccountNumber"] = ca.CurrentAccountNumber;
Session["CurrentBalance"] = ca.CurrentBalance;
Session["SaverAccountNumber"] = sa.SaverAccountNumber;
Session["CurrentBalance"] = sa.SaverAccountNumber;
return View(model);
}
My model is a edmx entity file and i can seem to retrieve some data to my locals but only from 1 table and i need data to be from multiple tables selecting a full row of data for a paricular Id then having this information visable on the same view. There is also a relation between id on both tables. Thanks :)
Here you have called wrong object because you are fetching data in model variable but calling from ca. please use as following
public ActionResult MyAccount(CurrentAccount ca, SaverAccount sa, int id)
{
var model = db.CurrentAccounts.FirstOrDefault(_ => _.Id == id);
Session["Id"] = model.Id;
Session["CurrentAccountNumber"] = model.CurrentAccountNumber;
Session["CurrentBalance"] = model.CurrentBalance;
Session["SaverAccountNumber"] = sa.SaverAccountNumber;
Session["CurrentBalance"] = sa.SaverAccountNumber;
return View(model);
}
You need to execute join query to get data from two models like following exmaple
Create a common class like follwing
public class datafrombothclass
{
public int Id { get; set; }
public String saveaccount_name { get; set; }
public String currrentaccount_name { get; set; }
}
Now use join query in entity framework to get data from both model in you case from CurrentAccount and SaverAccount.
See the bellow code example:
var frombothclass=(from a in Model.saveaccount join s in Model.currentaccountaccount
where a.Id=id
select new datafrombothclass{
Id=a.Id,
saveaccount_name=s.name,
currrentaccount_name=a.name
});
return View(frombothclass);
Hope you will get the solution.

How to create a simple left outer join in lambda expression in EF ASP.NET MVC5

I searched hours and hours for this without any luck. I'm trying to create a lambda expression to fetch data from two tables Schedule and Request. But i'm outputting a bool here. How can i do a proper left outer join to fix this?
this is the best i could come up with
ViewBag.RequestList = db.Requests
.Include(r => r.Department)
.Select(r => db.Schedules.Any(s => s.RequestId == r.RequestId));
but its a bool not a list.
Assume my table models are as follows
public class Request{
public virtual int RequestId { get; set; }
public virtual string Remarks { get; set; }
}
public class Schedule{
public virtual int ScheduleId{ get; set; }
public virtual string Name{ get; set; }
public virtual Request Request { get; set; }
}
I'm trying to see if each and every request has one or more schedules associated with it or not. so if i could attach schedule object to request and output it as a list then thats all i need.
But I want to do it using LINQ and lambda expressions and I've seen queries as below;
var leftList = (from emp in db.Requests
join d in db.Schedules
on emp.RequestId equals d.RequestId into output
from j in output.DefaultIfEmpty()
select new { RequestId = emp.RequestId,
name = emp.Department.Name,
route = emp.Route.Name });
But that's not what i want, because i have to specify every field i need in new { RequestId = emp.RequestId, name = emp.Department.Name, route = emp.Route.Name }
Thanks a lot!
just list what you want like this:
var leftList = from emp in db.Requests
join d in db.Schedules
on emp.RequestId equals d.RequestId into output
from j in output.DefaultIfEmpty()
select new
{
RequestId = emp.RequestId,
name = emp.Department.Name,
route = emp.Route.Name,
ScheduleId=j==null?0:j.ScheduleId,
SName=j==null?""j.Name,
};

Entity Framework ViewModel with Multiple Tables

I am building MVC CRUD pages for the Events table, where I have one to many between table (C) DeviceChannel and table (D) Events. Table (C) is described by a 2 foreign keys to tables (A) Device and (B) Channel. There is a further foreign key to describe the EventType.
The DeviceChannel does not have a description (so I can't use this as the look up for Events directly) though it does have various values associated with it so I have to include the Device and Channel tables.
I have built a ViewModel with the above tables but when I try to create a Controller, I get errors complaining there is no Metadata for the ViewModel. No keys defined between the tables. All the tables have keys and load up correctly in EF database-first.
I am using VS2013 with .Net 4.5.2
I did try using a repository after following the NerdDinners example, without success.
Questions:
Should I be using a ViewModel to develop this solution.
If I use a ViewModel, how do I resolve the metadata/key issue when trying to scafold the controller?
I solved this issue by simplifying the ViewModel.
public class DeviceChannelEventsViewModel
{
public SelectList eventType { get; private set; }
public SelectList deviceChannel { get; private set; }
public CRHSEvent crhsEvent { get; set; }
public DeviceChannelEventsViewModel(SelectList _deviceChannel, SelectList _eventType, CRHSEvent _crhsEvent)
{
deviceChannel = _deviceChannel;
eventType = _eventType;
crhsEvent = _crhsEvent;
}
public DeviceChannelEventsViewModel(SelectList _deviceChannel, SelectList _eventType)
{
deviceChannel = _deviceChannel;
eventType = _eventType;
crhsEvent = new CRHSEvent();
}
}
When I created the selectlist for deviceChannel, I joined the Device and Channel tables to the devicechannel table:
DeviceChannelRepository dcr = new DeviceChannelRepository();
IEnumerable<DeviceChannel> dc = dcr.FindAllDeviceChannelsDescribed();
SelectList deviceChannels = new SelectList(dc.Select(s => new
{
DeviceChannelID = s.DeviceChannelID ,
DCDescription=string.Format("{0} | {1}",s.Device.DeviceName,s.Channel.Description)
}).ToList(), "DeviceChannelID", "DCDescription");
SelectList eventType = new SelectList(db.EventType, "EventTypeID", "Description");
DeviceChannelEventsViewModel dCEViewModel =
new DeviceChannelEventsViewModel(deviceChannels, eventType);

Resources