4

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?

Uriel Arvizu
  • 1,876
  • 6
  • 37
  • 97

1 Answers1

2

I think PATCH should work, but I'm not an expert in automapper.

Another option, if you only have a few users in each organization, is to create views in your backend that represent the joined tables. See my posts in this forum thread for an example of how this would work: https://social.msdn.microsoft.com/Forums/azure/en-US/4c056373-d5cf-4ca6-9f00-f152d2b01372/best-practice-for-syncing-related-tables-in-offline-mode?forum=azuremobile. The advantage of this approach is that it really simplifies the client data model, and it doesn't have to know anything about foreign keys.

The issue, however, is that you could end up with duplicate users/organizations and that would take up more space in the client database. However, because of the way my example sets up UpdatedAt and version, you will get all updates, for example, once a user's details are changed.

lindydonna
  • 3,874
  • 17
  • 26
  • But how would I make it so those views get to be used? My main issue is that I don't have an idea how to expose these relationships. By that I mean, I create a TableController for each entity, but since we're talking about weak entities that are created by EF when checking the properties in my Model classes, how would I create the TableControllers for handling these?. Also I think the approach you recommended me would bring problems since I'm dealing with a situation where an Organization could have thousands of Users (my ER is for performing surveys in private companies) so duplicates arebad. – Uriel Arvizu Jan 14 '15 at 19:32
  • You would change the type of your table controller rather than create a new one, but the underlying type would be a DTO that has the weak entities. How are these exposed in EF? For instance, if it is method calls, you would populate the relationship fields in your table controller. Also, this tutorial might help: http://blogs.msdn.com/b/azuremobile/archive/2014/06/18/insert-update-related-data-with-1-n-relationship-using-net-backend-azure-mobile-services.aspx – lindydonna Jan 28 '15 at 23:02
  • I've read that article, but it only explains how to resolve the many-to-many relationships on the model and DTO classes, but it doesn't an example on how to insert data for those relationships. The issue I'm dealing with is that I have no idea how to insert a User in an Organization or how to insert an Organization into a User, the TableControllers' Path method is the one I think I have to use to solve this, but I'm not sure if I should do that since it's already in charge of patching the entity related. – Uriel Arvizu Feb 03 '15 at 16:30
  • Sorry, which article? The blog post describes how to insert/update using relationships. You set up the parent with nested children, and you use patch to add new children to the parent. – lindydonna Feb 18 '15 at 07:33