I have a class called Facility. Facility has a collection property on it called Employees. I'm using the disconnected layer of EF. I want to clear the Employees collection from a specific facility, but I don't want to make two trips to the DB: (1) getting all the employees, and then (2) clearing the. How can I do this?
Here's what I've tried...
Facility f = new Facility()
{
Id = 4,
Employees = new List<Employee>()
};
context.Facilities.Attach(f);
context.Entry<Facility>(f).Collection(fac => fac.Employees).IsLoaded = true;
context.SaveChanges();
I think I'm close, but it doesn't work. Thanks for the advice.
If you want to use EF only, you're always going to need some roundtrip. In the end, EF needs to generate DELETE ... WHERE Id = x statements. How would it know the values for x without first grabbing them from the database?
But of course you can do this in a more efficient way than fetching the complete Employee objects. It's enough to get the Id values. Then you can use these Ids to create stub entities that you mark as Deleted:
var ids = context.Empoyees.Where(e => e.FacilityId == 4)
.Select(e => e.Id).ToArray();
foreach(int id in ids)
{
var emp = new Empoyee { Id = id }; // Stub entity
context.Entry(emp).State = System.Data.Entity.EntityState.Deleted;
}
context.SaveChanges();
This is pure EF. But you can also use EntityFramework.Extended. This allows you to execute a statement like
context.Empoyees.Where(e => e.FacilityId == 4)
.Delete();
Related
This question already has answers here:
Fluent API, many-to-many in Entity Framework Core
(3 answers)
Closed 1 year ago.
Somebody edit the join table, because I can't remember the word for a table that contains only 2 foreign keys for 2 entities. Edit: I think it's called a link table?
The db-model looks like this DB-Model entities. And in the code for the models and DTO's, datadock has a List<Standard>. So the code looks like this:
private List<DatadockDTO> GetDatadocksByDataportId(Guid id)
{
var query = from r in _ddRepository.GetIQueryable()
where r.DataportId == id
select new DatadockDTO
{
Id = r.Id,
Name = r.Name,
Standards = r.Standards //Error not possible, the only suggestion it gives is DataDockStandards
//I used to put GetStandardsByDatadockId(r.Id) here but there is another way apparently
};
List<DatadockDTO> datadocks = query.ToList();
return datadocks;
}
But somebody I work with told me Entity Framework allows you to do something like that. But I'm not sure where to look for.
The extra code I made how I thought it was supposed to work:
private List<StandardDTO> GetStandardsByDatadockId(int id)
{
var query = from r in _standardRepository.GetIQueryable()
where r.DataDockStandards.Any(dds => dds.DatadockId == id)
select new StandardDTO
{
Id = r.Id,
Name = r.Name,
StandardVersions = GetStandardVersionsByStandardId(r.Id)
};
List<StandardDTO> standards = query.ToList();
return standards;
}
private List<StandardVersionDTO> GetStandardVersionsByStandardId(int id)
{
var query =
from r in _svRepository.GetIQueryable()
where r.StandardId == id
select new StandardVersionDTO
{
Id = r.Id,
Name = r.Name,
};
List<StandardVersionDTO> versions = query.ToList();
return versions;
}
EDIT (next day): okay so apparently somehow I can select r.Standards somehow. I didn't change anything, I could suddenly just choose r.Standards without getting an error...
You need to configure many-to-many relationship.
Here is how it's done in EF Core (keep in mind, it's only for version 5 and above, it is not supported even in 3.1).
And here is how it's done in EF 6.
If you do it correctly, your DataDock entity will have Standards navigation property and you'll be able to use it in LINQ queries.
How do I convert this SQL command query to Entity Framework Linq? Trying to remove a contains list.
Trying to utilize resource
public async Task<int> PurgeInventory(IEnumerable<int> ids)
{
var command = new SqlCommand("Update [dbo].[Inventory] set status = 0 where InventoryId in ({ids})");
command.AddArrayParameters("ids", ids);
return await UnitOfWork.ExecuteSqlCommandAsync(command);
}
My attempt - looking for fix syntax:
IEnumerable<int> ids = new IEnumerable<int>();
UnitOfWork.DBSet<Inventory>().Remove(x => Where(x => InventoryId.Contains(ids));
Resource: How to use parameters in Entity Framework in a "in" clause?
For large bulk delete operations it's generally better to do that as a raw SQL statement, however if you have a manageable number of rows to delete:
(fast)
IEnumerable<int> ids = getIdsToDelete();
var idRecords = ids.Select(x => new Inventory { InventoryId = x }).ToList();
using (var context = new MyDbContext())
{
foreach(var idRecord in idRecords)
{
context.Inventory.Attach(idRecord);
context.Inventory.Remove(idRecord);
}
context.SaveChanges();
}
This requires a new Context instance which will not have any Inventory records possibly loaded, and requires that the Inventory ID's selected to remove contain no duplicates. We convert the IDs into dummy Inventory entities then attach them to the DbContext and tell EF to remove them.
(thorough)
IEnumerable<int> ids = getIdsToDelete();
using (var context = new MyDbContext())
{
var idRecords = context.Inventory.Where(x => ids.Contains(x.InventoryId)).ToList();
foreach(var idRecord in idRecords)
context.Inventory.Remove(idRecord);
context.SaveChanges();
}
This one retrieves the entities from the context then removes them. In cases where you want to update related records or otherwise manage relationships, this along with remembering to .Include() those relationships would be the way.
I'm having trouble understanding .Select and .Where statements. What I want to do is select a specific column with "where" criteria based on another column.
For example, what I have is this:
var engineers = db.engineers;
var managers = db.ManagersToEngineers;
List<ManagerToEngineer> matchedManager = null;
Engineer matchedEngineer = null;
if (this.User.Identity.IsAuthenticated)
{
var userEmail = this.User.Identity.Name;
matchedEngineer = engineers.Where(x => x.email == userEmail).FirstOrDefault();
matchedManager = managers.Select(x => x.ManagerId).Where(x => x.EngineerId == matchedEngineer.PersonId).ToList();
}
if (matchedEngineer != null)
{
ViewBag.EngineerId = new SelectList(new List<Engineer> { matchedEngineer }, "PersonId", "FullName");
ViewBag.ManagerId = new SelectList(matchedManager, "PersonId", "FullName");
}
What I'm trying to do above is select from a table that matches Managers to Engineers and select a list of managers based on the engineer's id. This isn't working and when I go like:
matchedManager = managers.Where(x => x.EngineerId == matchedEngineer.PersonId).ToList();
I don't get any errors but I'm not selecting the right column. In fact the moment I'm not sure what I'm selecting. Plus I get the error:
Non-static method requires a target.
if you want to to select the manager, then you need to use FirstOrDefault() as you used one line above, but if it is expected to have multiple managers returned, then you will need List<Manager>, try like:
Update:
so matchedManager is already List<T>, in the case it should be like:
matchedManager = managers.Where(x => x.EngineerId == matchedEngineer.PersonId).ToList();
when you put Select(x=>x.ManagerId) after the Where() now it will return Collection of int not Collection of that type, and as Where() is self descriptive, it filters the collection as in sql, and Select() projects the collection on the column you specify:
List<int> managerIds = managers.Where(x => x.EngineerId == matchedEngineer.PersonId)
.Select(x=>x.ManagerId).ToList();
The easiest way to remember what the methods do is to remember that this is being translated to SQL.
A .Where() method will filter the rows returned.
A .Select() method will filter the columns returned.
However, there are a few ways to do that with the way you should have your objects set up.
First, you could get the Engineer, and access its Managers:
var engineer = context.Engineers.Find(engineerId);
return engineer.Managers;
However, that will first pull the Engineer out of the database, and then go back for all of the Managers. The other way would be to go directly through the Managers.
return context.Managers.Where(manager => manager.EngineerId == engineerId).ToList();
Although, by the look of the code in your question, you may have a cross-reference table (many to many relationship) between Managers and Engineers. In that case, my second example probably wouldn't work. In that case, I would use the first example.
You want to filter data by matching person Id and then selecting manager Id, you need to do following:
matchedManager = managers.Where(x => x.EngineerId == matchedEngineer.PersonId).Select(x => x.ManagerId).ToList();
In your case, you are selecting the ManagerId first and so you have list of ints, instead of managers from which you can filter data
Update:
You also need to check matchedEngineer is not null before retrieving the associated manager. This might be cause of your error
You use "Select" lambda expression to get the field you want, you use "where" to filter results
I want to update a sequence (SortOrder) column in my database using Entity Framework.
Seems like the easiest way is to load the SortOrder for each item in the set, update the SortOrder as needed, and then save the rows back to the database.
Of course, this would be more efficient if I don't need to retrieve every column in the rows I am updating. And what I'm having trouble understanding is what happens if I only select some of the columns, make changes to those columns, and save it back.
var x = from o in Package.PackageProducts
orderby o.SortOrder
select new { o.Id, o.SortOrder };
// Modify SortOrder in this collection x
// Save x back to the database
How does Entity Framework handle modifying and saving partial entities like this?
Does anyone know if Microsoft has documented somewhere what happens in this case? Does anyone know enough about what happens that you can tell me?
You can create stub entities from the anonymous types and mark SortOrder as modified:
var x = (from o in Package.PackageProducts
orderby o.SortOrder
select new { o.Id, o.SortOrder }).ToList();
// Modify SortOrder in this collection x
...
// Save x back to the database
foreach(y in x)
{
var p = new PackageProduct { Id = y.Id, SortOrder = y.SortOrder }); // stub
db.PackageProducts.Attach(p);
db.Entry(p).Property(p1 => p1.SortOrder).Modified = true;
}
db.SaveChanges();
Where db is the DbContext.
You may have to disable validation first, because the stubs probably don't have all required properties:
db.Configuration.ValidateOnSaveEnabled = false;
I wish to return a graph for a "Business" entity. The Buiness entity has a collection of "Contacts".
So I basically want this:
ctx.Business.Include("Contacts").Where(b => b.BusinessID == id).Single();
The problem is I don't want ALL the contacts for the business. Just those that have a ContactTypeID = x. How can I accomplish this? I figured I may need to break the query into 2 steps. 1 to get the business and a 2nd to grab the contacts and then attach them to the business somehow.
But I'm using using the STE t4 template and I can't figure out how to do it using that.
I'd greatly appreciate any help.
One way for doing so is:
var biz = from b in ctx.Business where b.BusinessID == id
select new
{
Business = b,
Contacts = b.Contacts.Where(c => c.ContactTypeID == x)
}.ToList();
I think I found how to do it:
var biz = ctx.Business.Where(b => b.BusinessID == id).Single();
var contacts = ctx.Contacts.Where(c => c.BusinessID==id && c.ContactTypeID==6);
foreach (var contact in contacts)
{
biz.Contacts.Add(contact);
}
I was worried by adding the contacts this way they would be treated as "new" items but change tracking is off so they are treated as unchanged. I think change tracking only turns on once they are deserialized.