I have two entities. File and Binary. File contains file metadata and Binary contains file content. I want Binary instance be deleted when I remove File instance. I use the following:
public partial class MyEntities : Entities
{
public override int SaveChanges()
{
foreach (var entry in ChangeTracker.Entries<File>().Where(e => e.State == EntityState.Deleted))
{
entry.Reference<Binary>(i => i.FileBinary).EntityEntry.State = EntityState.Deleted;
}
return base.SaveChanges();
}
}
This code does not work. I mean Binary instance is not deleted and also there is no error. Can anyone tell the reason or a better way to do that?
Thanks
You must add more details of your code, like your EF type(code first,model first,schema first) and etc. however if there is a relation between these two entities you can see this(it seems your entities are related, so if there is not relationship you can add it):
code first:
Entity Framework: Delete Object and its related entities
model first and schema first:
Cascading deletes
Related
I have the following Update generic method for my entities:
public void Update < T > (T entity) where T: class {
DbEntityEntry dbEntityEntry = DbContext.Entry(entity);
if (dbEntityEntry.State == System.Data.Entity.EntityState.Detached) {
DbContext.Set < T > ().Attach(entity);
}
dbEntityEntry.State = System.Data.Entity.EntityState.Modified;
}
After SaveChanges() the data is successfully updated in the DB.
Now I nee to implement and Audit Log before SaveChanges() but I noticed that CurrentValues are equal to OriginalValues:
// For updates, we only want to capture the columns that actually changed
if (!object.Equals(dbEntry.OriginalValues.GetValue<object>(propertyName), dbEntry.CurrentValues.GetValue<object>(propertyName))){
//here I add a new Audit Log entity
}
Any clue on how to solve this? Or is there a better way to do it in Entity Framework 6?
If you are using a disconnected entity, you can set originals values without affect entity instance values, adapt this method at you needs
public static void LoadOriginalValues(this WorkflowsContext db, DbEntityEntry entity)
{
var props = entity.GetDatabaseValues();
foreach (var p in props.PropertyNames)
{
if (entity.Property(p).IsModified)
{
entity.Property(p).OriginalValue = props[p];
}
}
}
The original values are recovered from the entity itself. If the entity is being tracked by a context, this information is available.
In your case, you're using a disconected entity, so there is no change tracking, and the entity doesn't have the original values.
SO, in this case, if you need the original values there is no other option than getting them from the DB, and compare them, one by one.
If you want to get an entity that behaves as if it had been tracked by the context you can use a context to read the entity from the DB, and use something like ValueInjecter to automatically set the property values from the disconected entity into the tracked entity.
I'm working with asp.net mvc3.
I have a edmx that was created ADO.NET Entity Data Model. (Data-First)
TestDb.Designer.cs
namespace Test.Web.DataModel
{
public partial class TestDbContext : ObjectContext
{
public ObjectSet<T_Members> T_Members { ... }
public ObjectSet<T_Documents> T_Documents { ... }
....
}
}
T_Members, T_Documents <-- This property is a table of the database.
I want to get a list of this table.
How to get the list of table name from EDMX?
Answer Myself.
TestDbContext context = new TestDbContext();
var tableList = context.MetadataWorkspace.GetItems<EntityType>(System.Data.Metadata.Edm.DataSpace.CSpace);
foreach (var item in tableList)
{
item.Name;
}
To be help for people who have the same problem ...
I think your 'solution' only works when the table and the entity have the same name.
If you would rename your entity to Document (without the prefix) it would fail.
Quote from Microsoft employee:
No, unfortunately it is impossible using the Metadata APIs to get to
the tablename for a given entity. This is because the Mapping metadata
is not public, so there is no way to go from C-Space to S-Space using
the EF's APIs.
I have created a model POCO class called Recipe; a corresponding RecipeRepository persists these objects. I am using Code First on top of an existing database.
Every Recipe contains an ICollection<RecipeCategory> of categories that link the Recipes and the Categories table in a many-to-many relationship. RecipeCategory contains the corresponding two foreign keys.
A simplified version of my controller and repository logic looks like this (I have commented out all checks for authorization, null objects etc. for simplicity):
public ActionResult Delete(int id)
{
_recipeRepository.Remove(id);
return View("Deleted");
}
The repository's Remove method does nothing but the following:
public void Remove(int id)
{
Recipe recipe = _context.Recipes.Find(id);
_context.Recipes.Remove(recipe);
_context.SaveChanges();
}
Howevery, the code above does not work since I receive a System.InvalidOperationException every time I run it: Adding a relationship with an entity which is in the Deleted state is not allowed.
What does the error message stand for and how can I solve the problem? The only thing I try to achieve is deleting an entity.
#Ladislav: I have replaced ICollection<RecipeCategory> by ICollection<Category>. Accidentially, ReSharper refactored away the virtual keyword.
However, the problem remains — I cannot delete a Category from a Recipe entity. The following code does not persist the deletion of the categories to the database:
private void RemoveAllCategoriesAssignedToRecipe()
{
foreach (Category category in _recipe.Categories.ToArray())
{
_recipe.Categories.Remove(category);
category.Recipes.Remove(_recipe);
}
_context.SaveChanges();
}
I have debugged the code and can confirm that the collections are modified correctly — that is, they contain no elements after the loop (I have also used the Clear() method). After calling SaveChanges(), they are populated again.
What am I doing wrong?
(Maybe it is important: I am using the Singleton pattern to only have one instance of the context.)
I was able to solve the problem the following way:
private void RemoveAllCategoriesAssignedToRecipe()
{
foreach (Category category in _recipe.Categories.ToArray())
{
Category categoryEntity = _categoryRepository.Retrieve(category.CategoryID);
var recipesAssignedToCategory = categoryEntity.Recipes.ToArray();
categoryEntity.Recipes.Clear();
foreach (Recipe assignedRecipe in recipesAssignedToCategory)
{
if (assignedRecipe.RecipeID == _recipe.RecipeID)
{
continue;
}
categoryEntity.Recipes.Add(assignedRecipe);
}
_context.Entry(categoryEntity).State = EntityState.Modified;
}
_recipe.Categories.Clear();
_context.SaveChanges();
}
I have a schema similar to the standard Product / OrderDetails / Order setup. I want to delete a single Product and cascade delete all OrderDetails which reference that product.
Assuming that I've thought this through from the business rules perspective, what's the most elegant way to handle that with Entity Framework 4?
First thing is first:
Is there any reason on delete cascade at the database level won't work?
If that's really not a possibility, you could try the following:
Since ObjectContext doesn't have a DeleteAll style method...you could always implement your own:
public static void DeleteAll(this ObjectContext context,
IEnumerable<Object> records)
{
foreach(Object record in records)
{
context.DeleteObject(record);
}
}
Then you could write something like (probably in a Repository):
context.DeleteAll(context.OrderDetails.Where(od => od.Product == product));
Or, to be a little cleaner:
var toDelete = context.OrderDetails.Where(od => od.Product == product);
context.DeleteAll(toDelete);
I have an entity with self reference (generated by Entity Designer):
public MyEntity: EntityObject
{
// only relevant stuff here
public int Id { get...; set...; }
public MyEntity Parent { get...; set...; }
public EntityCollection<MyEntity> Children { get...; set...; }
...
}
I've written a stored procedure that returns a subtree of nodes (not just immediate children) from the table and returns a list of MyEntity objects. I'm using a stored proc to avoid lazy loading of an arbitrary deep tree. This way I get relevant subtree nodes back from the DB in a single call.
List<MyEntity> nodes = context.GetSubtree(rootId).ToList();
All fine. But when I check nodes[0].Children, its Count equals to 0. But if I debug and check context.MyEntities.Results view, Children enumerations get populated. Checking my result reveals children under my node[0].
How can I programaticaly force my entity context to do in-memory magic and put correct references on Parent and Children properties?
UPDATE 1
I've tried calling
context.Refresh(ClientWins, nodes);
after my GetSubtree() call which does set relations properly, but fetches same nodes again from the DB. It's still just a workaround. But better than getting the whole set with context.MyEntities().ToList().
UPDATE 2
I've reliably solved this by using EF Extensions project. Check my answer below.
You need to assign one end of the relationship. First, divide the collection:
var root = nodes.Where(n => n.Id == rootId).First();
var children = nodes.Where(n => n.Id != rootId);
Now, fix up the relationship.
In your case, you'd do either:
foreach (var c in children)
{
c.Parent = root;
}
...or:
foreach (var c in children)
{
root.Children.Add(c);
}
It doesn't matter which.
Note that this marks the entities as modfied. You'll need to change that if you intend to call SaveChanges on the context and don't want this saved.
The REAL solution
Based on this article (read text under The problem), navigation properties are obviously not populated/updated when one uses stored procedures to return data.
But there's a nice manual solution to this. Use EF Extensions project and write your own entity Materilizer<EntityType> where you can correctly set navigation properties like this:
...
ParentReference = {
EntityKey = new EntityKey(
"EntityContextName.ParentEntitySetname",
new[] {
new EntityKeyMember(
"ParentEntityIdPropertyName",
reader.Field<int>("FKNameFromSP")
)
})
}
...
And that's it. Calling stored procedure will return correct data, and entity object instances will be correctly related to eachother. I advise you check EF Extensions' samples, where you will find lots of nice things.