MTM relationship in EF CodeFirst - updating woes - entity-framework

I am having a problem with an Entity Framework 6.1.3 CodeFirst Many-to-Many relationship. My model is essentially like so:
class Schedule
{
int Id { get; set; }
}
class Contest
{
int Id { get; set; }
ICollection<Schedule> GameSchedules { get; set; }
}
My context for reference:
class MyContext
{
MyContext() : base("name=DefaultConnection")
{
// no lazy loading for us
this.Configuration.LazyLoadingEnabled = false;
// do not auto detect changes for me
this.Configuration.AutoDetectChangesEnabled = false;
// we don't want our stuff to be wrapped in proxies
this.Configuration.ProxyCreationEnabled = false;
}
}
Entity Farmework CodeFirst configuraiton:
class ContestConfiguration : EntityTypeConfiguration<Contest>
{
ContestConfiguration()
{
// setup many-to-many table between game schedules and contests
this.HasMany(contest => contest.GameSchedules)
.WithMany(schedule => schedule.Contests)
.Map(
contestSchedule =>
{
contestSchedule.MapLeftKey("ContestId");
contestSchedule.MapRightKey("ScheduleId");
contestSchedule.ToTable("ContestSchedule", "something");
});
}
}
Upon creating a contest with an existing schedule I do the following and I see taht two sql statements went in, one that creates the contest and one that creates a record for the MTM table.
Contest Add(Contest entity)
{
// setup schedules
entity.GameSchedules.ToList().ForEach(schedule => this.Context.Entry(schedule).State = EntityState.Unchanged);
// call base add method
return base.Add(entity);
}
However, when I try to Update, its a very different story. I have tried numerous ways, and cannot get CodeFirst to update the relationshiop in the MTM table. It either tries to delete a schedule along with the MTM record or it does nothing. Any ideas on how to accomplish this mind blowing feat?

Ok, figured it out. First and foremost, remove the ManyToManyCascadeDeleteConvention or else on each DELETE from the MTM table, it will try to remove the schedule or contest as well. This is because by default EntityFramework is setting the cascade delete to true.
// remove many-to-many cascade auto delete
modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
now here is the code that updates
1. Ensure that you have copied over the new schedules to a new collection
2. Modify the existing entity with new data (this can be done either before or after the MTM table is updated)
3. To make it simple, I just removed all of the current rows from the MTM table that are linked to this and added the new ones for the sake of brevity.
4. Save your changes to make sure the schedules are deleted
5. Now add the new ones
6. Save your changes again to make sure the new schedules are added
public void Update(Contest entity)
{
// (1) generate new collection so that the reference doesn't get overwritten
var schedulesToAdd = entity.GameSchedules.ToList();
// (2) get entry
var entry = this.context.Entry(entity);
// set entry to modified
entry.State = EntityState.Modified;
// save the current state
this.Context.SaveChanges();
// get the existing context from the db
var existingContest = this.Set.Where(contest => contest.Id == entity.Id).Include(contest => contest.GameSchedules).SingleOrDefault();
// get the object context
var context = ((IObjectContextAdapter)this.Context).ObjectContext;
// iterate over existing relationships and eliminate
foreach (var existingSchedule in existingContest.GameSchedules.ToList())
{
var schedule = this.Context.Schedules.Find(existingSchedule.Id);
// (3) mark relationship as deleted
context.ObjectStateManager.ChangeRelationshipState(existingContest, schedule, contest => contest.GameSchedules, EntityState.Deleted);
}
// (4) save the current state
result = this.SaveChanges();
// iterate over new entity relationships and add them
foreach (var newSchedule in schedulesToAdd)
{
// find the schedule
var schedule = this.Context.Schedules.Find(newSchedule.Id);
// (5) mark relationship as added
context.ObjectStateManager.ChangeRelationshipState(existingContest, schedule, contest => contest.GameSchedules, EntityState.Added);
}
// (6) save the current state
this.SaveChanges();
}
keep in mind, you will want to come up with some more elaborate behavior for removing eixsting relationships and adding new ones. This was just an exmaple.

Related

Issues with Automatically setting created and modified date on each record in EF Core

Using ASP.NET Core 2.2 with EF Core, I have followed various guides in trying to implement the automatic creation of date/time values when creating either a new record or editing/updating an existing one.
The current result is when i initially create a new record, the CreatedDate & UpdatedDate column will be populated with the current date/time.
However first time I edit this same record, the UpdatedDate column is then given a new date/time value (as expected) BUT for some reason, EF Core is wiping out the value of the original CreatedDate which results in SQL assigning a default value.
Required result I need as follows:
Step 1: New row created, both CreatedDate & UpdatedDate column is given a date/time value (this already works)
Step 2: When editing and saving an existing row, I want EF Core to update the UpdatedDate column with the updated date/time only, BUT leave the other CreatedDate column unmodified with the original creation date.
I'm using EF Core code first, and do no want to go down the fluent API route.
One of the guides i was partially following is https://www.entityframeworktutorial.net/faq/set-created-and-modified-date-in-efcore.aspx but neither this or other solutions I've tried is giving the result I am after.
Baseclass:
public class BaseEntity
{
public DateTime? CreatedDate { get; set; }
public DateTime? UpdatedDate { get; set; }
}
DbContext Class:
public override Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default(CancellationToken))
{
var entries = ChangeTracker.Entries().Where(E => E.State == EntityState.Added || E.State == EntityState.Modified).ToList();
foreach (var entityEntry in entries)
{
if (entityEntry.State == EntityState.Modified)
{
entityEntry.Property("UpdatedDate").CurrentValue = DateTime.Now;
}
else if (entityEntry.State == EntityState.Added)
{
entityEntry.Property("CreatedDate").CurrentValue = DateTime.Now;
entityEntry.Property("UpdatedDate").CurrentValue = DateTime.Now;
}
}
return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
}
UPDATE FOLLOWING ADVICE FROM STEVE IN COMMENTS BELOW
I spent a bit more time debugging today, turns out the methods I posted above are appear to be functioning as expected i.e. when editing an existing row and saving it, only the entityEntry.State == EntityState.Modified IF statement is being called. So what I'm finding is that after saving the entity, the CreatedDate column is being overwitten with a Null value, I can see this by watching the SQL explorer after a refresh. I believe the issue is along the lines of what Steve mentions below "If it is #null then this might also explain the behavior in that it is not being loaded with the entity for whatever reason."
But i'm a little lost in tracing where this CreatedDate value is being dropped somewhere through edit/save process.
Image below shows the result at the point just before the entity is saved following an update. In the debugger I'm not quite sure where to find the entry of the CreatedDate to see what value is held at this step, but it appears to be missing from the debugger list so wandering whether somehow it doesn't know about the existence of this field when saving.
Below is the method I have in my form 'Edit' Razor page model class:
public class EditModel : PageModel
{
private readonly MyProject.Data.ApplicationDbContext _context;
public EditModel(MyProject.Data.ApplicationDbContext context)
{
_context = context;
}
[BindProperty]
public RuleParameters RuleParameters { get; set; }
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null)
{
return NotFound();
}
RuleParameters = await _context.RuleParameters
.Include(r => r.SystemMapping).FirstOrDefaultAsync(m => m.ID == id);
if (RuleParameters == null)
{
return NotFound();
}
ViewData["SystemMappingID"] = new SelectList(_context.SystemMapping, "ID", "MappingName");
return Page();
}
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Attach(RuleParameters).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!RuleParametersExists(RuleParameters.ID))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToPage("./Index");
}
private bool RuleParametersExists(int id)
{
return _context.RuleParameters.Any(e => e.ID == id);
}
}
Possibly one of the reasons for this issue is the fact that I have not included the CreatedDate field in my Edit Razor Page form, so when I update the entity which in turn will run the PostAsync method server side, there is no value stored for the CreatedDate field and therefore nothing in the bag by the tine the savechangesasync method is called in my DbContext Class. But I also didn't think this was necessary? otherwise I'd struggle to see what value there is in the this process of using an inherited BaseEntity class i.e. not having to manually add the CreatedDate & UpdatedDate attribute to every model class where I want to use it...
It may be easier to just give your BaseEntity a constructor:
public BaseEntity()
{
UpdatedDate = DateTime.Now;
CreatedDate = CreatedDate ?? UpdatedDate;
}
Then you can have your DbContext override SaveChangesAsync like:
public override Task<int> SaveChangesAsync(
bool acceptAllChangesOnSuccess,
CancellationToken token = default)
{
foreach (var entity in ChangeTracker
.Entries()
.Where(x => x.Entity is BaseEntity && x.State == EntityState.Modified)
.Select(x => x.Entity)
.Cast<BaseEntity>())
{
entity.UpdatedDate = DateTime.Now;
}
return base.SaveChangesAsync(acceptAllChangesOnSuccess, token);
}
Possibly one of the reasons for this issue is the fact that I have not included the CreatedDate field in my Edit Razor Page form, so when I update the entity which in turn will run the PostAsync method server side, there is no value stored for the CreatedDate field and therefore nothing in the bag by the tine the savechangesasync method is called in my DbContext Class.
That's true.Your post data does not contains the original CreatedDate,so when save to database, it is null and could not know what the exact value unless you assign it before saving.It is necessary.
You could just add below code in your razor form.
<input type="hidden" asp-for="CreatedDate" />
Update:
To overcome it in server-side,you could assign data manually:
public async Task<IActionResult> OnPostAsync()
{
RuleParameters originalData = await _context.RuleParameters.FirstOrDefaultAsync(m => m.ID == RuleParameters.ID);
RuleParameters.CreatedDate = originalData.CreatedDate;
_context.Attach(RuleParameters).State = EntityState.Modified;
await _context.SaveChangesAsync();
}
I don't suspect EF is doing this, but rather your database, or you're inadvertently inserting records instead of updating them.
A simple test: Put break-points in your SaveChangesAsnc method within both the Modified and Added handlers and then run a unit test that loads an entity, edits it, and saves. Which breakpoint is hit? If the behavior seems to be normal with a simple unit test, repeat again with your code.
If the Modified breakpoint is hit, and only the Modified handler is hit then check the state of the CreatedDate value in the entity modified. Does it still reflect the original CreatedDate? If yes, then it would appear that something in your schema will be overwriting it on save. If no then you have a bug in your code that has caused it to update. If it is #null then this might also explain the behaviour in that it is not being loaded with the entity for whatever reason. Check that the property has not been configured as something like a Computed property.
If the Added breakpoint is hit at all, then this would point at a scenario where you're dealing with a detached entity, such as an entity that was read from a different DB Context and being associated to another entity in the current DB Context and saved as a byproduct. When a DbContext encounters an entity that was loaded and disassociated with a different DbContext, it will treat that entity as a completely new entity and insert a new record. The biggest single culprit for this is invariably MVC code where people pass entities to/from views. Entity references are loaded in one request, serialized to the view, and then passed back on another request. Devs assume they are receiving an entity that they can just associate to a new entity and save, but the Context of this request doesn't know about that entity, and that "entity" isn't actually an entity, it is now a POCO shell of data that the serializer created. It's no different to you newing up a new class and populating fields. EF won't know the difference. The result of this is you will trip the Added condition for your entity, and after completion you will have a duplicate record. (with different PK if EF is configured to treat PKs as Identity)
So an example is an Order screen: When presenting a screen to create a new order I may have loaded the Customer and passed that to the view to display customer information and will want to associate to the new order:
var customer = context.Customers.Single(x => x.CustomerId == 15);
var newOrder = new Order { Customer = customer };
return View(newOrder);
This looks innocent enough. When we go to save the new order after setting their details:
public ActionResult Save(Order newOrder)
{
context.Orders.Add(newOrder);
newOrder.Customer.Orders.Add(newOrder);
context.SaveChanges();
// ...
}
newOrder had a reference to Customer #14, so all looks good. We're even associating the new order to the customer's order collection. We might even want to have updated fields on the customer record to reflect a change to the Modified date. However, newOrder in this case, and all associated data including .Customer are plain 'ol C# objects at this point. We've added the new order to the Context, but as far as the context is concerned, the Customer referenced is also a new record. It will ignore the Customer ID if that is set as an Identity column and it will save a brand new Customer record (ID #15 for example) with all of the same details as Customer ID 14 and associate that to the new order. It can be subtle and easy to miss until you start querying Customers and spotting duplicate looking rows.
If you are passing entities to/from views, I'd be very wary of this gotcha. Attaching and setting modified state is one option, but that involves trusting that the data has not been tampered with. As a general rule, calls to update entities should never pass entities & attach them, but rather re-load those entities, validate row version, validate the data coming in, and only copy across fields you expect should ever be modified before saving the entity associated to the DbContext.
Hopefully that gives you a few ideas on things to check to get to the bottom of the issue.

How to avoid parent entities beeing reinserted in EF6 - dealing with many to many relationship?

Im new to EF6 and facing the following issue:
If I add my model via db.Modelclass.Add( model )-method and save the changes to the db new entities will be created in the tables Tours and ToursEmployees like it should for the model, but also in my parent table Employees. Last one is my use in the code below.
I already read this article about the issue https://msdn.microsoft.com/en-us/magazine/dn166926.aspx but in my case I dont know what to do, cause Im dealing with a many-to-many realtionship. I used code first and marked the artibutes in the models via virtual as navigation properties, so the framework created me a join table.
Whats the right way to use the framework for this kinda relaltionships, maybe creating a model for the joined table and then using the FK described in the article?
public ActionResult Create(TourViewModel tourVM) {
if (ModelState.IsValid) {
Tour tour = new Tour() {
Tourname = tourVM.Tourname,
Tourdate = tourVM.Tourdate,
VehicleId = tourVM.VehicleId
,Employees = new List<Employee>()
};
for (var i = 0; i < tourVM.Employees.Count; i++)
if (tourVM.Employees[i].Assigned)
tour.Employees.Add(
new Employee() { EmployeeId = tourVM.Employees[i].EmployeeId }
);
db.Tours.Add(tour);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(tourVM);
}
I believe this is happening because you are adding the employees to the list that are not bound to the data context. Doing something like the below should work and correctly bind them to the data context object.
public ActionResult Create(TourViewModel tourVM) {
if (ModelState.IsValid) {
Tour tour = new Tour() {
Tourname = tourVM.Tourname,
Tourdate = tourVM.Tourdate,
VehicleId = tourVM.VehicleId,
Employees = new List<Employee>()
};
var IDs = tourVM.Employees.Where(e => e.Assigned).Select(e => e.EmployeeId);
var Employees = db.Employees.Where(e => IDs.Contains(e.EmployeeId)).ToList();
tour.Employees.AddRange(Employees);
db.Tours.Add(tour);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(tourVM);
}

Delete data sith Breeze.js without loading it to client

I am using Breeze.js with Entity Framework WebAPI backend, and I need to delete a large set of data that is not loaded to client. I would really like to do it on the server and not load it.
Is there a "breeze way"? By that I mean a method in a BreezeController.
EDIT
I have to delete all rows from one table that belong to the user, whose date field is in future, and all their child rows.
public override int SaveChanges()
{
foreach (
var entry in
this.ChangeTracker.Entries()
.Where((e => (e.State == (EntityState) Breeze.WebApi.EntityState.Deleted))))
{
if (entry.Entity.GetType() == typeof(User))
{
var entity = entry.Entity as User;
var childEntitiesInFuture = ChildEntities.Where(c => c.DateField > DateTime.Now);
foreach (var child in childEntitiesInFuture){
var grandchildrenForDeletion = Grandchildren.Where(c => c.ChildId == child.Id);
foreach (var g in grandchildrenForDeletion) Grandchildren.Remove(g);
ChildEntities.Remove(child);
}
}
}
}
Assuming you are deleting User, one User has many ChildEntity saved in ChildEntities and each ChildEntity has many Grandchild saved in Grandchildren. A bit messy names, but that's what you get with no real names :)
This method goes into your Context class. Good luck.

How can I create a generic update method for One to Many structures in Entity Framework 5?

I am writing a web application, such that I get different objects back from the web that need to be either updated or added to the database. On top of this, I need to check that the owner is not modified. Since a hacker could potentially get an account and send an update to modify the foreign key to the user model. I don't want to have to manually code all of these methods, instead I want to make a simple generic call.
Maybe something as simple as this
ctx.OrderLines.AddOrUpdateSet(order.OrderLines, a => a.Order)
Based on old persisted records that have a foreign key to Order, and on the new incoming records.
Delete old records that are not on the new records list.
Add new records that are not on the old records list.
Update new records that exist on both lists.
ctx.Entry(orderLine).State=EntityState.Deleted;
...
ctx.Entry(orderLine).State=EntityState.Added;
...
ctx.Entry(orderLine).State=EntityState.Modified;
This gets a bit complicated when the old record is loaded to verify that ownership did not change. I get an error if I don't do.
oldorder.OrderLines.remove(oldOrderLine); //for deletes
oldorder.OrderLines.add(oldOrderLine); //for adds
ctx.Entry(header).CurrentValues.SetValues(header); //for modifications
With Entity Framework 5 there is a new extension function called AddOrUpdate. And there was a very interesting (please read) blog entry on how to create this method before it was added.
I'm not sure if this is too much to ask as a question in StackOverflow, any clues on how to approach the problem may be sufficient. Here are my thoughts so far:
a) leverage AddOrUpdate for some of the functionality.
b) create a secondary context hoping to avoid loading order into the context and avoid extra calls.
c) Set the state of all the saved objects to initially deleted.
Since you have linked to this question from my own question, I thought I'd throw in some newly-aquired experience with Entity Framework for me.
To achieve a common save method in my generic repository with Entity Framework, I do this. (Please note that the Context is a member of my repository, as I am implementing the Unit of Work pattern as well)
public class EFRepository<TEntity> : IRepository<TEntity> where TEntity : class
{
internal readonly AwesomeContext Context;
internal readonly DbSet<TEntity> DbSet;
public EFRepository(AwesomeContext context)
{
if (context == null) throw new ArgumentNullException("context");
Context = context;
DbSet = context.Set<TEntity>();
}
// Rest of implementation removed for brevity
public void Save(TEntity entity)
{
var entry = Context.Entry(entity);
if (entry.State == EntityState.Detached)
DbSet.Add(entity);
else entry.State = EntityState.Modified;
}
}
Honestly, I can't tell you why this works, because I just kept changing the state conditions - however I do have unit (integration) tests to prove that it works. Hopefully someone more into EF than myself can shed some light on this.
Regarding the "cascading updates", I was curious myself as if it would work using the Unit of Work pattern (my question I linked to was when I did not know it existed, and my repositories would basically create a unit of work whenever I wanted to save/get/delete, which is bad), so I threw in a test case in a simple relational DB. Here is a diagram to give you an idea.
IMPORTANT In order for test case number 2 to work, you need to make your POCO reference properties virtual, in order for EF to provide lazy loading.
The repository implementation is just derived from the generic EFRepository<TEntity> as shown above, so I'll leave out that implementation.
These are my test cases, both pass.
public class EFResourceGroupFacts
{
[Fact]
public void Saving_new_resource_will_cascade_properly()
{
// Recreate a fresh database and add some dummy data.
SetupTestCase();
using (var ctx = new LocalizationContext("Localization.CascadeTest"))
{
var cultureRepo = new EFCultureRepository(ctx);
var resourceRepo = new EFResourceRepository(cultureRepo, ctx);
var existingCulture = cultureRepo.Get(1); // First and only culture.
var groupToAdd = new ResourceGroup("Added Group");
var resourceToAdd = new Resource(existingCulture,"New Resource", "Resource to add to existing group.",groupToAdd);
// Verify we got a single resource group.
Assert.Equal(1,ctx.ResourceGroups.Count());
// Saving the resource should also add the group.
resourceRepo.Save(resourceToAdd);
ctx.SaveChanges();
// Verify the group was added without explicitly saving it.
Assert.Equal(2, ctx.ResourceGroups.Count());
}
// try creating a new Unit of Work to really verify it has been persisted..
using (var ctx = new LocalizationContext("Localization.CascadeTest"))
{
Assert.DoesNotThrow(() => ctx.ResourceGroups.First(rg => rg.Name == "Added Group"));
}
}
[Fact]
public void Changing_existing_resources_group_saves_properly()
{
SetupTestCase();
using (var ctx = new LocalizationContext("Localization.CascadeTest"))
{
ctx.Configuration.LazyLoadingEnabled = true;
var cultureRepo = new EFCultureRepository(ctx);
var resourceRepo = new EFResourceRepository(cultureRepo, ctx);
// This resource already has a group.
var existingResource = resourceRepo.Get(2);
Assert.NotNull(existingResource.ResourceGroup); // IMPORTANT: Property must be virtual!
// Verify there is only one resource group in the datastore.
Assert.Equal(1,ctx.ResourceGroups.Count());
existingResource.ResourceGroup = new ResourceGroup("I am implicitly added to the database. How cool is that?");
// Make sure there are 2 resources in the datastore before saving.
Assert.Equal(2, ctx.Resources.Count());
resourceRepo.Save(existingResource);
ctx.SaveChanges();
// Make sure there are STILL only 2 resources in the datastore AFTER saving.
Assert.Equal(2, ctx.Resources.Count());
// Make sure the new group was added.
Assert.Equal(2,ctx.ResourceGroups.Count());
// Refetch from store, verify relationship.
existingResource = resourceRepo.Get(2);
Assert.Equal(2,existingResource.ResourceGroup.Id);
// let's change the group to an existing group
existingResource.ResourceGroup = ctx.ResourceGroups.First();
resourceRepo.Save(existingResource);
ctx.SaveChanges();
// Assert no change in groups.
Assert.Equal(2, ctx.ResourceGroups.Count());
// Refetch from store, verify relationship.
existingResource = resourceRepo.Get(2);
Assert.Equal(1, existingResource.ResourceGroup.Id);
}
}
private void SetupTestCase()
{
// Delete everything first. Database.SetInitializer does not work very well for me.
using (var ctx = new LocalizationContext("Localization.CascadeTest"))
{
ctx.Database.Delete();
ctx.Database.Create();
var culture = new Culture("en-US", "English");
var resourceGroup = new ResourceGroup("Existing Group");
var resource = new Resource(culture, "Existing Resource 1",
"This resource will already exist when starting the test. Initially it has no group.");
var resourceWithGroup = new Resource(culture, "Exising Resource 2",
"Same for this resource, except it has a group.",resourceGroup);
ctx.Cultures.Add(culture);
ctx.ResourceGroups.Add(resourceGroup);
ctx.Resources.Add(resource);
ctx.Resources.Add(resourceWithGroup);
ctx.SaveChanges();
}
}
}
It was interesting to learn this, as I was not sure if it would work.
After working on this for a while I found an opensource project called GraphDiff here is it's blog entry 'introducing graphdiff for entity framework code first – allowing automated updates of a graph of detached entities'. I only began using it but it looks impressive. And it does solve the problem of issuing update/delete/insert for Many to One relationships. It actually generalizes the problem to graphs and allows arbitrary nesting.
Here is the generic method I concocted. It does use AddOrUpdate from the System.Data.Entity.Migrations namespace. Which may be reloading records from the db, I'll be checking on that later. The usage is
ctx.OrderLines.AddOrUpdateSet(l => l.orderId == neworder.Id,
l => l.Id, order.orderLines);
Here is the code:
public static class UpdateExtensions
{
public static void AddOrUpdateSet<TEntity>(this IDbSet<TEntity> set, Expression<Func<TEntity, bool>> predicate,
Func<TEntity, int> selector, IEnumerable<TEntity> newRecords) where TEntity : class
{
List<TEntity> oldRecords = set.Where(predicate).ToList();
IEnumerable<int> keys = newRecords.Select(selector);
foreach (TEntity newRec in newRecords)
set.AddOrUpdate(newRec);
oldRecords.FindAll(old => !keys.Contains(selector(old))).ForEach(detail => set.Remove(detail));
}
}

In Entity Framework, take a newly created object and use it to update an existing record

Here's what I'd like to do:
var myCustomer = new Customer();
myCustomer.Name = "Bob";
myCustomer.HasAJob = true;
myCustomer.LikesPonies = false;
Then I'd like to pass it into an update method:
public UpdateCustomer(Customer cust)
{
using(var context = dbcontext())
{
var dbCust = context.Customers.FirstOrDefault(c => c.Name == cust.Name);
if(dbCust != null)
{
// Apply values from cust here so I don't have to do this:
dbCust.HasAJob = cust.HasAJob;
dbCust.LikesPonies = cust.LikesPonies
}
context.SaveChanges();
}
}
The reason for this is I'm working in multiple different parts of my application, and/or across DLLs. Is this possible?
EDIT: Found this question to be immensely useful:
Update Row if it Exists Else Insert Logic with Entity Framework
If you are sure that the entity is in the database and you have key you would just Attach the object you have to the context. Note that attached entities are by default in Unchanged state as the assumption is that all the values of properties are the same as in the database. If this is not the case (i.e. values are different) you need to change the state of the entity to modified. Take a look at this blog post: http://blogs.msdn.com/b/adonet/archive/2011/01/29/using-dbcontext-in-ef-feature-ctp5-part-4-add-attach-and-entity-states.aspx it describes several sceanrios including the one you are asking about.