I have following table setup:
Order (ID)
Product (ID) [where ProductID 1 and 2 already exist]
OrderProduct (OrderId, ProductID) [two keys FK'ng to Order and Product tables]
And I'am trying to add a record to Order table assigning 2 products in to the order as follows:
var order = new Order();
order.Products.Add(new Product {ID=1});
order.Products.Add(new Product {ID=2});
db.SaveChanges();
The problem is: When saving the order, two Products are getting inserted in to the DB, instead of referring to the product records that already exists.
Please help. Thanks.
You should use db instead of creating new Product, like in this example:
var order = new Order();
order.Products.Add(db.Products.First(p => p.ID = 1));
order.Products.Add(db.Products.First(p => p.ID = 2));
db.SaveChanges();
Or, you need to "Update Reference" after product creation.
you can do something like this:
var product = new Product() { ID = 1 };
db.Attach(product);
order.Products.Add(product);
Related
I'm trying to convert raw SQL to EF core now.
my table has many columns, such as column1 to 10, and I need to select and update specific columns. original code like this :
SELECT column1, column2 FROM table WHERE key = "someKey"
(processing data)
UPDATE table SET column2 = someValue WHERE key = "someKey"
first, I tried like this :
var query =
from model in context
where key == "someKey"
select model;
query.First().column2 = someValue;
context.SaveChanges();
this code works very fine as I wished, but SQL generated like this :
SELECT key, column1, column2, ... column10 FROM table WHERE key = "someKey"
I do not want select useless columns so I tried this:
var query =
from model in context
where key == "someKey"
select new myDTO
{
Item1 = model.column1,
Item2 = model.column2
};
query.First().Item2 = someValue;
context.SaveChanges();
this code generates SELECT SQL statement exactly I wished, but cannot generate update statement. (obviously, myDTO is not registered into DbContext)
How can I do this with EF Core?
You can use Attach and Entry methods to track the changes to a entity model. To identify the model you would need all the keys (here I'm considering only one primary key: Id)
var query =
from model in context
where key == "someKey"
select new myDTO
{
Id = model.Id,
Item1 = model.column1,
Item2 = model.column2
};
var dto = query.First();
// Here I'm using Entity but you should use the right type
var entityyModified = new Entity();
entityModified.Id = dto.Id;
entityyModified.Item1 = dto.Item1;
entityyModified.Item2 = dto.Item2;
// ...
// Item1 or Item2 properties can be assigned to different values
// ...
// Save the changes
context.Attach(entityyModified);
var dbEntry = context.Entry(entityyModified);
dbEntry.Property(e => e.Item1).IsModified = true;
dbEntry.Property(e => e.Item2).IsModified = true;
context.SaveChanges();
The example given in the blog has the following
from e in s.StudentCourseEnrollments where courseIDs.Contains(e.Course.CourseID) select e
The contains logic will not work when we are looking for an exact match. If a student has enrolled for 6 courses (ex : 1,2,3,4,5,6) and the requested list contains 5 (ex: 1,2,3,4,5) the query will return a match when it should not. The other way works well when the student has enrolled in a subset of the requested list.
Below solution works but need help to convert the below sql to LINQ (EF Core 3.0) ?
Create TABLE dbo.Enrollments (StudentId INT NOT NULL, CourseId INT NOT NULL)
insert into dbo.Enrollments values (1,1)
insert into dbo.Enrollments values (1,2)
insert into dbo.Enrollments values (1,3)
insert into dbo.Enrollments values (1,4)
insert into dbo.Enrollments values (1,5)
insert into dbo.Enrollments values (1,6)
DECLARE #TempCourses TABLE
(
CourseId INT
);
INSERT INTO #TempCourses (CourseId) VALUES (1), (2), (3),(4),(5);
SELECT t.StudentId
FROM
(
SELECT StudentId, cnt=COUNT(*)
FROM dbo.Enrollments
GROUP BY StudentId
) kc
INNER JOIN
(
SELECT cnt=COUNT(*)
FROM #TempCourses
) nc ON nc.cnt = kc.cnt
JOIN dbo.Enrollments t ON t.StudentId = kc.StudentId
JOIN #TempCourses n ON n.CourseId = t.CourseId
GROUP BY t.StudentId
HAVING COUNT(*) = MIN(nc.cnt);
drop table dbo.Enrollments
db<>Fiddle
I don't know about the SQL query, but the EF Core 3.0 LINQ query for the same task is something like this:
var matchIds = new[] { 1, 2, 3, 4, 5 }.AsEnumerable();
var query = dbContext.Students
.Where(s => s.Enrollments.All(e => matchIds.Contains(e.CourseId))
&& s.Enrollments.Count() == matchIds.Count());
The main matching job is done with All subquery. Unfortunately that's not enough for the case when related link records are more than the matching ids, so additional counts comparison solves that.
You can achieve it with a simple way like this, live demo here
Let's say that you've got the list of enrollments by this way
var enrollments = from s in dc.Students
from c in s.Courses
select new { StudentID = s.StudentID, CourseID = c.CourseID };
Then get the result by this way
var groupedEnrollment = enrollments.GroupBy(p => p.StudentId)
.Select(g => new
{
StudentId = g.Key,
Courses = g.Select(p => p.CourseId).ToArray()
});
var result = groupedEnrollment.Where(g =>
g.Courses.Length == courses.Length &&
g.Courses.Intersect(courses).Count() == courses.Length);
I have a table say Category with the following fields:
cat_id, cat_name and Cat_desc
I also have another table product with the following fields:
pro_id, cat_id, pro_name and pro_desc, is_finished
Ordinarily, if I select a category with Linq to sql, it returns the category with all the associated products.
But I want to select a category but the products returned should be only product with is_finished value that is true.
any suggestion/code sample will be appreciated.
You need to join the tables:
select * from product p
left join Category c on c.cat_id = p.cat_id
where c.cat_name = 'yourcategory' and p.is_finished = 1
In linq to SQL:
var query = from product in products
join category in categories on category.cat_id = product.cat_id
select new { product.is_finished = true, category.cat_name = "yourcategory" };
This is probably the wisest way to do what you're asking:
var query = from product in products
select new {
product,
finishedCategories = product.categories.Where(c => c.is_finished)
};
This creates an anonymous type that has the data you're looking for. Note that if you access .product.categories you'll still get all of that product's categories (lazily-loaded). But if you use .finishedCategories you'll just get the categories that were finished.
I have Two tables "Customer" table and "Blacklist" customer table.
When i blacklist a customer i put the customerid as a foreign key to Blacklist table.
What i want to do is I need to find the Customer by "CusId" in the Customer Table.
I retrieve Name,Area,Telephone,Email from Customer table. When i retrive it, it should also check whether the customer id is in the black list customer table too.
depending on the existance it should pass a boolean value.
Final result should have total 5 columns.
(Name,Area,Telephone,Email,IsBlacklist).
Please help me to code this Entity Framework C#.
Thanks in advance.
Customer
---------
(CusId,Name,Telephone,Email)
Blacklist
---------
(CusId)
you can use navigation property of blacklist, that is exist on customer :
var customer = Customer.Select(u => new
{
u.Name,
u.Area,
u.Telephone,
u.Email,
Blacklist = u.Blacklist.Any()
})
.ToList();
To start you off:
var customer =
from c in Customer
where c.CusId == yourId
select new
{
c.Name, c.Area, c.Telephone, c.Email,
IsBlacklist = Blacklist.Any(b => b.CusId == yourId)
};
I have three tables
Employee (ID numeric, Name varchar)
Login (ID numeric, UserName varchar, Password varchar)
EmployeeLogin (ID numeric, EmployeeID, LoginID)
Relation is one employee can have multiple login. How will I get all the Login Name of a particular Employee.
I am able to fetch single record using the code given below but how will I get multiple records
using (var context = new AllEntities())
{
var query = from c in context.Employees
where c.ID == 9
select c;
}
The EmployeeLogin table seems redundant if you only have a one-to-many relationship between Employee and Login. You could just place a column EmployeeId in the Login table. The setup you have right now supports many-to-many between Employee and Login.
If you change your model according to my suggestion you could then get all Logins for a EmployeeId like this:
var query = from c in context.Logins
where c.EmployeeID == 9
select c;
If you keep your current model you could get all logins for an employee id like this:
var query = from l in context.Logins
join el in context.EmployeeLogins
on l.LoginId equals el.LoginId
where el.EmployeeID == 9
select l;
You should have all of the Logins in a navigation property on the Employee entity. See this tutorial:
http://www.asp.net/web-forms/tutorials/getting-started-with-ef/the-entity-framework-and-aspnet-getting-started-part-1
You can let the Entity Framework get the related data automatically or you can do it manually; for descriptions of lazy vs. eager loading see these tutorials:
http://www.asp.net/web-forms/tutorials/getting-started-with-ef/the-entity-framework-and-aspnet-getting-started-part-2
http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/reading-related-data-with-the-entity-framework-in-an-asp-net-mvc-application