First, I'm not sure if I'm reinventing the wheel with regards to foreign keys here, but lets say I have a Patient table
PatientId
Name
Gender
Age
HospitalId
I want to make sure that when an object is inserted into the Patient table, that it won't insert a record with a HospitalId that doesn't exist in the Hospital table. Is there an efficient way of doing this? Or as I said above am I reinventing a wheel here?
Try this:
public class Patient
{
public int PatientId {get;set;}
public string Name {get;set;}
public string Gender {get;set;}
public int Age {get;set;}
public int HospitalId {get;set;}
//add this line of code
public virtual Hospital Hospital {get;set;}
}
Also you can change Hospital class this way:
public class Hospital
{
//your code....
//new property
public virtual ICollection<Patient> Patients {get;set;}
}
Related
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
}
}
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.
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.
Hi I try to create a ManyToOne relation but get stucked.
I have a BILL and a Booking Class
The realation is :
A Bill can have many Bookings. (1:m)
The Bill Class should manage the relationship
A Booking belong only to one Bill. (1:1 A Booking can't exist without a Bill )
If I delete the Bill, all Bookings that belongs to the Bill should be deleted.
If I remove a Booking from the Bill, only this Booking should be deleted.
If I delete a Booking, this Booking should be removed from the Bill.
So far i have this model :
Bill Class
#Entity
public class Bill extends Model{
#OneToMany(cascade={CascadeType.ALL})
public List<Booking> bookings;
public void setBookings(List<Booking> bookings)
{
for (Booking booking : bookings)
{
booking.bill = this;
}
}
}
Booking Class
#Entity
public class Booking extends Model{
#ManyToOne(optional=false )
#Required(message="Bill needed")
public Bill bill;
public void setBill(Bill bill){
this.bill=bill;
bill.bookings.add(this);
}
}
If I delete the Bill, all Bookings belong to the Bill deleted as well.
But I can't delete a single Booking, either from the Bill Side or the Booking side.
What do I miss ?
Your case is the standard parent child case. You need to declare the following:
#Entity
public class Bill extends Model {
#OneToMany(cascade = CascadeType.ALL, orphanRemoval=true, mappedBy="bill")
public List<Booking> bookings = new ArrayList<Booking>();
}
#Entity
public class Booking extends Model {
#ManyToOne public Bill bill;
}
This code contains all necessary declarations. The orphanRemoval ensures that whenever you delete a Booking from Bill (i.e., when that Booking becomes orphaned) it will be deleted from the database. If you delete the Bill, then all its BookingS are deleted as well (via the CascadeType.ALL annotation).
With the above declarations, you can set Bookings by
for(Booking booking : bookings) {
bill.bookings.add(booking);
booking.bill = bill;
}
(you don't need to code setBookings() yourself) but you must make sure that you do the change on both sides.
For removal, if you set a Booking's bill to null and remove the booking from the Bill's bookings:
booking.bill = null;
bill.bookings.remove(booking);
should do it.
If you call
booking.delete();
then also its bookings should be deleted.
Really quite standard....
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.