I am very new to ASP.Net and MVC applications so pardon me if this question has been asked or is trivial.
I know how to create an entity model class from a database table, but I want to perform a series of Joins and create a Pivot table from an SQL query and then pass this to a view.
However, I do not know a quick way to create an entity class for this.
Currently, I am doing it the long way by manually defining a model class like so:
public class OAData
{
public int Zone { get; set; }
public string Device { get; set; }
public string Part { get; set; }
...
//CONSTRUCTOR
public OAData(int zone, string device, string part...){
Zone = zone;
Device = device;
Part = part;
...
}
}
and then create a database connection in the controller, loop through all the records, creating OAData objects for each record, add it to a list and then pass that list to the View.
Is there an easier way to do this (there are many fields returned by the query)? Can I create a model from a complex SQL query rather than just off a database table?
Related
I would like to get help from you, please :-)
I'm thinking about good way for programmatically creating of classes in C# via MySQL database.
In my app I'm creating composite classes. For example Student, Classroom, Room (dormitory) and so on. Class Student contains properties Classroom and Room. ClassRoom is also related to another entities in database...
public class Student
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public Classroom Classroom { get; set; }
public Room Room { get; set; }
}
public class Classroom
{
public int Id { get; set; }
public string Title { get; set; }
public byte Level { get; set; }
public Teacher Teacher { get; set; }
}
...etc
Usually when I need create object of some class, I must create also another classes' objects (sometime a part of database :-) ).
I think this way is not good optimalized, BUT there are great OOP benefits. When I load all students in a DataGridView, I can manage lots of related parts... for example:
Student student = ...
string currentTeacher = student.Classroom.Teacher.LastName //... and so on.
Is OK to create all related classes' objects immediately or is better create only necessary data of current created object and another data load / create "on demand"?
Or absolutely different way? :-)
See, the idea is for you to make a query exactly like what you need an Ingress for a structural database like sql, talking a lot about the EntityFrame mappings where it is possible to query only the student object/table only by its id, however, if no process you will need the Classroom in which it belongs to you use a .Include() no entity and you would only be able to fetch the objects you will need in your request no problem mapping all entities as a bank into objects, the problem is to retrieve all of them from the relation since it only has a feature in some articles
https://learn.microsoft.com/pt-br/ef/ef6/querying/related-data
https://www.entityframeworktutorial.net/code-first/configure-entity-mappings-using-fluent-api.aspx
But if your purpose is performance, mount only the data you need in an object, just an example StudentInfo with the information you need grouped, use a Dapper to make the optimized query and make it do this mapping to your object, gaining performance
this is clear speaking of relational databases like Sql if your case is a NoSql database like MongoDb there is no problem in your mappings since it will return everything in a single document it is structured for this type of information there will be no InnerJoin cost between tables
Is it possible to run Linq-to-SQL queries when underling database structure is changing from time to time (I mean database updates that happens due to business requirements and since database is shared among several apps It may be happens without announcements to me)?
Is there any way that I can connect to new database structure in Linq-to-SQL without updating the .dbml file in my source code?
If I want to run raw queries knowing that my database structure changes during time, can I use any of Linq-to-SQL benefits somehow?
Provided the structure you have in your classes match to your tables (at least covering all the fields you need) you can do that. ie: Northwind customers table have more than 4 fields in reality. Provided below 4 are still in that table this would work:
void Main()
{
DataContext db = new DataContext(#"server=.\SQLexpress;trusted_connection=yes;database=Northwind");
Table<Customer> Customers = db.GetTable<Customer>();
var data = Customers.Where(c => c.Country == "USA");
foreach (var customer in data)
{
Console.WriteLine($"{customer.CustomerID}, {customer.CompanyName}");
}
}
[Table(Name = "Customers")]
public class Customer
{
[Column]
public string CustomerID { get; set; }
[Column]
public string CompanyName { get; set; }
[Column]
public string ContactName { get; set; }
[Column]
public string Country { get; set; }
}
For raw SQL, again you could use a type covering fields in select list or dynamic.
Note: For inserts, for this to work, fields that are not in your model should either accept null or have default values.
I have a sample ASP.net application, and I want to create it using n tier architecture, so I have a data base that contains tables and stored procedures (that perform (CRUD) operations on these tables)
, now when I tried to create the data access layer I created methods that uses ado.net to call these stored procedures,and I have created it but these methods returns datatables like this one:
public DataTable getallcourcesdetailsbyid(string courseid) {
SqlParameter[] parameter = new SqlParameter[] { new SqlParameter("#courseid", courseid) };
return sqlhelper.ExecuteParamerizedSelectCommand("usp_getcoursedetailsbyid", CommandType.StoredProcedure, parameter);
}
So I found that there is better way to create classes with properties that represents tables in the database to hold the data returned by data access layer
like this one:
class course{
public int courseid { get; set; }
public string coursename { get; set; }
public short specializationid { get; set; }
public short subjectid { get; set; }
public short instructorid { get; set; }
public string startdate { get; set; }
public string enddate { get; set; }
public bool isactive { get; set; }
public bool isdeleted { get; set; }
}
But these stored procedures not always returns the data from specific table for example the class course above present the course table in the data base but the method above called "getallcourcesdetailsbyid" call stored procedure with the following code
select courseid,coursename,startdate,enddate,courseimgpath,specialization,firstname,lastname,subjectname,price,coursedetails,teacherimgpath
from joacademytest.course
inner join joacademytest.specialization ON joacademytest.course.specializationid = joacademytest.specialization.specializationid
inner join joacademytest.[subject] on joacademytest.course.subjectnameid=joacademytest.[subject].subjectid
inner join joacademytest.teachers on joacademytest.course.instructerid=joacademytest.teachers.teacherid
inner join dbo.courcesprices on joacademytest.course.priceid=dbo.courcesprices.priceid
where joacademytest.course.isactive=1 and joacademytest.course.isdeleted=0 and courseid = #courseid;
So the stored procedure will not return the same column exists in the course object but it represent columns from 4 joined tables, so do I have to create the entity classes based on the columns existing on the tables or based on the columns returned by my stored procedure. I have searched the Internet and never found any body mentioned that I can create entity classes based on stored procedure returned column which made me confused.
There is nothing wrong with creating classes that map to the results of a store procedure. Infact Many of the ORMs like entity framework, nHibernate allow you to do that. In the end It all depends on what you want to achieve, performance, maintenance etc and these are very broad topics. to answer your question and keeping in mind your current setup, i would propose -
Create Entity that map to store procs if, store procs are the preferred way of getting data from database, Infact so that you dont re-invent the wheel you can use one of the many Orm tools - dapper, EF.
Or instead of creating entities from your datatables , you can return dynamic objects.
hope this helps
What I always do is I create a classes that represents my tables in my database. In a scenario like you describe, where you have a stored procedure that returns data, I move it to a view. Then I create a class that represents that view. If you can't do that and you must keep it in a stored procedure, then I would just create a class around what the stored procedure returns.
It seems this problem would have been encountered before me but I'm not finding much help online probably because I don't really know what to search for.
My problem in short is that I have a db table. That table has 5 keys to other tables.
I then have a model that represents this table in EF. Of course this object that represents the db table has List<T> properties that are representations of foreign keys in the db. That doesn't seem to be the problem as much as the EF model that has this table representation but also List<T> properties to other models.
The problem I am experiencing is that a call to a stored procedure to populate the main modelforces additional calls to the db to populate the related List<T> models.
I am looking to improve performance namely by eliminating the multiple calls.
My only thought to this point is to modify the stored procedure to return multiple recordsets and match each List<T> property to its corresponding recordset.
My sterilized structure is something like this.
DB:
sql_Id Int PK
sql_Status Int FK
sql_Reason Int FK
sql_GuestId Int
sql_Name varchar
sql_Created DateTime
sql_Original Int FK
EF:
public class OrderHeader : ClassBase
{
public OrderHeader()
{
TaskCodeAssignments = new List<OrderHeaderTaskCodeAssignment>();
StatusReasonCode = new OrderHeaderStatusReasonCode();
StatusCode = new OrderHeaderStatusCode();
Links = new OrderHeaderLinks();
}
public int OrderHeaderID { get; set; }
public short OrderHeaderStatusCodeID { get; set; }
public short? OrderHeaderStatusReasonCodeID { get; set; }
public short? OriginatingApplicationId { get; set; }
public string CustomerFirstName { get; set; }
public string CustomerLastName { get; set; }
public OrderHeaderStatusCode StatusCode { get; set; }
public OrderHeaderStatusReasonCode StatusReasonCode { get; set; }
public CustomerStatusCode CustomerStatusCode { get; set; }
public ICollection<OrderHeaderTaskCodeAssignment> TaskCodeAssignments { get; set; }
}
public class OrderHeaderStatusCode
{
public OrderHeaderStatusCode()
{
OrderHeaderStatusReasonCodes = new List<OrderHeaderStatusReasonCode>();
}
public ICollection<OrderHeaderStatusReasonCode> OrderHeaderStatusReasonCodes { get; set; }
public virtual ICollection<OrderHeader> OrderHeader { get; set; }
}
The other custom types like OrderHeaderStatusReasonCode are pretty similar in design so I'm leaving out for brevity.
C# Web API
public async Task<IHttpActionResult>GetOrdersHistory([FromUri]GetOrderRequestParameters orderParams)
{
....removed for brevity....
var query = await TheOrderRepository.GetOrderHistory(getOrder);
}
Order Repository:
public async Task<IQueryable<OrderHeader>> GetOrderHistory(GetOrderParameters orderParams)
{
// this is the call to stored procedure that I would modify to return multiple recordsets
var storedProcedure = StoredProcedure.Name.MyStoredProc.ToString();
var ordersHistory = await dbctx.Database.SqlQuery<OrderHeader>(...), storedProcParam).ToListAsync();
// now I jump off to fill in the other properties and their data has to come from the db
await GetOrdersData(ordersHistory, orderParams.Include);
}
private async Task GetOrdersData(List<OrderHeader> ordersHistory)
{
if (ordersHistory != null)
{
await LoadOrderStatusCodeForList(ordersHistory);
await LoadOrderStatusReasonCodeForList(ordersHistory);
await LoadCustomerStatusCodeForList(ordersHistory);
await LoadOrderHeaderTaskCodeAssignmentsForList(ordersHistory);
await LoadOrderHeaderTaskCodeForList(ordersHistory);
}
}
Again most of these awaits are similar so I'm just going to give an example of one...
private async Task LoadOrderStatusCodeForList()
{
....snipped for brevity...
await LoadOrderStatusCode(order.OrderHeaderStatusCodeID));
}
private async Task<OrderHeaderStatusCode> LoadOrderStatusCode(short orderHeaderStatusCodeId)
{
....snipped brevity....
var storedProcedure = StoredProcedure.Name.MySprocStatusCode.ToString();
return await _dbctx.Database.SqlQuery<OrderHeaderStatusCode>(...), ...).FirstOrDefaultAsync();
}
EDIT:
The crux is this. OrderHeader has properties with a custom type and basically those custom types have a List<T> that has to be populated. My current design is such that I repeatedly hit the db to populate those custom types List properties.
Is there a way to make one trip to the db to get all my information. As mentioned earlier the only way I can think of is to modify the stored procedure to return multiple record sets and then match them up.
BTW the architecture may be the flaw...in which case educate me on how to properly populate a complex object like this.
TIA
The root problem is that stored procedures aren't composable. In SQL you can't join a stored procedure call with anything (a database table or another stored procedure). So EF can't do that either.
If you want to get data with loaded collections from the database, normally you'd have to use Includes. EF will translate that into the appropriate joins and figure out how to load the entities and their collections from one big result set. But, as said, joins are no option here.
There is a way to load multiple result sets from one stored procedure. IMO it's pretty messy and very procedural. I would keep loading the data separately as you do now, if you want to keep using stored procedures. Others may suggest that you could load the additional data by lazy loading. Unfortunately that's not as straightforward as it should be with SqlQuery.
Another option of course is to start using regular DbSets (with Includes), but I can't judge if that's possible for you.
I have been searching for a way to join 2 distantly related domain models into one view model with no luck.
I am working on an existing application and have been asked to add a field to a result of fax log search.
My controller is returning a viewModel, and I am just wanting to add an additional field to it. Which sounds like it should be an easy task.
Background Info:
This is the original viewModel:
public class VOEIFaxLogSearchListViewModel
{
public DateTime DateTimeAdded { get; set; }
public string Processor { get; set; }
public string FaxStatusCode { get; set; }
public string VendorOrderID { get; set; }
public string FromFaxNumber { get; set; }
}
I want to add an additional field to this viewmodel:
public string CustomerName { get; set; }
The way that the application is designed, a stored procedure is called to return a dataset of the search results. (I won't the search method (GetFaxLogSearchResult) or its SQL call as it isn't necessary)
var resultFaxDS = vOEIDAO.GetFaxLogSearchResult(startDate,endDate,userName,faxType);
The resulting DataSet is then converted to a DataTable.
DataTable faxTable = resultFaxDS.Tables[0];
A For loop interates through each of the result records and puts them into a Domain Model named FaxModel. Which is mapped with Automapper to the viewModel named FaxLogSearchListViewModel.
for (int i=0; i<faxTable.Rows.Count;i++)
{
var row = faxTable.Rows[i];
var faxLogModel = vOEIDAO.DRToFaxModel(row);
faxViewModel.Add(Mapper.Map<FaxModel,FaxLogSearchListViewModel>(faxLogModel));
}
}
return faxViewModel;
}
Here is what I have done so far to add this result field:
1) added the new property to the view model.
2) modified stored procedure that pulls back the search results so it returns CustomerName in the dataset
The dilemna:
The method adding each row of the dataset into the Domain model (DRToFaxModel) is doing just that... it is populating a domain model(FaxModel). The field that I want to add isn't in the Domain model.
As a result, I don't want to add a field to the domain model if it doesn't belong to the concrete class.
Here is the domain model and the method used to populate it with each row from the search results:
public class FaxModel
{
public int FaxID { get; set; }
public int FaxStatusID { get; set; }
public string ToFaxNumber { get; set; }
public string FromFaxNumber { get; set; }
public DateTime DateTimeAdded { get; set; }
public string FaxStatusCode { get; set; }
public string Processor { get; set; }
public string VendorOrderID { get; set; }
}
public FaxModel DRToFaxModel(DataRow dr)
{
FaxModel voObj = new FaxModel();
voObj.FaxID = GetVOInt(dr["FaxID"]);
voObj.FaxStatusID = GetVOSmallInt(dr["FaxStatusID"]);
voObj.ToFaxNumber = GetVOStr(dr["ToFaxNumber"]);
voObj.FromFaxNumber = GetVOStr(dr["FromFaxNumber"]);
voObj.DateTimeAdded = GetVODateTime(dr["DateTimeAdded"]);
voObj.FaxStatusCode = GetVOStr(dr["FaxStatusCode"]);
voObj.Processor = GetVOStr(dr["Processor"]);
voObj.VendorOrderID = GetVOStr(dr["VendorOrderID"]);
//Cant add CustomerName to the model without modifying the FaxModel domain model.
//Shouldn't do that because it is a domain model.
//CustomerName is in the CustomerModel domain Model
// voObj.CustomerName = GetVOStr(dr["CustomerName"]);
return voObj;
}
So currently, my ViewModel with the added CustomerName property is returned with a null for CustomerName.
My domain models are distantly related.
In the database the FAX table can be joined joined to the CUSTOMER table but only by joining through an ORDER table. (the FAX table has an orderID field and the ORDER table has a CustomerID field)
So my resulting question is:
how do you use autoMapper to map a Fax domain model to a Customer domain model since the 2 domains don't have any common fields to build the relationship without joining through another table?
Or can you map more than 2 tables into 1 viewModel using automapper? how is this done?
What a great question. First of all, it's so refreshing to see someone asking about the proper way to do something, rather that just seeking a quick fix. Second, the amount of documentation provided is exactly the way SO questions should be written. I wish I could give this more than +1.
That said, since you're essentially asking an architecture question, there aren't any concrete answers, just opinions.
Here's my opinion:
You state the result of the sproc is mapped to a domain model:
var resultFaxDS = vOEIDAO.GetFaxLogSearchResult(startDate,endDate,userName,faxType);
However, you've added a return field, CustomerName to your sproc which is not part of the domain model. I think that's the heart of your issue.
There's a choice to be made here: does this sproc return a domain model or doesn't it?
Right now, my opinion is that it does not represent a domain model anymore, due to the new field, so you should not be trying to map it to a domain model before mapping it to your view model. You need to create a new data type to map this result to, which represents what you are actually getting from the sproc, and map that to your view model.
The alternate option is that this sproc does in fact represent a domain model. If that is the case, you should not be adding a new field to it that is not part of the model. Rather, you'll need to get the FaxModel domain objects and CustomerModel domain objects separately, and assemble your view models from both objects.
This is an example of the Single Responsibility Principle, meaning that an object, function, assembly, heck, even a program, should have one purpose. By giving your sproc a return value that both is and isn't a domain model, you're giving it more than one purpose. It would be best to either decide that it represents a FaxModel, and accept that the customer name needs to come from another source, or decide that it returns something else, say CustomerFaxModel which contains both customer and fax information, and use it as such.
To answer your technical question, AutoMapper does allow you to pass an existing target object to the map function in addition to a source object. You can map the target from object A to get some fields, and then pass the already mapped target to Map() a second time with a source of object B to map other fields.
Always, always, keep asking questions like this and you'll do well.