I am using Entity framework and I have Users, Events and a table UsersEvents.
I know to this questions I probably can find on google, and I already tried but somehow, still have problems.
So I have:
User: UserId, Name
Event: EventId, Title
UserEvents: UserId, EventId
I want to create Event.
So on my Repository I have a method:
public void CreateEvent(Event #event)
{
//How to add event? (so the database will update the user that created the
//event and also will update the UsersEvents table.
}
I have tried to add the event first, but then had a problem to get the user.
Can someone help me and even without a code, just to help me understand how should I do this. Should I add the event and then the user? or should I add the event and the user to the UsersEvents? Thanks in advance :)
In a pure many to many association (where the junction table is not visible in the class model) you have to add items to the collection of the owner. In your case this would be:
var evnt = new Event { ... };
var user = db.Users.Include(u => u.Events).Single(u => u.UserId == id);
user.Events.Add(evnt);
db.SaveChanges();
The Include is there to make sure that user.Events is loaded before you add an item to it. If it isn't loaded the change tracker won't notice the change and nothing is saved.
But I think I understand your problem. You've implemented a repository pattern, so you feel forced to handle each object by its own repository. A User has nothing to do with an EventRepository, right? So remove this repository layer! In most cases it's just a useless layer that only sandbags everything you want to do with data. To get convinced you may want to read Generic Repository With EF 4.1 what is the point.
Related
I'm surprised that despite spending quite some time looking for the answer I haven't found it on the internet myself. What's even more surprising to me is that people tend to compare Attach to Add, and not to the Update. Furthermore, I've found very little mentions of the actual Update method, despite it being seemingly most obvious choice for the desired modify operation.
From the materials I managed to find I figured, that Attach should be used when you know the object exists somewhere in the database. If you don't make changes to it, no operations will be performed on the entity, and if you do - it will be updated. But then why would you need Update if it does the same thing from the sound of it?
I have this code snippet which I took from udemy course.
MovieController.cs
[HttpPut("{id}")]
public IActionResult ReplaceMovie(long id, [FromBody] Movie m)
{
if (ModelState.IsValid)
{
////
// logic in question starts here vvv
////
m.MovieId = id;
if (m.Studio != null && m.Studio.StudioId != 0)
{
context.Attach(m.Studio);
}
context.Update(m);
context.SaveChanges();
return Ok();
}
else
{
return BadRequest(ModelState);
}
}
But then why not just make it this way?
m.MovieId = id;
context.Update(m);
context.SaveChanges();
return Ok();
Would it not result in the same outcome, since we know that in case the Movie already has it's Studio assigned, then it (the Studio) exists in the database and we don't need to apply any changes to it?
Now if we applied some changes to the movie AND to the studio objects, then wouldn't it be safer to just perform the update modifications in their individual controllers? For example - if I want to update the movie and the studio, then I would first call the StudioController which would perform the modify operation on the Studio and then call the MovieController to update the movie.
What approach would be optimal for the best flexibility and clarity? What should I know about Attach and Update in general?
.Update will mark the entity as being in a Modified state. Update can be used to start tracking a mix of new and existing entities where the existing entities may have some modifications. The new entities will be inserted while the existing entities will be updated.
.Attach will mark the entity as being in an Unchanged state. However, entities will be put in the Added state if they have store-generated keys (e.g. Identity column) and no key value has been set. This means that when exclusively using store-generated keys, Attach can be used to start tracking a mix of new and existing entities where the existing entities have not changed. The new entities will be inserted while the existing entities will not be saved other than to update any necessary FK values.
Source Info:
Why use Attach for update Entity Framework 6?
https://blog.oneunicorn.com/2016/11/17/add-attach-update-and-remove-methods-in-ef-core-1-1/
Issue in passing DbContext to another method i.e. for e.g:
public bool MarkCustomerForDelete(Customer customerObj)
{
using(var dbContext = new MyContext())
{
using(var dbTransaction = dbContext.Database.BeginTransaction())
{
//Clear all orders for the Given Customers
var orderList = dbContext.Orders.Where(x=>x.id == customerObj.OrderId).ToList();
CommonLogicMethod(dbContext, orderList);
//Logic
customerObj.Status = "Deleted";
// The Modification will fail over due to the Customer Object for that object is already attached to the DbContext with Previous Values
dbContext.Entry(customerObj).State = EntityState.Modified;
dbContext.SaveChanges();
dbTransaction.Commit()
return true;
}
}
}
public void DeleteOrderRelatedData(MyContext dbContext, List<Orders> orderList)
{
foreach(var entity2 in entity2List)
{
var OrderAddresses = dbContext.OrderAddresses.Where(x=>x.Id == entity2.Id).ToList();
//Now if here the dbContext has 100 Entities (Tables)
//It internally Enumerates all the entities in the Local cache i.e. dbContext.Coupons.Local has all the Records from the DB in the Local present.
}
}
Question: Why does when DbContext is passed to another method internally calls for all the data i.e. in dbContext.Customers.Local has all the Data in the Database in First-Level Cache ?
Question: How to Pass DbContext from one Method to Another (without creating above given issue) ?
This is Creating problem related to modification of the Data i.e. DeleteCustomer will fail over.
Now, if the code in the DeleteOrderRelatedData, is merged into the DeleteCustomer function, it works fine.
I added a Logs for the dbContext , and dbContext while passing it to the Function internally is calling all the Select queries related to the different Queries..
For more details, please check this Video out : Link
Tools being used :
Entity Framework 6.0
System.Data.Sqlite
PostSharp for MethodBoundary Aspect.
Sounds like your problem is something to do with cascading deletions but the wording is difficult to understand ...
The statement in your question ...
DbContext is passed to another method internally calls for all the
data
... DbContexts don't just "go and get all data" automatically, you must be triggering something that's causing it.
It sounds to me like when you are deleting your customer object EF you are manually implementing the code for a cascading delete when what you should perhaps do is just add that to the model and then remove the customer object negating the need for all this extra logic.
In other words you have said / are trying to say "when a customer is deleted, also find and remove the customers related orders".
In the code sample above you do ...
//Clear all orders for the Given Customers
var orderList = dbContext.Orders.Where(x=>x.id == customerObj.OrderId).ToList();
This is purely getting the orders by executing a "select * from orders where customerid = customer.Id"
then in the method you define below that ...
public void DeleteOrderRelatedData(MyContext dbContext, List<Orders> orderList)
... it looks like you then want to further delete all the addresses for the order. Although you don't appear to be calling that method in the sample above.
Instead you can do something like this to have EF worry about the children and grandchildren deletions for you all in the db ...
Entity Framework (EF) Code First Cascade Delete for One-to-Zero-or-One relationship
Cascading deletes with Entity Framework - Related entities deleted by EF
The Microsoft documentation for this is here ...
https://msdn.microsoft.com/en-gb/data/jj591620.aspx
EDIT:
My answer was based on what I knew EF would do out of the box, it seems that the actual problem was caused by a component not mentioned in the question, the problem was not about performing a heirarchy of actions as I had interpreted it was in fact about solving the issue of how another third party component was adversely affecting the EF behaviour.
In repsonse to the question:
How to Pass DbContext from one Method to Another (without creating above given issue) ?
... Just do it as passing a context between 2 methods will not in its own right cause the problem you were having.
...
It seems that answering this question correctly was impossible :(
Issue was due to :
I was using PostSharp, to Log the Traces using OnMethodBoundaryAspect.
Now this was using Arguments internally.
Since while Logging, it was serializing the Arguments, And this was creating the Problem.
When a user hits the button, I'm executing the following code.
using (Context context = new Context())
{
foreach (Thing thing ViewModel.Things)
context.Things.AddOrUpdate(thing);
context.SaveChanges();
}
The updates are executed except for when the user selected a row and hit delete button. Visually, that post is gone but it's not really being removed from the database because it's not in the view model anymore. Hence, the loppification only ticks for the remaining things and not touching the removees.
I can think of two ways to handle that. One really bad - to remove everything from the context, save it and then recreate based on the view model. It's an idiotic solution so I'm only mentioning it for the reference's sake.
The other is to store each removed post in an array. Then, when the user invokes the code above, I could additionally perform the deletion of the elements in that array. This solution requires me to build the logic for that and I'm having this sense that it should be done automagically for me, if I ask nicely.
Am I right in my expectation and if so, how should I do it? If not, is there a smarter way to achieve my goal than creating this kill squad array?
At the moment, I do a double loop, first adding and updating what's left in the data grid. Then, removing anything that isn't found there. It's going to be painful if the number of elements grows. Also, for some reason I couldn't use Where because I need to rely on Contains and EF didn't let me do that. Not sure why.
using (Context context = new Context())
{
foreach (Thing thing in ViewModel.Things)
context.Things.AddOrUpdate(driver);
foreach (Thing thing in context.Things)
if (!ViewModel.Things.Contains(thing))
context.Things.Remove(thing);
context.SaveChanges();
}
The first thing I want to advice you is you should use the AddOrUpdate extension method only for seeding migrations. The job of AddOrUpdate is to ensure that you don’t create duplicates when you seed data during development.
The best way to achieve what you need you can find it in this link.
First in your ViewModel class you should have an ObservableCollection property of type Thing:
public ObservableCollection<Thing> Things {get;set;}
Then in the ViewModel's constructor (or in another place), you should set the Things property this way:
context.Things.Load();
Things = context.Things.Local;
From the quoted link:
Load is a new extension method on IQueryable that will cause the
results of the query to be iterated, in EF this equates to
materializing the results as objects and adding them to the DbContext
in the Unchanged state
The Local property will give you an ObservableCollection<TEntity> that
contains all Unchanged, Modified and Added objects that are currently
tracked by the DbContext for the given DbSet. As new objects enter the
DbSet (through queries, DbSet.Add/Attach, etc.) they will appear in
the ObservableCollection. When an object is deleted from the DbSet it
will also be removed from the ObservableCollection. Adding or Removing
from the ObservableCollection will also perform the corresponding
Add/Remove on the DbSet. Because WPF natively supports binding to an
ObservableCollection there is no additional code required to have two
way data binding with full support for WPF sorting, filtering etc.
Now to save changes, the only you need to do is create a command in your ViewModel class that call SaveThingsChanges method:
private void SaveThingsChanges()
{
context.SaveChanges();
}
Okay, so it's late and I'm tired, but this shouldn't be happening... or so I believe. I have the following code for an event handler for a button click where I create a new customer from a web form.
int customerId = <from_somewhere_doesnt_matter>;
Customer cust;
if (int.TryParse(Request.QueryString["cid"], out customerId)) {
// update existing customer
cus = db.Customers.Single(c => c.CustomerId == customerId);
cus.UpdatedByUser = user;
cus.Updated = DateTime.Now;
}
else {
// create new customer
cus = new Customer();
cus.InsertedByUser = user;
cus.Inserted = DateTime.Now;
}
SetFields(cus);
db.SaveChanges();
The SetFields() method just populates the different properties for the customer from the corresponding web form fields.
I have been running this code in production for quite a while and it's been working fine. However, recently a user told me it doesn't work to add a new user (doesn't happen very often). I checked it, and sure enough, I filled in the form and tried to add the user but was just redirected back to the user list without any error message and without a new user.
I checked the code, and realised I had forgotten to add the db.Users.AddObject(usr) when adding a new user. I added the method call, and the user was added correctly. I then went to the customer code, just to check how and when I call the AddObject-method there, and it turns out I don't!
I might be blind, but I have searched the source code and I do not call the method anywhere and it still works to add a customer! The only thing I can think of is that the customer is added because it refers to another object (the current user), and that somehow triggers an add. The user does not depend on any other fields.
What is happening!?
Thanks in advance!
The reason might be automatic association fixup. The T4 Template POCO Generator of EF 4.0 for instance creates such methods which are called in the property setters. Possibly it happens in this line in your code:
cus.InsertedByUser = user;
If your User entity has a collection of customers (as the inverse navigation property to InsertedByUser) then the POCO Generator would create a fixup method which does something like ...
InsertedByUser.Customers.Add(this);
... where this is cus in your example. This is called in the setter for the InsertedByUser property. This means that your new created customer cus is automatically added to the Customers collection of the user which you assign. EF change detection will recognize this and put the new customer in Added state into the context. When you call SaveChanges a new customer record in the database is created.
Just a hypothesis. You could check in detail what's going on by debugging into the property setter.
When you set
cus.InsertedByUser = user;
Your customer will be added to the user.Customers collection that represents the other end of the many-to-one. This gives EF a handle on the object which will then be added to the context and saved away since it is new.
I have the following scenario:
Entities are loaded from the database.
One of them is presented to the user in a Form (a WPF UserControl) where the user can edit properties of that entity.
The user can decide to apply the changes to the entity or to cancel the editing.
How would I implement something like this with the EntityFramework?
My problem is that, when I bind the UI directly to the Properties of the Entity, every change is instantanously applied to the entity. I want to delay that to the moment where the user presses OK and the entity is validated successfully.
I thought about loading the Entities with NoTracking and calling ApplyPropertyChanges after the detached entity has been validated, but I'm not entirely sure about the correct way to do that. The docu of the EntityFramework at MSDN is very sparse.
Another way I could think of is to Refresh the entity with StoreWins, but I don't like resetting the changes at Cancel instead of applying changes at Ok.
Has anyone a good tutorial or sample?
One options is what you said do a no-tracking query.
ctx.Customers.MergeOption = MergeOption.NoTracking;
var customer = ctx.Customers.First(c => c.ID == 232);
Then the customer can modify 'customer' as required in memory, and nothing is actually happening in the context.
Now when you want actually make the change you can do this:
// get the value from the database
var original = ctx.Customers.First(c => c.ID == customer.ID);
// copy values from the changed entity onto the original.
ctx.ApplyPropertyChanges(customer); .
ctx.SaveChanges();
Now if you are uncomfortable with the query either for performance or concurrency reasons, you could add a new extension method AttachAsModified(...) to ObjectContext.
that looks something like this:
public static void AttachAsModified<T>(
this ObjectContext ctx,
string entitySet,
T entity)
{
ctx.AttachTo(entitySet, entity);
ObjectStateEntry entry =
ctx.ObjectStateManager.GetObjectStateEntry(entity);
// get all the property names
var propertyNames =
from s in entry.CurrentValues.DataRecordInfo.FieldMetadata
select s.FieldType.Name;
// mark every property as modified
foreach(var propertyName in propertyNames)
{
entry.SetModifiedProperty(propertyName);
}
}
Now you can write code like this:
ctx.Customers.MergeOption = MergeOption.NoTracking;
var customer = ctx.Customers.First();
// make changes to the customer in the form
ctx.AttachAsModified("Customers", customer);
ctx.SaveChanges();
And now you have no concurrency or extranous queries.
The only problem now is dealing with FK properties. You should probably look at my index of tips for help here: http://blogs.msdn.com/alexj/archive/2009/03/26/index-of-tips.aspx
Hope this helps
Alex
I suggest IEditableObject, too, and additionally IDataErrorInfo.
The way i do it is, i basically have a viewmodel for an entity that takes the entity as constructor parameter (basically a wrapper object).
In BeginEdit, i copy the entity properties to my viewmodel, so if i do CancelEdit, the data is only changed in the ViewModel and the original Entity hasn't changed. In EndEdit, i just apply the ViewModel properties to the Entity again, or course only if validation has succeeded.
For validation i use the methods of IDataErrorInfo. I just implement IDataErrorInfo.Error so that it checks each Property name via IDataErrorInfo[string columnName] and concatenates eventual error messages. If it's empty, everything is ok. (not sure if Error is meant to be used that way, but i do it)
If i have other Entities attached to my original Entity, such as Customer.Orders, i create them as nested ViewModels in the original Entity's ViewModel. The original ViewModel calls it's subModels' Begin-,Cancel-,EndEdit / Error methods in it's own implementations of those methods then.
It's a bit more work, but i think it's worth it because between BeginEdit and EndEdit, you can be pretty sure that nothing changes without you noticing it. And having a code snippet for INotifyPropertyChanged-enabled properties helps a lot, too.
The normal way of doing this is binding to something that implements IEditableObject. If and how that fits in with the entity framework, I'm not sure.