Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException - c#

I'm trying to update an entity but when I do saveChangesAsync() I'm getting this error message:
Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException: The database operation was expected to affect 1 row(s), but actually affected 0 row(s); data may have been modified or deleted since entities were loaded.
Before updating, I'm getting this entity like this in my repository class:
public IQueryable<Profile> getProfileByUserEmail(string userEmail)
{
return _context.Profiles
.Include(pr => pr.Photos)
.AsQueryable()
.Where(p => p.UserEmail == userEmail);
}
In my service, I call my repository class and materialize the query with the FirstOrDefaulAsync() like this:
public async Task<Profile> getProfileByUserEmail(string userEmail)
=> await _repository.getProfileByUserEmail(userEmail).FirstOrDefaultAsync();
In my controller I get the profile, add a new photo and update it, like this:
var profile = await _service.getProfileByUserEmail(userEmail: model.UserEmail);
Photo newPhoto = new Photo();
newPhoto.Description = model.Description;
newPhoto.Id = Guid.NewGuid();
profile.Photos.Add(newPhoto);
var updated = await _service.update(profile);
This is my Profile entity:
public class Profile
{
[Key]
public Guid Id { get; set; }
public DateTime CreationDate { get; set; }
public string ProfilePhoto { get; set; }
public HashSet<Photo> Photos { get; set; }
}
and my Photo entity:
public class Photo
{
[Key]
public Guid Id { get; set; }
public string Uri { get; set; }
[StringLength(1000)]
public string Description { get; set; }
public Guid ProfileId { get; set; }
public virtual Profile Profile { get; set; }
}
I have found on the internet the reason but not a possible solution, if anybody could help that would be great, thanks in advance.

Related

Entity Framework Core not filling object by foreign key on update

I see this error when updating an Application. The object has two external connections: ApplicationVisitors and ApplicationPlatforms. The properties in Application have been updated, but external connections was not be updated.
What am I doing wrong? How to update Application correctly?
Route
[Route("update")]
[HttpPut]
public async Task<IActionResult> UpdateApplication(ApplicationDTO applicationDTO)
{
if (!ModelState.IsValid)
return BadRequest("Модель не валидна!");
await applicationService.UpdateApplication(applicationDTO);
return Ok();
}
Service
public async Task UpdateApplication(ApplicationDTO applicationDTO)
{
var visitors = Mapper.ToVisitors(applicationDTO.ApplicationVisitors);
var visitorsToCreate = visitors.Where(w => w.Id == 0).ToList();
var createdVisitors = visitors.Where(w => w.Id > 0).ToList();
var resultCreateVisitors = await _wrapper.Visitor.CreateVisitorsAsync(visitorsToCreate);
createdVisitors.AddRange(resultCreateVisitors);
applicationDTO.ApplicationVisitors = Mapper.ToVisitors(createdVisitors);
await _wrapper.Application.UpdateAsync(Mapper.ToApplication(applicationDTO));
}
Repository method
public async Task UpdateAsync(Application application)
{
Update(application);
await SaveAsync();
}
BaseRepository
public void Update(T entity)
{
_repositoryContext.Set<T>().Attach(entity);
_repositoryContext.Entry(entity).State = EntityState.Modified;
}
public async Task SaveAsync()
{
await _repositoryContext.SaveChangesAsync();
}
I have not any exeption in debug. Application was filled by ID, but Platform and Visitor in collections ApplicationPlatforms and ApplicationVisitors does not filling by foreign key.
References is existing in classes.
view result Attach
Application
public class Application
{
public int Id { get; set; }
[Column(TypeName="date")]
public DateTime DateStart { get; set; }
[Column(TypeName = "date")]
public DateTime DateEnd { get; set; }
public int ApproverId { get; set; }
public User Approver { get; set; }
public int StatusId { get; set; }
public ApplicationStatus Status { get; set; }
public string VisitPurpose { get; set; }
public DateTime CreatedAt { get; set; }
public int AuthorId { get;set; }
public User Author { get; set; }
public IList<Draft> Drafts { get; set; }
public IList<ApplicationVisitor> ApplicationVisitors { get; set; }
public IList<ApplicationPlatform> ApplicationPlatforms { get; set; }
public IList<Pass> Passes { get; set; }
}
ApplicationVisitor
public class ApplicationVisitor
{
public int ApplicationId { get; set; }
[ForeignKey("ApplicationId")]
public Application Application { get; set; }
public int VisitorId { get; set; }
[ForeignKey("VisitorId")]
public Visitor Visitor { get; set; }
}
ApplicationPlatform
public class ApplicationPlatform
{
public int ApplicationId { get; set; }
[ForeignKey("ApplicationId")]
public Application Application { get; set; }
public int PlatformId { get; set; }
[ForeignKey("PlatformId")]
public Platform Platform { get; set; }
}
UPD: 22.10.2019. This is my solution what working for me on base selected answer!
I rewrited update method in ApplicationRepository
public async Task UpdateAsync(Application application)
{
var app = await GetAsync(application.Id);
app.DateStart = application.DateStart;
app.DateEnd = application.DateEnd;
app.ApproverId = application.ApproverId;
app.StatusId = application.StatusId;
app.VisitPurpose = application.VisitPurpose;
app.CreatedAt = application.CreatedAt;
app.AuthorId = application.AuthorId;
app.ApplicationVisitors = application.ApplicationVisitors;
app.ApplicationPlatforms = application.ApplicationPlatforms;
Update(app);
await SaveAsync();
}
And rewrided method in my BaseRepository
public void Update(T entity)
{
_repositoryContext.Set<T>().Update(entity);
}
Although you attached the Application object and set its state to Modified, all other objects references by it will be in Unchanged state, as stated here:
For entity types with generated keys if an entity has its primary key
value set then it will be tracked in the Unchanged state. If the
primary key value is not set then it will be tracked in the Added
state. This helps ensure only new entities will be inserted. An entity
is considered to have its primary key value set if the primary key
property is set to anything other than the CLR default for the
property type.
You can either manually set the state of all referenced objects or, A better approach in my option, is to load the objects from the database (and have EF track them for changes) and modify these objects. These way EF will know the exact state for each object when it comes to save it to the database.

My method is only returning one image when there are two images for the entity

I have a property entitiy. Each property can have multiple images.
I have a method that retrieves the property data, as well as the images, but only one image is being returned when I test the API in post man.
This is the interface
IPropertyRepository :
Task<Property> GetProperty(int id);
PropertyRepository :
public async Task<Property> GetProperty(int id)
{
var property = await _context.Property.Include(ph => ph.Photos)
.FirstOrDefaultAsync(p => p.Id == id);
return property;
}
PropertyController :
[HttpGet("{id}", Name = "GetProperty")]
public async Task<IActionResult> GetProperty(int id)
{
var property = await _repo.GetProperty(id);
var propertyToReturn = _mapper.Map<PropertyForDetailedDto>(property);
return Ok(property);
}
This is the class of the DataTransferObject that is used above.
public class PropertyForDetailedDto
{
public int Id { get; set; }
public string Town { get; set; }
public string County { get; set; }
public string Address { get; set;
public int UserId {get; set; }
public ICollection<Photo> Photos { get; set; }
}
Any ideas here?
AutoMapper
public class AutoMapperProfiles : Profile
{
public AutoMapperProfiles()
{
CreateMap<UserForRegisterDto, User>();
CreateMap<PropertyForCreateDto, Property>();
CreateMap<PropertyToReturnDto, Property>();
CreateMap<PhotoForCreationDto, Photo>();
CreateMap<PropertyToReturnDto, Property>();
}
I have solved my issue.
In my PropertyForDetailedDto I had to include another DTO for the photos as opposed to a Model.
public ICollection<PhotosForDetailedDto> Photos { get; set;}
public ICollection<Photo> Photos { get; set; }
Next, I had to map both the PropertyForDetailedDto and the above PhotoForDetailedDto
CreateMap<PropertyForDetailedDto, Property>();
CreateMap<Photo, PhotosForDetailedDto>();
I know the correct property as well as all corresponding that belong to property returned.

Entity Framework Update Records Not Working

Hi i am running into some issues when trying to do an update on a nested collection using Entity Framework. I am using Entity Framework Core but I don't know if that make a difference with this issue.
The application should allow people to be tagged and untagged in a photo.
I am using WebApi and trying to send a patch to update some details but the update doesn't seem to work.
Here is the data model:
Here is my Photo class
public class Photo
{
private Photo() { }
public Photo(string title, string location)
{
CreatedDate = DateTime.UtcNow;
Title = title;
Location = location;
Persons = new HashSet<PersonPhoto>();
}
public int Id { get; set; }
public string Title { get; private set; }
public string Location { get; private set; }
public DateTime CreatedDate { get; private set; }
public ICollection<PersonPhoto> Persons { get; set; }
}
Here is my Person class
public class Person
{
public Person()
{
Photos = new HashSet<PersonPhoto>();
}
public Person(string firstName, string lastName, DateTime dateOfBirth)
{
FirstName = firstName;
LastName = lastName;
DateOfBirth = dateOfBirth;
CreatedDate = DateTime.UtcNow;
}
public int Id { get; set; }
public string FirstName { get; private set; }
public string LastName { get; private set; }
public DateTime? DateOfBirth { get; private set; }
public DateTime CreatedDate { get; private set; }
public DateTime? LastModified { get; set; }
public ICollection<PersonPhoto> Photos { get; set; }
}
Here is my PersonPhoto class
public class PersonPhoto
{
public int PersonId { get; set; }
public Person Person { get; set; }
public int PhotoId { get; set; }
public Photo Photo { get; set; }
}
I am sending a JsonPatch document containing a PhotoViewModel to the endpoint and I would expect the result to either update or remove a record for the PhotoPerson joining table.
Here is the PhotoViewModel
public class PhotoViewModel
{
public int Id { get; set; }
public string Title { get; set; }
public string Location { get; set; }
public List<int> Persons { get; set; }
}
Here is the endpoint that I am posting to:
public IActionResult TagPhoto(int id, [FromBody]JsonPatchDocument<PhotoViewModel> patch)
{
// Get Photo first
var photos = _uow.Photos.GetPhoto(id);
var photoVM = Mapper.Map<Photo, PhotoViewModel>(photos);
patch.ApplyTo(photoVM);
var photo = Mapper.Map<PhotoViewModel, Photo>(photoVM);
foreach (var i in photoVM.Persons)
{
photo.Persons.Add(new PersonPhoto()
{
PersonId = i
});
}
_uow.Photos.UpdatePhoto(photo);
_uow.Complete();
return Ok();
}
The mapper that maps a PhotoViewModel to a Photo Entity looks like this:
Mapper.CreateMap<PhotoViewModel, Photo>()
.ForMember(dest => dest.Persons,
opts => opts.Ignore());
The mapper that maps a Photo Entity to a PhotoViewModel looks like this:
Mapper.CreateMap<Photo, PhotoViewModel>()
.ForMember(vm => vm.Persons,
map => map.MapFrom(p => p.Persons.Select(c => c.PersonId)));
and the Repository that handles the data access has this update method:
public void UpdatePhoto(Photo photo)
{
contextTest.Entry(photo).State = EntityState.Modified;
}
Whats currently happening:
If I update the PhotoViewModel properties other than the collection of Persons then the database is updated accordingly. Sql Profiler shows that the update statement contains the new property change (title for example) however when I try to update the nested collection nothing happens in the database and sql profiler has no mention of the nested collection. It just seems to ignore it.
I've tried a few different things. I changed the repository to update like this:
_context.Photos.Update(photo)
This just kept adding new records into the joining table (duplicates too) when I would try to remove from the nested collection nothing would every be removed from the joining table.
Am I missing something really obvious? I've been looking around online but I can't really see a solution for this.
Any help would be very much appreciated!
** Edit **
The _context.SaveChanges() ocurrs in my UnitOfWork here:
public void Complete()
{
_context.SaveChanges();
}

EF Core returns only first record of a list unless FK entities are reset

I am facing the same issue as described in this question. Problem: my method GetAllConferences() returns correctly all the conferences from the DB, but when I return the result to the View from the controller return Ok(tripListVm) inly the first collection item is returned to the client. On the otehr side, by setting to null all the FK references (as pointed out in the SO question above) I can return correctly all the entities to the client, however this does not seem to me the proper way of proceeding.
EDIT: the solution was much simpler than I though. In the code below (I leave it in its original form for others to see it) I was not mapping the FK entities inside the ViewModel to Dto objects, but returning the model entity itself. That was the reason why I needed to null those inner references to make it work. By returning all Dtos objects, it works properly.
I have three entities involved with 1-many relationships:
public class Conference
{
public int Id { get; set; }
[Required]
[MaxLength(50)]
public string Name { get; set; }
public ICollection<Venue> Venues { get; set; }
public int? LocationId { get; set; }
public Location Location { get; set; }
}
public class Venue
{
public int Id { get; set; }
[Required]
[MaxLength(50)]
public string Name { get; set; }
public int? ConferenceId { get; set; }
public Trip Conference { get; set; }
public int? LocationId { get; set; }
public City City { get; set; }
}
public class City
{
public int Id { get; set; }
[Required]
[MaxLength(50)]
public string Name { get; set; }
public ICollection<Conference> Conferences { get; set; }
public ICollection<Venue> Venues { get; set; }
}
In the repository, I have a method that returns the conferences and the related entities (City and Venues):
public IEnumerable<Conference> GetAllConferences()
{
return _context.Conferences
.Include(t => t.Venues)
.Include(t => t.City)
.ToList();
}
In the controller I need to use the following code to return all the results:
var conferences = _repository.GetAllConferences();
if (conferences.Any())
{
var conferenceListVm = trips.ToConferenceVmList();
//Without setting the FK references to null, I can return only the first result of the collection
foreach (var vm in conferenceListVm)
{
foreach (var pm in vm.PoinOfInterests)
{
pm.Trip = null;
}
vm.Location.Conferences = null;
vm.Location.Venues = null;
}
return Ok(conferenceListVm);
}
public static ConferenceViewModel ToConferenceVm(this Conference conference)
{
var confVm = new ConferenceViewModel();
confVm.Name = conference.Name;
confVm.City = conference.City;
confVm.Venues = conference.Venues;
return tripVm;
}
public static IEnumerable<ConferenceViewModel> ToConferenceVmList(this IEnumerable<Conference> conferences)
{
return conferences.Select(c => c.ToConferenceVm()).ToList();
}

Object doesn't add with children - EF Code First

It's been quite a while since I last used EF. I've never had any problems using it before. Now I'm attempting to insert an object that has a one-many relationship with another object. But in the API call, the collection array of the child object is shown to be empty however the parent object can be seen in the api call of the child object.
I have my models as below:
Conversation Table
public class Conversation
{
public Conversation()
{
this.ChatMessages = new List<ChatMessage>();
this.DeletedConversations = new List<ConversationDeleted>();
}
public int ConversationID { get; set; }
public string toUser { get; set; }
public string FromUser { get; set; }
[InverseProperty("Conversation")]
public ICollection<ChatMessage> ChatMessages { get; set; }
public ICollection<ConversationDeleted> DeletedConversations { get; set; }
public DateTime CreatedAt { get; set; }
public int UserID { get; set; }
}
ChatMessage Table
public class ChatMessage
{
public int ChatMessageID { get; set; }
public string fromUser { get; set; }
public string toUser { get; set; }
public string Message { get; set; }
public bool DeliveryStatus { get; set; }
public DateTime CreatedAt { get; set; }
public Guid UniqueID { get; set; }
public int ConversationID { get; set; }
[ForeignKey("ConversationID")]
public virtual Conversation Conversation { get; set; }
public ICollection<MessageDeleted> MessagesDeleted { get; set; }
public int UserId { get; set; }
}
My Fluent API looks like this:
modelBuilder.Entity<ChatMessage>()
.HasRequired(x => x.Conversation)
.WithMany(x => x.ChatMessages)
.HasForeignKey(x => x.ConversationID);
I'm trying to create a conversation entity and add a chat object to it's collection. I do it like so:
public IHttpActionResult CreateConversation()
{
ChatMessage msg = new ChatMessage { CreatedAt = DateTime.UtcNow, DeliveryStatus = true, fromUser = "annettehiggs", toUser = "terrydriscoll", Message = "Hum tum", UniqueID = Guid.NewGuid(), UserId = 43 };
Conversation conv = new Conversation();
conv.ChatMessages.Add(msg);
conv.CreatedAt = DateTime.UtcNow;
conv.FromUser = "annettehiggs";
conv.toUser = "terrydriscoll";
DataModel db = new DataModel();
db.Conversations.Add(conv);
db.SaveChanges();
return Ok(conv);
}
and this is how I retrieve the conversation object:
public IQueryable<Conversation> GetConversations()
{
return db.Conversations;
}
As a result, ChatMessage API call shows the conversation it's associated to but the Conversation object doesn't show the chat in it's collection. What am I doing wrong here?
The add code is working properly (otherwice you'll not be able to see the new chat message). The problem is with your data retrieval code.
Since your ChatMessage.Conversation property is marked as virtual, most probably it gets lazy loaded, that's why you see it populated.
At the same time, your Conversation.ChatMessages is not virtual, hence you need to explicitly eager load it using the Inlclude method, or depending on your requirements, mark it virtual to get the lazy load behavior like the inverse navigation property.

Categories