I have a simple example
I want to display a list of companies and employees
ACME
David
Simon
Zac
Microsoft
Kevin
Paul
Currently I do a linq query that returns the company model then traverse the model tree to get the employees. This works fine, however using glimpse I see its doing in the above example 3 queries. One for company then 2 queries for employees one for each company.
Im wondering if I would be better off having a ViewModel representing the data and populating that. However when trying to build the view model I dont know how to build it without doing multiple queries.
var data =
from company in db.Companies
join employee in db.Employees on company.companyId equals employee.companyId
orderby company.name, employee.name
select new { companyName = company.name, employeeName = employee.name }
Is there an easy way to link this to the view model or is there another best practice way of doing something simple like this.
You can get data in one round trip from database by adding ToList():
var data =
(from company in db.Companies
join employee in db.Employees on company.companyId equals employee.companyId
orderby company.name, employee.name
select new { companyName = company.name, employeeName = employee.name }).ToList();
or get company type instead of anonymous type
var data = db.Companies.Include(t => t.Employees )
.Select(t => new
{
t.name,
Employees = t.Employees .Select(p => new { p.name})
}).ToList();
or if you wanna use ViewModel, create a viewmodel:
public class CompanyVm
{
public string name { get; set; }
public IEnumerable<Employee> Employees{ get; set; }
}
public class Employee
{
public string name { get; set; }
}
then use automapper to map data
var comVm = data
.Select(comp => AutoMapper.Mapper.DynamicMap(comp, comp.GetType(), typeof(CompanyVm)))
.Select(comp => comp as CompanyVm).ToList();
Assuming you have the navigation properties in Entity Framework wired up correctly, you should be able to do:
var data = db.Companies
.Include(i => i.Employees)
.Tolist();
Then you can loop through them:
foreach (var company in data)
{
foreach (var employee in company.Employees)
{
}
}
Don't forget to include using System.Data.Entity; or you won't be able to use the strong-typed .Include() method.
Related
I have very complex code. I'm developing a web application for many restaurants where people can order online. I have two tables and models for Order, which contains information about User, and OrderDetails which has MenuItems. I created ShoppingCartController which has a ProcessOrder action
public ActionResult ProcessOrder(FormCollection frc)
{
List<Cart> lstCart = (List<Cart>)Session[strCart];
Order order = new Order()
{
Name = frc["Name"],
UserId = User.Identity.GetUserId(),
OrderTime = DateTimeOffset.UtcNow,
PaymentType = "Cash",
Status = "Processing"
};
db.Orders.Add(order);
db.SaveChanges();
foreach (Cart cart in lstCart)
{
OrderDetail orderDetail = new OrderDetail()
{
OrderId = order.Id,
MenuId = cart.Menu.Id,
Quantity = cart.Quantity,
Price = cart.Menu.Price,
RestaurantId = cart.Menu.RestaurantId
};
db.OrderDetails.Add(orderDetail);
db.SaveChanges();
}
Session.Remove(strCart);
return View("OrderSuccess");
}
Also I created an OrderController for displaying list of orders:
public ActionResult Index(int? restaurantId = null)
{
var orders = db.Orders.Include(o => o.User)
.Include(p => p.OrderDetails)
.OrderByDescending(x => x.Id);
return View(orders.ToList());
}
Here I just added restaurantId parameter.
And now I want to display orders by RestaurantId. I thought about taking RestaurantId from OrderDetails->Menu->RestaurantId, but I don't think that it works because OrderDetails of each order can have many RestaurantId.
Should I add RestaurantId column in Order table? Can you suggest an approach?
Order detail has both orderId and RestaurantId thats the table you need to query from!
I am new to asp.net core. I am building a web application for book management. I have a table called Author and books. Being a many to many relationships I made an associative entity that consists of the bookId and authorId. When I try to create I am able to create author and book. I successfully added the author and book to the database.
My author class looks like this
public class Author
{
private int _ID
private string _Name;
public string ID {
get { return _ID; }
set { _ID = value; }
public string Name {
get { return _Name; }
set { _Name = value; }
}
My book class is
public class Author
{
private int _ID
private string _Name;
private string _Title;
public string ID {
get { return _ID; }
set { _ID = value; }
}
public string Title {
get { return _Name; }
set { _Name = value; }
}
public string Name {
get { return _Name; }
set { _Name = value; }
}
I have a data access called db.cs to help to create the book and author in database.
public static int AddAuthor(Author A)
{
int renum = -1;
SqlConnection conn = null;
conn = new SqlConnection(ConnectionString);
conn.Open();
SqlCommand comm = new SqlCommand("sproc_AuthorAdd", conn);
comm.CommandType = System.Data.CommandType.StoredProcedure;
comm.Parameters.AddWithValue("#Name", A.Name);
comm.Parameters.AddWithValue("#Title", a.Title);
SqlParameter output = new SqlParameter();
output.ParameterName = "#AuthorID";
output.DbType = System.Data.DbType.Int32;
output.Direction = System.Data.ParameterDirection.Output;
comm.Parameters.Add(output);
int affect = comm.ExecuteNonQuery();
renum = affect;
c.ID = (int)output.Value;
I have done the same for books as well. I want to fill out the association table as well when the user filled out a book and author using their ID. I tried to do various things like using a cookie to pass data. But I cannot store data. Any kind of help is appreciated. Thanks in advance.
I'm not really sure I understand your last code snippet, but if you're having issues managing your many-to-many relationship between Books and Authors, have you considered just using Entity Framework Core?
Instead of writing a bunch of code that accesses your database, you just create models of your tables (similar to the classes you have defined above), and it handles the many-to-many relationship for you. The code to query for Authors and/or Books could then look as simple as:
using (var db = new dbContext())
{
var books = db.Books
.Where(b => b.ID > 1234)
.OrderBy(b => b.Title)
.ToList();
}
And creating a new Book or Author would be similarly simple:
using (var db = new dbContext())
{
var book = new Book { ID = 1234, Title = "Some Title", Name = "Some Name" };
db.Books.Add(book);
db.SaveChanges();
}
You might have to reimplement a bunch of things to take advantage of Entity Framework Core in your app, but it sounds like it would save you time in the long run.
I have made a working application that shows contacts from database, allows editing and deleting them and each of their emails/telephones/tags.
Database looks like this:
Contacts - Id (P), Name, Surname, Address;
Emails - EntryId (P), PersonId, Email1;
Telephones - EntryId (P), PersonId, Telephone1;
Tags - EntryId (P), PersonId, Tag1;
(where P means primary key and PersonId is always the corresponding Id from Contacts table, of the person whose email that is)
I haven't connected my tables in any way, I just approched Email of a person with id ID like this, for example:
var mailsById = contactsData.Emails.Where(x => x.PersonId == ID).ToList();
Now I realized I should maybe add a foreign key to tables Emails, Telephones, Tags, which would be the PersonId connected to Id from table Contacts. So I added this to table definitons on Emails:
CONSTRAINT [FK_Emails_Contacts] FOREIGN KEY ([PersonId]) REFERENCES [dbo].[Contacts] ([Id])
(same with Telephones and Tags)
Everything went well, in the EDMX diagram it now shows (1,*) connections between Contacts table and every other table (which I wanted), but now I get an 500 (Internal Server Error) on the following function. Function returns complete info about all the contacts in the database (on the frontend, I have a table Name-Surname-Address-Emails-Telephones-Tags, that's why I'm connecting all the tables from database to one list).
public JsonResult getAll()
{
using (ContactsDBEntities contactsData = new ContactsDBEntities())
{
List<Contact_Info> completeList = new List<Contact_Info>();
var contacts = contactsData.Contacts;
var emails = contactsData.Emails;
var telephones = contactsData.Telephones;
var tags = contactsData.Tags;
//GroupJoin:
//Outer.GroupJoin(Inner, outer => key, inner => key, (outer, inner) => result)
//first join joins contacts table with emails table
var contactList = contacts.GroupJoin(emails,
contact => contact.Id,
email => email.PersonId,
(contact, email) => new
{
Id = contact.Id,
Name = contact.Name,
Surname = contact.Surname,
Address = contact.Address,
Email = email
});
//second join joins telephones to the existing contacts-emails list
var contactList2 = contactList.GroupJoin(telephones,
contact => contact.Id,
telephone => telephone.PersonId,
(contact, telephone) => new
{
Id = contact.Id,
Name = contact.Name,
Surname = contact.Surname,
Address = contact.Address,
Email = contact.Email,
Telephone = telephone
});
//third join creates the needed contacts-emails-telephones-tags list
var contactList3 = contactList2.GroupJoin(tags,
contact => contact.Id,
tag => tag.PersonId,
(contact, tag) => new
{
Id = contact.Id,
Name = contact.Name,
Surname = contact.Surname,
Address = contact.Address,
Email = contact.Email,
Telephone = contact.Telephone,
Tag = tag
});
//contactList3 to completeList
foreach(var contact in contactList3)
{
Contact_Info temp = new Contact_Info();
temp.Id = contact.Id;
temp.Name = contact.Name;
temp.Surname = contact.Surname;
temp.Address = contact.Address;
foreach (var em in contact.Email)
{
temp.Emails.Add(em);
}
foreach (var tel in contact.Telephone)
{
temp.Telephones.Add(tel);
}
foreach (var tag in contact.Tag)
{
temp.Tags.Add(tag);
}
completeList.Add(temp);
}
return Json(completeList, JsonRequestBehavior.AllowGet);
}
}
This is my Contact_Info class which is used in this function:
public class Contact_Info
{
public Contact_Info () {}
public Contact_Info (string name, string surname, string address, List<Email> emails, List<Telephone> telephones, List<Tag> tags)
{
Name = name;
Surname = surname;
Address = address;
Emails = emails;
Telephones = telephones;
Tags = tags;
}
public int Id;
public string Name;
public string Surname;
public string Address;
public List<Email> Emails = new List<Email>();
public List<Telephone> Telephones = new List<Telephone>();
public List<Tag> Tags = new List<Tag>();
}
I tried debugging server side code, it returns no exeptions :/ This is the error output: The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.
Can you maybe tell me what I need to change in the working application if I changed this foreign-key properties of tables? Is it too late to be doing it now (i.e. does it require changing my code radically), and how important are the foreign key constraints here? This is my first use of databases in a more complex code, so please have understanding if I made some please-don't-do-that mistakes :)
Thanks a lot!
If navigation properties exist between your entities (ie. you generated the edmx from the database and FK constraints exist), then you don't need to get the entities individually and then join them. Just use Include():
var contacts = contactsData.Contacts
.Include(x=>x.Emails)
.Include(x=>x.Telephones)
.Include(x=>x.Tags)
.ToList();
I think Include is part of the System.Data.Entity namespace.
Afternoon,
Can any one see why my query is not returning a random 6 items please?
public class GetQuestions
{
public int qId { get; set; }
public string question { get; set; }
public string answer1 { get; set; }
public string answer2 { get; set; }
public string answer3 { get; set; }
}
[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public List<GetQuestions> Questions()
{
using (QuizDataContext dc = new QuizDataContext())
{
var query = from q in dc.tblquizs
orderby Guid.NewGuid()
select new GetQuestions
{
qId = q.id,
question = q.q,
answer1 = q.a1,
answer2 = q.a2,
answer3 = q.a3,
};
return query.Take(6).ToList();
}
Updated Add the GetQuestions Class
You can't get random order by using
orderby Guid.NewGuid()
You can test this by doing the following query and seeing the result:
from q in dc.tblquizs
select Guid.NewGuid()
Entity Framework 4.3.1 will translate the Guid.NewGuid() call to newid() - this would definitely be the preferred methodology if your DAL supports it. It may be, however, that whatever DAL you're using doesn't translate the call properly (in which case it may get translated to a GUID before it's sent to the database server, resulting in a static value for ordering). You should use a database profiler to see what your DAL is doing.
If the Guid.NewGuid call is not translating to newid() properly, you have two other options:
Use a sproc
Use something like the LINQ below (as a last resort)
var context = new ScratchContext();
var products = new List<Product>();
for (int i = 0; i < num; i++)
{
Product product = null;
while (product == null)
{
int randomId = r.Next(context.Products.Count());
product = context.Products.FirstOrDefault(p => p.ID == randomId);
}
products.Add(product);
}
return products.AsQueryable();
I used the following code to resolve this issue.
var qry = from q in dc.tblwhiskysprintquizs.AsEnumerable()
orderby Guid.NewGuid()
select new GetQuestions
{
qId = q.id,
question = q.q,
answer1 = q.a1,
answer2 = q.a2,
answer3 = q.a3,
};
return qry.Take(6).ToList();
It was as simple as adding .AsEnumerable to the look up.
orderby Guid.NewGuid()
generates random numbers that may not exist in your db
Please read my question carefully and reply me.
I have two tables as table1 and table2.
In table1 I have columns as AddressID(Primary Key),Address1,Address2,City
In table2 I have columns as ContactID(Primary Key),AddressID(Foriegn Key),Last Name,First Name.
By using join operation I can retrieve data from both the tables.
I created a Model in my MVC Application. I can see both the tables in entity editor.
In the ViewData folder of my solution explorer I have created two class as ContactViewData.cs and SLXRepository.cs.
In the ContactViewData.cs, I have following code
public IEnumerable<CONTACT> contacts
{
get;
set;
}
In the SLXRepository.cs, I have following code
public IEnumerable<CONTACT> GetContacts()
{
var contact =
(
from c in context.CONTACT
join a in context.ADDRESS on c.ADDRESSID equals a.ADDRESSID
select new
{
a.ADDRESS1,
a.ADDRESS2,
a.CITY,
c.FIRSTNAME,
c.LASTNAME
}
);
return contact;
}
I am getting the error in return type
Cannot implicitly convert type 'System.Linq.IQueryable<AnonymousType#1>' to 'System.Collections.Generic.IEnumerable<SiennaSLX.Models.CONTACT>'. An explicit conversion exists (are you missing a cast?)
In Linq2Sql, your particular query will return an IQueryable object.
You can either change the return type of your method to IQueryable
public IQueryable<CONTACT> contacts
{
get;
set;
}
public IQueryable<CONTACT> GetContacts()
{
var contact =
(
from c in context.CONTACT
join a in context.ADDRESS on c.ADDRESSID equals a.ADDRESSID
select new
{
a.ADDRESS1,
a.ADDRESS2,
a.CITY,
c.FIRSTNAME,
c.LASTNAME
}
);
return contact;
}
or you can cast your return value with the .AsEnumerable() function:
public IEnumerable<CONTACT> GetContacts()
{
var contact =
(
from c in context.CONTACT
join a in context.ADDRESS on c.ADDRESSID equals a.ADDRESSID
select new
{
a.ADDRESS1,
a.ADDRESS2,
a.CITY,
c.FIRSTNAME,
c.LASTNAME
}
);
return contact.AsEnumerable();
}