Base solution for your next web application
Open Closed

Many-To-Many relationship with Entity Framework #3657


User avatar
0
Ricavir created

Hello support,

I have following issue :

I have added a collection to User entity like this :

/// <summary>
    /// Represents a user in the system.
    /// </summary>
    public class User : AbpUser<User>
    {
        public const int MinPlainPasswordLength = 6;

        public const int MaxPhoneNumberLength = 24;

        public virtual Guid? ProfilePictureId { get; set; }

        public virtual bool ShouldChangePasswordOnNextLogin { get; set; }

       <span style="color:#FFFF00">public virtual ICollection<Event> Events { get; set; } </span>

As you can see, a collection of Event entities is added.

I also added a collection of User entity in Event entity like this :

[Table("LsEvents")]
    public class Event : FullAuditedEntity<long>, IMustHaveTenant
    {
        public virtual int TenantId { get; set; }

        [ForeignKey("AddressId")]
        public virtual Address Address { get; set; }
        public virtual long AddressId { get; set; }

        [Required]
        public virtual DateTime Begin { get; set; }

        [Required]
        public virtual DateTime End { get; set; }

        public virtual string Description { get; set; }

        public virtual ICollection<User> EventUsers { get; set; }

I've run the migrations on the database and everything went as excepted : foreign keys added to both table AbpUsers and LsEvents and an additional table UserEvents has been added automaticaly to manage many-to-many relationship.

Now, looking to the Event application service : I've created my DTO's to get, create and update Events. I am able to add a new event object with some Abp users as collection. No problem with this.

The problem is to update existing Event entities... When my Event DTO is received, the mapping is working well but User properties are being validated and following error is trhrown :

ERROR 2017-08-04 15:03:12,810 [8 ] EntityFramework.DbContext - There are some validation errors while saving changes in EntityFramework: ERROR 2017-08-04 15:03:12,810 [8 ] EntityFramework.DbContext - - Password: Le champ Password est requis.

Actually, I just want to update the link between Event and Users ... and not update each User entity !

Can you please provide a solution for that ?


4 Answer(s)
  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi,

    Can you share your DTOs and app service code for updating event ?

    Thanks.

  • User Avatar
    0
    Ricavir created

    Yes sure, here it is :

    DTO's

    public class GetEventsInput : PagedAndSortedInputDto, IShouldNormalize
        {
            public DateTime StartDate { get; set; }
            public DateTime EndDate { get; set; }
            public string Description { get; set; }
    
            public void Normalize()
            {
                if (Sorting.IsNullOrWhiteSpace())
                {
                    Sorting = "Begin DESC";
                }
            }
        }
    
        [AutoMapFrom(typeof(Event))]
        public class EventListDto : FullAuditedEntityDto<long>
        {
            public long AddressId { get; set; }
    
            public DateTime Begin { get; set; }
    
            public DateTime End { get; set; }
    
            public string Description { get; set; }
    
            public long? MainUserId { get; set; }
    
            public List<MainUserDto> EventUsers { get; set; }
        }
    
        [AutoMap(typeof(Event))]
        public class EventEditDto : EntityDto<long>
        {
            [Required]
            public DateTime Begin { get; set; }
    
            [Required]
            public DateTime End { get; set; }
    
            public string Description { get; set; }
    
            public List<MainUserDto> EventUsers { get; set; }
    
        }
    
        [AutoMap(typeof(Event))]
        public class CreateEventInput
        {
            [Required]
            public long AddressId { get; set; }
    
            [Required]
            public string Begin { get; set; }
    
            [Required]
            public string End { get; set; }
            
            public string Description { get; set; }
    
            public long? MainUserId { get; set; }  
            
            public List<MainUserDto> EventUsers { get; set; }
        }
    
        [AutoMap(typeof(User))]
        public class MainUserDto : EntityDto<long>
        {
            public string Name { get; set; }
    
            public string Surname { get; set; }
    
            public string UserName { get; set; }
    
            public string EmailAddress { get; set; }
    
            public string PhoneNumber { get; set; }
    
            public Guid? ProfilePictureId { get; set; }        
        }
    

    And AppService's

    [AbpAuthorize(AppPermissions.Pages_Tenant_Events_Create)]
            public async Task CreateEvent(CreateEventInput input)
            {            
                var eventVar = input.MapTo<Event>();
                eventVar.EventUsers.Clear();
                for (int i = 0; i < input.EventUsers.Count; i++)
                {
                    eventVar.EventUsers.Add(await UserManager.GetUserByIdAsync(input.EventUsers[i].Id));
                }
                await _eventRepository.InsertAsync(eventVar);            
            }
    
            public async Task<PagedResultDto<EventListDto>> GetEvents(GetEventsInput input)
            {
                var events = _eventRepository
                    .GetAll()     
                    .Include(c=>c.EventUsers)
                    .Where(
                        p => (p.Begin >= input.StartDate &&
                             p.End <= input.EndDate)
                    );       
    
                var resultCount = await events.CountAsync();
                var results = await events
                    .OrderBy(input.Sorting)
                    .PageBy(input)
                    .ToListAsync();
    
                var eventListDtos = results.MapTo<List<EventListDto>>();            
    
                return new PagedResultDto<EventListDto>(resultCount, eventListDtos);
    
            }       
    
            [AbpAuthorize(AppPermissions.Pages_Tenant_Events_Edit)]
            public async Task<EventEditDto> GetEventEdit(EntityDto<long> input)
            {
                var _event = await _eventRepository.GetAll()
                    .Include(c => c.EventUsers)
                    .FirstAsync(c => c.Id == input.Id);
                var _eventDto = _event.MapTo<EventEditDto>();
                return _eventDto;
            }        
    
            [AbpAuthorize(AppPermissions.Pages_Tenant_Events_Edit)]
            public async Task UpdateEventEdit(EventEditDto input)
            {           
                var _event = await _eventRepository.GetAsync(input.Id);
                _event.EventUsers.Clear();
                input.MapTo(_event);              
                await _eventRepository.UpdateAsync(_event);            
            }
    

    Still having issues when updating entities : error log is " Validation failed for one or more entities". I've tried the same design with other entities that I've created (not with user entity from abp) and still having same issue for updates.

  • User Avatar
    0
    alper created
    Support Team

    Hi

    I think you have to update each entity in its own way. Like update event entity then Event-User relation table.

  • User Avatar
    0
    Ricavir created

    Hi,

    It works with following code. As you said @alper, I need to update parent entity alone first and then childs entity. Tks !

    public async Task UpdateEventEdit(EventEditDto input)
            {           
                var _event = await _eventRepository.GetAsync(input.Id);            
                input.MapTo(_event);
                _event.EventUsers.Clear();
                var updatedEvent = await _eventRepository.UpdateAsync(_event);
                //Update EventUsers separatly
                if (input.EventUsers.Count > 0)
                {
                    foreach (var eventUser in input.EventUsers)
                    {
                        updatedEvent.EventUsers.Add(await UserManager.GetUserByIdAsync(eventUser.Id));
                    }
                    await _eventRepository.UpdateAsync(updatedEvent);
                }
            }