none
EntityFrameworkCore - Update vs Add RRS feed

  • Question

  • I was looking for "Upsert" functionality, and decided to see what would happen if I simply created a new entity and put it through an Update call, rather than calling Add. Happily, a new entity was added, and an identity Id was assigned to my local variable. This seems to be the Upsert I was looking for, but that begs the question... if Update will either automatically add or update an entity depending on whether it exists or not, then what's the use of the Add method? 

    Is there a reason not to simply use Update everywhere? 

    Thursday, May 23, 2019 11:28 PM

All replies

  • Hi William Snell,

    Here is the official documents you can follow.

    DbContext.Add Method;

    DbContext.Update Method.

    And here is an article you can refer to.

    Add, Attach, Update, and Remove methods in EF Core 1.1

    Regards,

    Kyle

    Note: This response contains a reference to a third party World Wide Web site. Microsoft is providing this information as a convenience to you. Microsoft does not control these sites and has not tested any software or information found on these sites; Therefore, Microsoft cannot make any representations regarding the quality, safety, or suitability of any software or information found there. There are inherent dangers in the use of any software found on the Internet, and Microsoft cautions you to make sure that you completely understand the risk before retrieving any software from the Internet.


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Friday, May 24, 2019 7:13 AM
    Moderator
  • Is there a reason not to simply use Update everywhere? 

    Maybe, the EF code figured out that you didn't know what you were doing and it did it for you anyway, kind of like a Mr. Wizard thing. :)

    What you did would be nothing I would take to the bank on a routine basis. I would do things like they are suppose to be done in order to not get caught in something that could potentially fail, becuase of cowboy yippee ki yay usage. .

    Friday, May 24, 2019 7:54 AM
  • They are different.

    Add creates a newly created instance of your DbSet type to the DbContext with the Added EntityState. When you call SaveChanges() on the the DbContext it will generate an insert statement.

    The Update method results in the entity being tracked by the context as Modified. When you call SaveChanges() on the DbContext it will generate an update statement.



    william xifaras


    Friday, May 24, 2019 12:14 PM
  • Kyle, thanks for the reply, but I still don't really know anything useful. The documentation page for "Add" simply had this text, which was repeated on the page 4 times, with little else: 

    "Begins tracking the given entity, and any other reachable entities that are not already being tracked, in the Added state such that they will be inserted into the database when SaveChanges() is called."

    The Update page had this: 

    "Begins tracking the given entity in the Modified state such that it will be updated in the database when SaveChanges() is called.

    All properties of the entity will be marked as modified. To mark only some properties as modified, useAttach(Object) to begin tracking the entity in the Unchanged state and then use the returned EntityEntryto mark the desired properties as modified."

    The only difference seems to be that the properties are all marked as "Modified" when using Update. If the entity does not exist yet and is being inserted, I don't see what functional difference this would make. If calls to Update will handle inserts, then this would be the Upsert functionality I was looking for. Otherwise, I'd need to create an extension method or manually attempt to select the entity from the database and test for its existence. I really hate code like that, though an extension method would clean it up quite a bit. 

    These two calls produce nearly the same results: 

    var student = _context.Student.Add(new Student
    {
    	Active = true, 
    	FirstName = viewModel.Student.FirstName, 
    	LastName = viewModel.Student.LastName, 
    	UserName = viewModel.Student.FirstName[0] + viewModel.Student.LastName, 
    	Email = viewModel.Student.Email, 
    	Phone = viewModel.Student.Phone
    });
    
    _context.SaveChanges();
    
    var student = new Student
    {
    	Active = true,
    	FirstName = viewModel.Student.FirstName,
    	LastName = viewModel.Student.LastName,
    	UserName = viewModel.Student.FirstName[0] + viewModel.Student.LastName,
    	Email = viewModel.Student.Email,
    	Phone = viewModel.Student.Phone
    };
    
    _context.Student.Update(student);
    
    _context.SaveChanges();

    The only difference I found was that if I used Add, the entity returned to my local variable had to be accessed like this:

    student.Entity.Id

    rather than simply:

    student.Id

    I'll go with using Add explicitly for now until I better understand the differences. 


    Friday, May 24, 2019 3:35 PM
  • SaveChanges() is what generates the actual insert or update SQL. Update or Add are state markers in the DbContext. See my answer.

    william xifaras

    Friday, May 24, 2019 4:27 PM
  • They are different.

    Add creates a newly created instance of your DbSet type to the DbContext with the Added EntityState. When you call SaveChanges() on the the DbContext it will generate an insert statement.

    The Update method results in the entity being tracked by the context as Modified. When you call SaveChanges() on the DbContext it will generate an update statement.



    william xifaras


    That's in a connected state, and then there is the disconnected state.

    https://www.c-sharpcorner.com/UploadFile/d87001/connected-and-disconnected-scenario-in-entity-framework/

    The disconnected state also applies to a Web based solution, since it is a stateless solution running on a Web server.

    Friday, May 24, 2019 7:52 PM
  • SaveChanges() is what generates the actual insert or update SQL. Update or Add are state markers in the DbContext. See my answer.

    william xifaras

    The EF Engine formulates the T-SQL and submits the T-SQL to the MS SQL Server DB engine for execution via means of a batch process or by usage of the internal MS SQL Server Stored Procedure.

    https://blogs.msdn.microsoft.com/bindeshv/2010/07/12/ef-query-execution-pattern-usage-of-sp_executesql-vs-direct-execution-of-sql-statement/


    • Edited by DA924x Friday, May 24, 2019 8:30 PM
    Friday, May 24, 2019 8:22 PM
  • Ok, but not relevant to the question. That article is from 2010.

    william xifaras


    Friday, May 24, 2019 8:26 PM
  • You are correct that SaveChanges() will generate a batch of SQL. That article is from 2010. I'd reference newer EF Core Architecture or source code .

    william xifaras

    Friday, May 24, 2019 8:28 PM
  • Ok, but not relevant to the question. That article is from 2010.

    william xifaras


    It doesn't make a difference how old the article is  in using architectural design and UI design patterns, such as MVC and DAO with DAO in the Data Access Layer that is sitting behind the WebAPI using the DTO pattern, which are being used in the ASP.NET Core solution doing CRUD operations.

    https://www.tutorialspoint.com/design_pattern/data_access_object_pattern.htm

    https://www.codeproject.com/Articles/1050468/Data-Transfer-Object-Design-Pattern-in-Csharp

    https://docs.microsoft.com/en-us/aspnet/web-api/overview/data/using-web-api-with-entity-framework/part-5

    Just becuase something doesn't have recent dates doesn't mean that the information is not relevant in current technology usage. :)

    using System.Collections.Generic;
    using System.Linq;
    using System.Transactions;
    using DAL.Models.DB;
    using Entities;
    using Microsoft.EntityFrameworkCore;
    using Microsoft.Extensions.Options;
    
    namespace DAL
    {
        public class DaoProject :IDaoProject
        {
            private readonly IOptions<ConnectionStrings> _options;
            
            public DaoProject(IOptions<ConnectionStrings> options)
            {
                _options = options;
            }
    
            public DtoProject GetProjectById(int id)
            {
                var dto = new DtoProject();
    
                using (var context = new ProjectManagementContext(_options))
                {
                    var project = (context.Projects.Where(a => a.ProjectId == id)).SingleOrDefault();
    
                    if (project == null) return dto;
                    dto.ProjectId = project.ProjectId;
                    dto.ClientName = project.ClientName;
                    dto.ProjectName = project.ProjectName;
                    dto.Technology = project.Technology;
                    dto.ProjectType = project.ProjectType;
                    dto.UserId = project.UserId;
                    dto.StartDate = project.StartDate;
                    dto.EndDate = project.EndDate;
                    dto.Cost = project.Cost;
                }
    
                return dto;
            }
    
            public List<DtoProject> GetProjectsByUserId(string userid)
            {
                var dtos = new List<DtoProject>();
    
                using (var context = new ProjectManagementContext(_options))
                {
                    
                    dtos = (from a in context.Projects.Where(a => a.UserId.Contains(userid))
                        select new DtoProject
                        {
                            ProjectId = a.ProjectId,
                            ClientName = a.ClientName,
                            ProjectName = a.ProjectName,
                            Technology = a.Technology,
                            ProjectType = a.ProjectType,
                            UserId = a.UserId,
                            StartDate = a.StartDate,
                            EndDate = a.EndDate,
                            Cost = a.Cost
                        }).ToList();
                }
    
                return dtos;
            }
    
            public void CreateProject(DtoProject dto)
            {
                using (var context = new ProjectManagementContext(_options))
                {
                    var project = new Projects
                    {
                        ClientName = dto.ClientName,
                        ProjectName = dto.ProjectName,
                        Technology = dto.Technology,
                        ProjectType = dto.ProjectType,
                        UserId = dto.UserId,
                        StartDate = dto.StartDate,
                        EndDate = dto.EndDate,
                        Cost = dto.Cost
                    };
    
                    context.Projects.Add(project);
                    context.SaveChanges();
               }
            }
    
            public void UpdateProject(DtoProject dto)
            {
                var project = new Projects();
                
                using (var context = new ProjectManagementContext(_options))
                {
                   project = (context.Projects.Where(a => a.ProjectId == dto.ProjectId)).SingleOrDefault();
                }
    
                if (project != null)
                {
                    project.ClientName = dto.ClientName;
                    project.ProjectName = dto.ProjectName;
                    project.Technology = dto.Technology;
                    project.ProjectType = dto.ProjectType;
                    project.UserId = dto.UserId;
                    project.StartDate = dto.StartDate;
                    project.EndDate = dto.EndDate;
                    project.Cost = dto.Cost;
                }
    
                using (var dbcontext = new ProjectManagementContext(_options))
                {
                    if (project == null) return;
                    dbcontext.Entry(project).State = EntityState.Modified;
                    dbcontext.SaveChanges();
                }
            }
    
            public void DeleteProject(int id)
            {
                Projects project;
    
                using (var context = new ProjectManagementContext(_options))
                {
                   project = (context.Projects.Where(a => a.ProjectId == id)).SingleOrDefault();
                }
    
                if (project == null) return;
    
                using (var newContext = new ProjectManagementContext(_options))
                {
                   
                    var tasks = new DaoTask(_options).GetTasksByProjectId(project.ProjectId);
                    using (TransactionScope scope = new TransactionScope())
                    {
                        foreach (var task in tasks)
                        {
                            new DaoTask(_options).DeleteTask(task.TaskId);
                        }
    
                        newContext.Entry(project).State = EntityState.Deleted;
                        newContext.SaveChanges();
    
                        scope.Complete();
                    }
                }
            }
        }
    }
    using System.Collections.Generic;
    using DAL;
    using Entities;
    using Microsoft.AspNetCore.Mvc;
    
    namespace ProgMgmntCore2Api.Controllers
    {
        [Produces("application/json")]
        [Route("api/[controller]")]
        [ApiController]
    
        public class ProjectController : ControllerBase, IProjectController
        {
            private readonly IDaoProject _daoProject;
    
            public ProjectController(IDaoProject daoProject)
            {
                _daoProject = daoProject;
            }
    
            [HttpGet]
            [Route("GetProjById")]
            public DtoProject GetProjectById(int id)
            {
                return  _daoProject.GetProjectById(id);
            }
            
            [HttpGet]
            [Route("GetProjsByUserId")]
            public List<DtoProject> GetProjectsByUserId(string userid)
            {
                return _daoProject.GetProjectsByUserId(userid);
            }
    
            [HttpPost]
            [Route("CreateProject")]
            public void Post_CreateProject(DtoProject dto)
            {
                _daoProject.CreateProject(dto);
            }
    
            [HttpPost]
            [Route("DeleteProject")]
            public void Post_DeleteProject(DtoId dto)
            {
                _daoProject.DeleteProject(dto.Id);
            }
    
            [HttpPost]
            [Route("UpdateProject")]
            public void Post_UpdateProject(DtoProject dto)
            {
                _daoProject.UpdateProject(dto);
            }
        }
    }



    • Edited by DA924x Friday, May 24, 2019 9:07 PM
    Friday, May 24, 2019 8:58 PM
  • You are correct that SaveChanges() will generate a batch of SQL. That article is from 2010. I'd reference newer EF Core Architecture or source code .

    william xifaras


    I kind of doubt that EF wouldn't be submitting T-SQL to the MS SQL Server internal sproc  and use all the advantages in using the sproc like another sproc that you or a DBA created. 
    Friday, May 24, 2019 9:03 PM
  • EF uses a provider model in which you must translate Entity Framework command trees into SQL. EF core also uses a provider model.

    https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/ef/sql-generation


    william xifaras

    Tuesday, May 28, 2019 9:15 PM
  • EF uses a provider model in which you must translate Entity Framework command trees into SQL. EF core also uses a provider model.

    https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/ef/sql-generation


    william xifaras

    Well. the EF engine and the DB engine,  winch submits the T-SQL to the to the DB,  are two different engines The provider model for EF  was there long before EF Core. And it doesn't explain how the DB engine executes the submitted T-SQL, other than what I have shown. 

    Wednesday, May 29, 2019 2:01 PM
  • You will see how it all works if you look at the sourcecode. Its all open source now.

    william xifaras

    Wednesday, May 29, 2019 3:03 PM