ASP MVC 5 EF 6 - Insert into Multiple Tables - sql-server

I have an existing database, from which I have built a shell web-app using VS2013 and EF6, but I have run into a few problems.
My database has the following tables, for example:
Table: Customer (Id, First, Last, Date)
Table: Assets (Id, CustID)
Table: Orders (Id, AssetId, CustID)
When the EF created the shell webapp for me (which has awesome) it gave me the following method, for example, to create a new Customer:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include="Id, First, Last, Date")] Customer customer)
....
return View(customer)
In my database I have a one Customer to many orders and assets, but I don't understand how to use this relationship during a Create operation, or any other.
My question -- How do I "Create" a new Customer when this operation needs to span multiple tables? For example, when the "Create Customer" form is filled out, you would add one or more Assets and/or Orders.
Should I use a stored procedure to do multiple inserts across three different tables? Or can I make a change to the database that will let me use all the EF magic?
Thanks for your help!

You can use a view model that contains all three classes.
public class CreateCustomerViewModel
{
public Customer Customer { get; set; }
public ICollection<Asset> Assets { get; set; }
public ICollection<Order> Orders { get; set; }
}
Your [HttpGet] action method will pass this view model as the model instead of a Customer.
[HttpGet]
public ActionResult Create()
{
CreateCustomerViewModel model = new CreateCustomerViewModel();
return View(model);
}
Your [HttpPost] action method will take the CreateCustomerViewModel as a parameter:
[HttpPost]
public ActionResult Create(CreateCustomerViewModel model)
{
// Create the Customer with the necessary Assets and Orders and save
}
If you're classes and relationships are configured properly, adding the appropriate Assets and Orders to the navigation properties on your Customer entity should trigger EF to automatically insert the assets and orders into the appropriate tables when you insert the Customer.

Related

laravel 5.4 many to many relationship

i have relationship many to many table 'admins' , 'pjt_roles' with pjt_role_admin.
but,not working
i have 2 model
class Role
protected $table = 'pjt_roles';
public function Admin(){
return $this->belongsToMany(Admin::class',pjt_role_admin');
}
class Admin
public function Role(){
return $this->belongsToMany(Role::class,'pjt_role_admin');
}
and table pjt_role_admin have attribute
admin_id from table admins
role_id from table pjt_roles
Specify your pivot table in relationship. Default laravel assume admin_role as your pivot table because you have Admin and Role models
class Role
protected $table = 'pjt_roles';
public function Admin(){ // should be admins() for better readability
return $this->belongsToMany(Admin::class, 'pjt_role_admin');
}
class Admin
public function Role(){ // should be roles() for better readability
return $this->belongsToMany(Role::class, 'pjt_role_admin');
}
To determine the table name of the
relationship's joining table, Eloquent will join the two related model
names in alphabetical order. However, you are free to override this
convention. You may do so by passing a second argument to the
belongsToMany method.
Fetch Data
$admin = Admin::find(1);
$roles = $admin->Role; // should change to roles() in relationship for better readability
Save
$admin->Role()->attach($roleId);
details https://laravel.com/docs/5.4/eloquent-relationships#many-to-many

Single SQL query to select all customers together with all the orders belonging to each of the customer?

I'm learning SQL and suddenly I have this question in my mind. Given that I have .NET classes representing customers and orders like this:
public class ListCustomers
{
public List<Customers> MyBigList {get; set;}
public ListCustomers()
{
// some code to query database
}
}
public class Customers
{
public int CustomerID {get; set;}
public String CustomerName {get; set;}
public String CustomerEmail {get; set;}
public String CustomerPhone {get; set;}
public List<Orders> CustomerOrders {get; set;}
}
public class Orders
{
public int OrderID {get; set;}
public double OrderTotalAmount {get; set;}
public String BillingAddress {get; set;}
public String ShippingAddress {get; set;}
}
Normally I would select all the customers first and then in my code I would have a foreach loop to select orders to fill each of the List<Orders> CustomerOrders property.
However this requires multiple trip to SQL Server and I'm wondering if there is any way to fill the whole List<Customers> MyBigList class using only one trip to SQL? In that case how would the DataSet/DataTable of result look like?
There are multiple ways to do this.
Im not sure how your SQL is returned but if you use a
SqlCommand.ExecuteReader()
One way would be to load all the orders belonging to a customer for every customer that is loaded, however there will be a trip to SQL for each customer to get their orders.
public LoadCustomers()
{
// some code to query database
// some code to handle the data and add to your list
while (SQLdta.Read()){
Customers customer = new Customers();
customer.CustomerOrders = loadCustomerOrders();
MyBigList.Add(this);
}
}
Option 2 - Another possible solution would be to create a list of the customers you need orders for and get them all in one pull and match them to their respective customers.
Minimum Two SQL hits...
Option 3 - A little messy in my opinion but may be best suited to your needs.
Create Tow SQL Queries
Execute as one SQL hit.
E.g.
SELECT CustomerID,CustomerName,CustomerEmail,CustomerPhone
FROM Customers
WHERE (YOUR WHERE STRING GOES HERE);
Then do another query for the Orders
SELECT OrderID,OrderTotalAmount,BillingAddress,ShippingAddress
FROM Orders
WHERE (YOUR ORDERS WHERE STRING GOES HERE)
AND CustomerID IN ( SELECT CustomerID FROM Customers WHERE (YOUR WHERE STRING GOES HERE));
Both will sent to SQL and you will execute your query together returning 2 result tables
So in your Class you may do something like this.
//Your SQL query execution code
//Reads your Customers First
while (SQLdta.Read())
{
//Your logic to populate your customers list
}
//Move the reader to the next returned results table and read the orders
while (SQLdta.NextResult())
{
while (SQLdta.Read())
{
//Your logic to populate your orders list
}
}
At this point you will have 2 lists and you may then be able to link the orders to your Customers
If your sticking with a dataset then it works the same except each table in the dataset will be your separate SQL query results.
if ((dataSet != null) && (dataSet.Tables != null) && (dataSet.Tables.Count > 0))
{
foreach (DataTable d in dataSet.Tables)
{
//your code to handle each tables data
}
}

Inheritance Hierarchies in Entity Framework

I'm working on a database for a flying club that has a table for Flights and a table for ClubMembers. Flights, unfortunately, must be paid for by so there is a BillTo that references the ClubMember who must pay.
So far it looks like this...
public ClubMember
{
public int ID{get;set;}
public string FirstName{get;set;}
public string LastName {get;set;}
}
public Flight
{
public int ID {get;set;}
public ClubMember PilotPayingTheBill{get;set;}
public double EnormousPriceToBePaid {get;set;}
}
Pretty simple ... but then the messy world intervenes. Sometimes the plane is flown by a mechanic for maintenance purposes. I don't want to do this the lazy way and enter the mechanic as a dummy record in the ClubMember table. The database is too new for that kind of kludge. Plus, EF has nifty ability to implement inheritance in the database, so I can keep it all nice and tidy like this:
public BillableEntity
{
public int ID{get;set;}
}
ClubMember : BillableEntity
{
public string FirstName{get;set;}
public string LastName {get;set;}
}
NonPayingUser : BillableEntity
{
public string Description {get;set;}
}
public Flight
{
public int ID {get;set;}
public BillableEntity billTo{get;set;}
public double EnormousPriceToBePaid {get;set;}
}
With a few instructions in my fluent configuration the NonPayingUser and ClubMembers are all put in their own tables, with ID as a primary key and foreign key - a nice, concise design I was very happy with. The Billto_id column in the flights table is not null so every flight will always have a billableEntity, which will be either a clubMember or NonPayingUser.
Writing the query in TSQL is pretty easy
select coalesce(cm.FirstName + ' ' + cm.LastName,np.Description) as BillTo
from Flights f
left outer join ClubMembers cm on f.billto_id = cm.ID
left outer join NonPayingUsers np on f.billto_id = np.ID
But doing the same thing in EF has me stumped.
The Flight class has a BillTO property which is the parent class of BillableEntity. I can cast it to the descendant classes like this...
var flights = DB.Flights
.Select(f => new
{
PersonName = (f.BillTo as ClubMember).FirstName + PersonName = (f.BillTo as ClubMember).LastName
,
OtherName = (f.BillTo as NonPayingUser).Description
});
but this produces monstrous amounts of TSQL.
One solution is just to write my own stored procs to join these tables together and use the EF classes to do all the basic CRUD on the individual tables, and that's the direction I'm leaning in. But is there a better way?
This is a duplicate of several similar questions...
EF (6, I'm looking at 7 RC1 now) always generates a huge amount of TSQL.
I think that the best answer is what is the problem of "producing monstrous amounts of TSQL"?
If you have a particular issue with a query solve that issue otherwise ignore it. You will see that in a huge percentage of cases it does not causes problem the DBMS.
I ended up creating a calculated field in the BillableEntities table
alter table BillableEntities drop column name;
alter table BillableEntities add Name as (case when [Description] is null then LastName + ', ' + FirstName else [Description] end)
and then marked it as DataBaseGenerated in the BIllableEntities class.
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public string FullName { get; private set; }
This way the SQL produced by EF is much more manageable. Performance is good, data integrity is good too.

Error when creating a new Customer - MVC ADO.NET

I get the following error when I fill out my form in my MVC Application:
UpdateException was unhandled by user code
Unable to update the EntitySet 'Customer' because it has a DefiningQuery and no <InsertFunction> element exists in the <ModificationFunctionMapping> element to support the current operation.
My Customer table has: ID (PK), Name, Age, Address and a TimeStamp.
The form only allows Name and Address to be filled out (don't know why - I'm new to MVC, ADO.NET btw)
This is my code:
[HttpPost]
public ActionResult Create(Customer customer)
{
customer.ID = 5;
db.Customers.AddObject(customer);
db.SaveChanges();
return View();
}
I am leaving customer.ID = 5 as a hard coded temp solution for the time being.
If ID is your primary key, Why you are updating that ? You should be updating the rest of the properties.
[HttpPost]
public ActionResult Create(Customer customer)
{
customer.Name= "New name";
db.Customers.AddObject(customer);
db.SaveChanges();
return View();
}
Do you have primary key in your Customer Table ?
This error originates when you do not have primary key on table or when your entity set is mapped with database view.

How to include parent objects list in child entity object

I have the following tables in my database
Table1: tblAddressType (Id, Name)
Table2: tblAddressDtls (Id, AddressTypeId, Address1)
I am left joining the above two tables to get list of all address types and corresponding address details as follows
SQL Query:
select t1.*, t2.*
from tblAddressType t1
left outer join tblAddressDtls t2 on t1.Id = t2.AddressTypeId and t2.Id = 1;
For the above tables, i have created POCO entity classes as follows:
[Table("tblAddressType ")]
public partial class AddressType
{
[Key]
[Column(Name="ID")]
public int ID { get; set; }
[Required]
[Column(Name = "Name")]
public virtual string Name {get; set;}
[Include]
[Association("AddressTypeAddress", "ID", "AddressTypeId")]
public virtual ICollection<Address> Addresses { get; set; }
}
[Table("tblAddress", SchemaName="dbo")]
public class Address
{
[Column(Name="ID")]
public int ID { get; set; }
[Column(Name = "AddressTypeId")]
public int? AddressTypeId{ get; set; }
[Column(Name = "Address1")]
public string Address1{ get; set; }
[Include]
[Association("AddressTypeAddress", "AddressTypeId", "ID", IsForeignKey = true)]
public virtual AddressType AddressType { get; set; }
}
and, to fetch the data as shown in the sql query above, i have written the following LINQ query in my service code and this query returns me the data as needed:
var qry = (from p in dbContext.AddressTypes
join pa in (from t in dbContext.Addresses
where t.ID == 1 select t)
on p.ID equals pa.AddressTypeId into ppa
from t in ppa.DefaultIfEmpty()
select t).AsQueryable();
Now, I want to write a domain service method named "GetAddressById(int addressId)" which should return me the matching Address object along with list of AddressType objects as i need to bind list of "AddressType" objects to the drop down box in Add/Edit address screen.
I wanted to include and fetch list of "AddressType" objects data at the time of
fetching Address object data itself to avoid round-trip to server
in my silverlight client app.
Kindly suggest me the best possible way to achieve this functionality?
NEW:
I assume that in the database, Addresses has a relation to AddressTypes and again that you are using EntityFramework.
GetAddressById(int addressId){
return dbContext.Address.SingleOrDefault(a => a.ID == addressId).Include("AddressTypes");
}
that row of code would now get a single address which has the id of addressId, if there are none it would return null or if more returned it would throw an exception, the Include would tell EF that you also want AddressTypes to be loaded when you get the address and would create an appropriate join to make this happen, all this would make into a single query to the database and get the result that you want.
OLD:
Let's say we want the AddressType and all its Addresses with just one call to the db (asuming you use EntityFramework), we would call a method like
GetAddressTypeIncludingAddresses(int id){
return _context.AddressType.Include("Addressess");
/*if you use ctp5 of ef code first you should even be able to do (at => at.Addresses) in the include */
}
and then when you have it just do addressType.Id and foreach(var address in addressType.Addresses){} and the like when you use it.
I hope I understood your question, if not try again and I'll edit my answer.
You could do this by creating a stored proc in your database that returns mutliple result sets. First the one which gets you your desired child and parent and second the one that gets you your list of parents. Then you can use the work-around described here:
http://blogs.msdn.com/b/swiss_dpe_team/archive/2008/02/04/linq-to-sql-returning-multiple-result-sets.aspx
Which allows you to get each part of the results.
As an aside, you don't need a left join for your query. Since your where clause references the table on the right you will never get null values on the right side of the join. Use an inner join instead.

Resources