I've been following the Field Engineer example project from the Windows Development Center to guide into mapping many-to-many relationships on my model. What's been bothering me is how to insert entries into many-to-many relationships.
Take my model as example:
An Organization can have many Users.
An User can belong to many Organizations.
My model class for User looks like this:
public class User
{
public User()
{
this.Organizations = new HashSet<Organization>();
}
public int Id { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public string PasswordSalt { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public bool Active { get; set; }
public string Picture { get; set; }
public string PictureMimeType { get; set; }
public bool Confirmed { get; set; }
public string ConfirmationKey { get; set; }
[JsonIgnore]
public virtual ICollection<Organization> Organizations { get; set; }
}
My UserDTO class is this:
public class UserDTO : EntityData
{
public string Email { get; set; }
public string Password { get; set; }
public string PasswordSalt { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public bool Active { get; set; }
public string Picture { get; set; }
public string PictureMimeType { get; set; }
public bool Confirmed { get; set; }
public string ConfirmationKey { get; set; }
}
My Organization class:
public class Organization
{
public Organization()
{
this.Users = new List<User>();
}
public string Id { get; set; }
public string Title { get; set; }
public string LegalName { get; set; }
public string TaxReference { get; set; }
[JsonIgnore]
public virtual ICollection<User> Users { get; set; }
}
My OrganizationDTO class:
public class OrganizationDTO : EntityData
{
public string Title { get; set; }
public string LegalName { get; set; }
public string TaxReference { get; set; }
public virtual ICollection<UserDTO> Users { get; set; }
}
With those classes in mind I created the controller classes, I mapped the DTO and the Model classes using AutoMapper as follows:
cfg.CreateMap<Organization, OrganizationDTO>()
.ForMember(organizationDTO => organizationDTO.Id,
map => map.MapFrom(organization => MySqlFuncs.LTRIM(MySqlFuncs.StringConvert(organization.Id))))
.ForMember(organizationDTO => organizationDTO.Users,
map => map.MapFrom(organization => organization.Users));
cfg.CreateMap<OrganizationDTO, Organization>()
.ForMember(organization => organization.Id,
map => map.MapFrom(organizationDTO => MySqlFuncs.LongParse(organizationDTO.Id)))
.ForMember(organization => organization.Users,
map => map.Ignore());
Using Fluent API, I defined the relationship between these two entities using an EntityTypeConfiguration class like this:
public class UserEntityConfiguration : EntityTypeConfiguration<User>
{
public UserEntityConfiguration()
{
this.Property(u => u.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
this.HasMany<Organization>(u => u.Organizations).WithMany(o => o.Users).Map(uo =>
{
uo.MapLeftKey("UserId");
uo.MapRightKey("OrganizationId");
uo.ToTable("OrganizationsUsers");
});
}
}
I created a TableController class to hanlde UserDTO and OrganizationDTO, I have no problem inserting new Users or new Organizations, but the endpoints from each TableController class only allows me to add Users or Organizations individually as far as I understand.
To create an entry into the OrganizationsUser table, how can I achieve this?
I was thinking a PATCH request should be a way to do this, but is it the right way? Do I have to define a TableController for this? How can I expose the Insert, Update, Select and Delete of the elements in this relationship? What would be the JSON to be sent to the endpoints?
EDIT 1
I tried to PATCH the Users property in an Organization like this:
url: serviceUrl/tables/Organization/1
JSON body:
{
"users": [
{
"id": "1"
}
]
}
But that gave me an error:
The type of one of the primary key values did not match the type defined in the entity. See inner exception for details. Parameter name: keyValues
Inner Exception:
The argument types 'Edm.Int32' and 'Edm.String' are incompatible for this operation. Near WHERE predicate, line 1, column 78.
It seems I need to map the string I'm sending into the join table created with Fluent API if I'm not mistaken, but how do I map a join table defined with Fluent API? Is PATCH the way to do this? Or is there other way?