Entity Framework - Track changes and rollback - c#

At the moment I've got a big Entity Data Model (.edmx) with a database underneath it. Now I want to give people who use my site the option to edit those entities but only after I gave approval of the change.
So this would mean that I want to "change" the entity but not really save it yet, after I accepted the change it would override the enity with the changed one. But when I did this I would still like to be able to rollback the change afterwards.
Now I've come up with a few solutions:
1) Make a entity with like: [ID], [ChangeApplyDate] [ChangeApproveDate] [Entity before change] [Entity after change] [Accepted (boolean)]
2) Make an extra entity with the changed values and a link to the "Original" entity and have a boolean in this entity if it is the current approved one or not. (this would not create a new table but expand the table like, "Book", with a few extra properties.
Now I would like to know how you think about those solutions and if you ever made something like this?

There are two things here:
Keep a list of all previous versions. Have a look at my answer to this question: Ideas on database design for capturing audit trails
Controll the acceptance of changes. To do this you could add an approval flag to each table, which only you have access to.

Related

How to implement LinkedList in Entity Framework Core

I want to persist a linked list of objects using in my ASP.Net Core application. For simplicity, I'll use blog and comments; although the real context is much more complex.
I scaffolded two tables, and changed ICollection<Comment> to LinkedList<Comment>. However, if I create an initial migration and apply to an empty database, I don't get anything "linked" in the database (no next or previous). Also, if I seed the data, and then do something like this:
var comments = _context.blogs.First().Comments
I get null. If I leave public virtual ICollection<Comment> Comments, I get the IEnumerable just fine.
I tried to use LinkedList<LinkedListNode<Comment>> instead, and it works nice unless I try to create a migration. Getting an error
No suitable constructor found for entity type 'LinkedListNode'. The following constructors had parameters that could not be bound to properties of the entity type: cannot bind 'value' in 'LinkedListNode(Comment value)'; cannot bind 'list', 'value' in 'LinkedListNode(LinkedList list, Comment value)'.
I couldn't find any guidance how to implement LinkedList in C#/.NET Core (obviously, I can do it manually, having next and prev fields - but I would very much prefer to use framework capabilities, if possible!)
I don't know much about MS SQL server, but the whole next and prev parts in mysql won't work because you can't map the keys it would require to track the fields properly because each link has to have an id in a database to link to. only thing I know of would be create the next/prev yourself and use some custom data persistence or data annotations. but the foreign key constraints I'm pretty sure on any relational database will prevent you from auto persisting those types of fields. reason being tracking deletes, inserts etc would be a nightmare because if you remove the middle of the chain, then the database has to try and guess where to link the ends to

Cascading deletes on *Derived Entities*

OK, check out the following simple object model:
Entity ConcreteThingy derives from AbstractThingy.
Entity Owner can have at most 1 ConcreteThingy.
No two Owners can have the same ConcreteThingy.
EDMX:
DB model (TPT):
What I want:
I want 2 things, in descending order of priority:
Thing I want #1: Above all, I want the simplest, most concise, cleaner solution to #2
below.
Thing I want #2: When I delete an Owner, I want to automatically delete any potentially associated ConcreteThingy, along with the base AbstractThingy.
What I'm doing:
So, naturally, I went to the EDMX and simply turned on Cascade delete on the Owner 0 ↔ 0..1 ConcreteThingy association.
This works well in setting the Delete rule of the FK_ConcreteThingy_Owner relationship to Cascade, but I don't see ANY constraints/DB code that makes sure that the AbstractThingy record gets deleted as well.
Again, when I delete an Owner, the corresponding ConcreteThingy (if any) is deleted, but it leaves the dangling AbstractThingy, which not surprisingly, I don't want.
Can somebody please tell me what is the standard way to implement this type of tasks using EF 5?
I've tried by making AbstractThingy abstract, hoping that the SQL code would somehow infer what I want (understandably there would be some sort of chicken-and-egg conundrum for the generator, but hey, at least I tried it.)
Or should I define some sort of smart-ass referential constraint in my EDMX?
Or is it expected that I'd go to the DB and write code myself, like a trigger, etc...!?!? That would be a bit of a[nother] fiasco for EF and its design tools.
Thanks.

Entity Framework "detach without deletion" for filtering

I'm having a problem with Entity Framework and filtering architecture.
Let's say that I have a couple of related entities, and I want to do some changes to them, based on a filter.
So, for example I have Orders and Orderlines (to put a simple example)
I have order1, with orderline1, orderline2, orderline3 relationships in the DB
Then I receive an update request for order1 but only for orderline1 and orderline3
I get the data from the db using entity framework, which retrieves an objectgraph of the order and its lines.
Is there a way to filter these entity objects so that I can work with an objectgraph that contains order1 and orderline1 and orderline3, but NOT orderline2 without that being a problem later?
Because if i remove orderline2 from the entitycollection, i get later on concurrency errors (or deleted entities, which is something i don't want)
I hope the question is clear, I know that there could be other ways (iterating and not performing updates on orderline2, so it remains the same and no changes are made) but the way the architecture was made doesn't let me do that right now.
If I could say "don't track any more changes to orderline2, just ignore any changes that I do to this particular object and descendants, just leave it in the DB the way it is", so that I can just remove it from the collection and move forward, that'd be perfect
Thanks!
You can go multiple ways as you already described yourself as well:
Iterating through all orderlines and only modifying those that need to be modified (but that isn't an option as you stated)
The alternative you described to specifically not track changes for orderline2 is not possible in a "normal" EF situation where the ObjectStateManager is responsible for change tracking (as far as I know). In a scenario with Self Tracking Entities it's more easy because every STE has it's own unique ChangeTracker on board which can be easily switched off.
But the most easy option would be to exclude the orderlines you dont want to modify in the "select" statement or the retrieval of the entities. Something like:
private void ModifyOrderLines(int orderID, List<int> orderlineIds)
{
using(Context context = new Context)
{
List<OrderLines> orderlines =
context.OrderLines.
Where(orderLine => orderLine.OrderID == orderID && orderlineIDS.Contains(orderLine.ID))
}
}
Assuming you have set up clean foreign key relationships which were translated into Navigation Properties in EF. So what you do is to get a list of OrderLines which belong to a certain order and have an ID that's in your list of OrderLines that need to be modified.
Afterwards you change the orderlines and apply the changes to the context and call SaveChanges. This is just a basic way of how you could do things. I don't know your exact setup but I hope this helps.
EDIT
Based on your comment I should just go for the easy way and write a loop as you already proposed. Why not? I don't think there are many alternatives, and if there are then they would make things overcomplicated.
So something like this might just work:
ObjectContext.OrderLines.ForEach(o => if(orderlineIds.Contains(o.ID) {o.SomeProperty = SomeValue}));
Or you could just write the loop yourself.
EDIT2
You already mentioned detaching from the ObjectContext in the title of your post. Why don't go that way then? You tell that you have no control over the ObjectContext that you get, that it is passed into several methods and that you get update requests for certain entities. Then detaching those entities that are not needed for the update request can be an option too. Maybe this topic on MSDN might help you decide. Afterwards you might attach the detached objects again for they maybe needed for subsequent "client" calls. But this depends on how you manage the ObjectContext.
Do you keep the ObjectContext "alive" over multiple "client" calls or do you instantiate it over and over again for specific client calls. I do not get the situation totally clear...

insert note automatically insert product - best practice for such cases?

I am asking in order to know what is the best practice for such cases. I am using EF4.
I have two entities called "Note" and "Product". I have another entity called "Stock".
Note has many Products and each Product can be in one Note only (Product must have a Note).
Whenever I insert, Delete or Update a Product, I have to update the relevant Stock entity according to the Note attached to the Product.
In the InsertProduct() method I insert the Product and then update the relevant Stock. In case the Product will break the Stock rules - I get an exception and the operation will abort as should be.
In the InsertNote() method, I cannot insert the Note because such thing will insert automatically the attached Products and for each Product I need to update the Stock and validate Stock rules (as described above). So before the Note insertion I loop through all the Products and call to InsertProduct(). After that I insert the note.
Until here two questions:
Does the things should be like that? Do I need to manually insert
each Product before I insert the Note? How does this settle with the
fact Product must be attached to exists Note (in the time of
insertion the Products, the Note wasn't inserted)?
Where should I put the InsertNote() and InsertProduct() methods?
each one of them deals with Note, Product and Stock entities. Should I create something like NoteService and ProductService classes or should I put them in one big service class?
In the question I asked here, I got an answer that describe how to create events like OnBeforeInsert(). Is this implementation is fine with my needs to update the Stock after insert Product?
I'm not quite sure I fully grasp what you're trying to do and what it means to validate Stock rules and update stocks. But it seems like you're trying to reuse InsertProduct from InsertNote when you shouldn't be.
It seems like InsertNote should
check all the rules and update Stock accordingly (I'm assuming this means loading the stocks into the context from the database or attaching them if you have them in memory somewhere)
add the new Note to the context (which will add the new products you've put into its collection before)
SaveChanges()
and InsertProduct should
check/update stocks as above
add the Product to the Note's collection of Product
SaveChanges()
In other words, both these methods would reuse just the rule checking part. InsertProduct might be better renamed to UpdateProductsOfNote because it sounds like Note is an aggregate root of your model (In DDD, you do persistence/repository operations against the aggregate).
As to where these methods live, if they are not too complex, adding them to a repository/data access class (that owns the EF context object) would be ok. It does seem like they have quite a few rules associated with them. If so, some sort of domain service would probably be a better choice. It seems like much of the rules aren't simply to support persistence. If you relocate these methods then you may not need to handle ObjectContext events as you asked in the post you referenced.

LINQ-to-SQL + One-to-Many + DataBinding deleting

I use LINQ-to-SQL to load data from a database that has two tables in a one-to-many relationship (one Recipe has many Ingredients).
I load a Recipe and LINQ retrieves Ingredient objects into an EntitySet that is binded into a ListBox.
If I want to delete some Ingredients off a Recipe, I get a "An attempt was made to remove a relationship between a Recipe and a Ingredient. However, one of the relationship's foreign keys (Ingredient.RecipeID) cannot be set to null.
I SOLVED this problem using the well known solution by adding 'DeleteOnNull="true"' to the DBML file. But adding this setting only removes the problem when we are deleting Ingredient objects that were retrieved from the DB.
The problem is with the Ingredient objects that were created in code (added to a Recipe) and added to the EntitySet collection of Ingredients and then deleted BEFORE SubmitUpdates is called. Then, the same exception happens again. This usually happens on a new, unsaved recipe when user is adding ingredients to it, makes a mistake and erases an ingredient off a recipe. I added the DeleteOnNull to both 'Association Name="Recipe_Ingredient"' lines in DBML.
How am I supposed to remove such objects? The only solution I see at the moment is that I would load the ingredients into a collection not under the DataContext and then when saving, delete all ingredients off a recipe and add then again from that cache..
try
{
// Needed for existing records, but will fail for new records
yourLINQDataContext.Ingredients.DeleteOnSubmit(ingredient);
}
catch (Exception)
{
// Swallow
}
yourRecipeObject.Ingredients.Remove(ingredient);
It seems that you're looking for something that I was looking for myself just a few days back when I asked "How do I design backing data types for a databound WPF dialog with Ok/Cancel buttons?".
The answer is an intriguing post from Paul Stovell describing a sample IEditable adapter for Linq to Sql. This will let you create your desired "Apply/Cancel" semantics in a generalized manner without completely dissociating yourself from the underlying ORm-generated classes through a full custom-written layer.
It's a pretty slick trick, overall, that will essentially let you sidestep the problems you're fighting right now. :)
On a different note, I'm curious as to why your recipe to ingredient relationship is 1:n instead of m:n. Is it for simplicity's sake? I use garlic in a lot of recipes. :)
// Create new entities
Cart c = new Cart();
CartEntry ce = new CartEntry();
ce.Cart = c;
// Delete the entry
c.CartEntries.Remove(ce);
dc.Cartentries.Attach(ce);
dc.CartEntries.DeleteOnSubmit(ce);
// Insert the cart into database
dc.Carts.InsertOnSubmit(c);
dc.SubmitChanges();
Explaination of the issue: Both entities, c and ce, are not related to a data context - they are not being tracked. EntitySet.Remove() (first delete line) only removes the relation between c and ce. While c can exist without associated cart entries, ce can't exist without an assiciated cart because of a foreign key constraint. When submitting changes to the database, the disconnected ce is dealt with as well, causing a constraint violation and the exception.
In order to get rid of that untracked and disconnected cart entry you need to attach it to your data context (causing it to be tracked) and then mark it for delete on submit. The moment you submit your changes the cart entry will be deleted properly and not cause the exception.
For more details on that issue check this out:
http://msdn.microsoft.com/en-us/library/bb546187%28v=VS.100%29.aspx
you need to decouple the save code from the events in your GUI, it seems like you're a little to eager to save things to the db before the dust has settled and you're queuing and removing things from the db that never got there in the first place, it would be best if you could identify a point when the user will "commit" their changes, and at that moment, process the full condition of the GUI - this will save you a bunch of spaghetti code.
I would also be curious to know if your entities have autonumber IDs or if you're using some other ID mechanism. You're probably sending DELETEs to the database for the as-yet-uncommitted Ingredient records, if those include NULL IDs, I think the linq could get nasty.
Have you hooked up a textwriter to your DataContext.Log to see what sorts of SQL is generated just before you get your exeception?
Thank you for your answer, I will examine the posts and see what I can do. I must say I'm surprised to even see this problem occuring, it seems quite natural to me that one could add records to the LINQ-provided "cache" of data, then decide to erase some of them and then commit. Change tracking should be able to handle that. I just starting with LINQ so I might be doing a stupid mistake somewhere in the code (wouldn't be the first).
On the other note: You are quite correct that garlic can belong to many recipes (not my coctail recipes thought!). I actually model that with an Article object/table. But for a recipe, you need quantities. So in my model, you have a Recipe that has 1:n Ingredients, each of them having a Quantity, a 1:1 link to an Article (which has a Name, an AlcoholContent and some data to establish an interchangeability hierarchy) and a 1:1 link to an Unit (for the quantity to make sense).
So in a sense, Ingredient table makes a M:N relationship between Recipe and Article, and at the same time adding some additional information to each individual linked pair.
I had exactly the same problem. I had a parent / child hierarchy, and when adding and removing the child entity without saving to the database I received the "An attempt was made to remove a relationship" exception.
I discovered that this problem only arose when I set an object style property of the child to another linq-sql entity before saving. eg
1. This creates the error
RetailAccountCustomerCard racc = new RetailAccountCustomerCard();
Card addedCard = _idc.Cards.Where(c => c.CardId == card.CardId).ToList().First();
racc.Card = addedCard;
this.CurrentCustomer.RetailAccountCardsBindingList.Add(racc);
// Some code triggered by the user before saving to the db
CurrentCustomer.RetailAccountCardsBindingList.Remove(racc);
2. This doesn't create the error
RetailAccountCustomerCard racc = new RetailAccountCustomerCard();
racc.CardId = card.CardId; // note that I have set the Id property not the object
this.CurrentCustomer.RetailAccountCardsBindingList.Add(racc);
// Some code triggered by the user before saving to the db
CurrentCustomer.RetailAccountCardsBindingList.Remove(racc);
Strangely enough, the error that arises in 1. specifies the problem is to do with the relationship is on the RetailAccountCustomerId property of RetailAccountCustomerCard. IT HAS NOTHING to do with the Card object I added. It seems that simply setting any object property of the new entity triggers the problem.
NB. Example 1 works fine in terms of saving, it only causes a problem if the the new entity is deleted before saving.
I am running into a similar issue, as a workaround, I need to call DataContext.GetChanges(), then everything seems to have caught on again :)
Another problem you could have it that you are binding to columns and not entity properties, and hence the referential collections are not updated (already stated by someone else, but enforcing the fact).

Categories