Skip to content

Step by step instructions for using M2M with WCR RIA Services

Daniel Svensson edited this page Apr 17, 2019 · 1 revision

Step-by-step instructions for using M2M with WCR RIA Services

(This information is obsolete since M2M4RIA has now been ported to OpenRiaServices, checkout OpenRiaServices.M2M)

The instructions below assume that you'll be using Entity Framework code first and DbContext. If you're using an alternative DAL, you can still use M2M4Ria but the steps marked with (*) will be slightly different.

1. Add M2M Nuget packages to your projects.

  • Add the RIAservices.M2M Nuget package to your RIA Service server project. This will also add the packages RIAServices.M2M.LinkTable, RIAServices.EntityFramework, RIAServices.T4, and FluentMetadata to your project. This package will add extensions to the FluentMetadata package that enables you to specify what your M2M associations are. Furthermore, the package will add an entity generator that integrates with RIA Services code generation framework.
  • optional: If your datamodel is defined in a different project, add the Nuget package RIAServices.M2M.LinkTable to that project as well. You'll need the generic LinkTable class provided by this package in your datamodel (I'll come to that later).
  • Add the RIAServices.M2M.Silverlight Nuget package to your Silverlight project. This package adds the Silverlight M2M view on link table entities to your Silverlight project.

2. Extend your data model with link table views.

  • Define a LinkTable entity type that connects both entity types of an M2M association. E.g., given two entity types {{Product}} and {{Order}}, you can define a link table entity as follows: {code:c#} public class ProductOrder : LinkTable<Product, Order> {} {code:c#}

  • Define "link table views" for your M2M associations for both entity types involved in an M2M Association. E.g., for {{Product}} and {{Order}} this will be: {code:c#} using RIAServices.M2M; public partial class Product { public ICollection Product_ProductOrderSet { get { return OrderSet.ProjectObject1(this, x => x.Order_ProductOrderSet); } } } public partial class Order { public ICollection Order_ProductOrderSet { get { return ProductSet.ProjectObject2(this, x => x.Product_ProductOrderSet); } } } {code:c#} {{ProjectObject1}} and {{ProjectObject2}} are extension methods that project an entity type to the corresponding property of a link table entity.

  • (*) Instruct entity framework to ignore these link table views from your entity model, such that the link table entities will not be populated to your data store. E.g., by adding {{.Ignore}} expressions as in the example below: {code:c#} public class MyEFContext : DbContext { public DbSet ProductSet { get; set; } public DbSet OrderSet { get; set; }

      protected override void OnModelCreating(DbModelBuilder modelBuilder)
      {
          base.OnModelCreating(modelBuilder);
    
          modelBuilder.Entity<Product>().HasMany(x => x.OrderSet).WithMany(x => x.ProductSet);
          modelBuilder.Entity<Product>().Ignore(x => x.Product_ProductOrderSet);
          modelBuilder.Entity<Order>().Ignore(x => x.Order_ProductOrderSet);
      }
    

    } {code:c#}

3. Configure your domain service to make use of M2M associations.

  • Configure your domain service to use the Fluent metadata API. {code:c#} EnableClientAccess FluentMetadata(typeof(MetadataConfiguration)) public class MyDomainService : DbDomainService { } {code:c#}

  • Define a fluent metadata configuration class. E.g., {code:c#} public class MetadataConfiguration : IFluentMetadataConfiguration { public void OnTypeCreation(MetadataContainer metadataContainer) { } } {code:c#}

  • In the {{OnTypeCreation}} method, specify your M2M association using the fluent API, e.g.,

{code:c#} metadataContainer.Entity() .Projection(x => x.OrderSet) .M2M(x => x.Product_ProductOrderSet, x => x.Order_ProductOrderSet, x => x.ProductSet); {code:c#} This expression specifies that the property {{Product_ProductOrderSet}} is the link table view for the property {{OrderSet}} in {{Product}}, and that {{Order_ProductOrderSet}} is the corresponding link table for property {{ProductSet}} in {{Order}}. With this information in place, the M2M entity generator is able to extend the generated entities in your Silverlight client with link table collections and with M2M views for them (i.e., the entity {{Product}} will have an M2M property {{OrderSet}} and {{Order}} will have an M2M property {{ProductSet}}).

4. Integrate M2M in your domain service

  • (*) Extend the domain service with Insert and Delete operations for your link table entities. Since link table entities are transmitted between your Silverlight client and domain service, the latter must have Insert and Delete operations defined for them. Link table entities are read-only because they consist only of a composite key (which will not change once created). For this reason, there is no Update method needed in your domain service. Moreover, since link table entities are always queried via their associated entities, there is no Query method needed for them. However, you are free to define one when needed. {code:c#} using RIAServices.M2M.DbContext; public void InsertProductOrder(ProductOrder ProductOrder) { var Product = ProductOrder.FetchObject1(ChangeSet, DbContext); var Order = ProductOrder.FetchObject2(ChangeSet, DbContext); Product.OrderSet.Add(Order); DbContext.ChangeTracker.DetectChanges(); }

    public void DeleteProductOrder(ProductOrder ProductOrder) { var Product = ProductOrder.FetchObject1(ChangeSet, DbContext); var Order = ProductOrder.FetchObject2(ChangeSet, DbContext); DbContext.LoadM2M<Product, Order, ProductOrder>(Product, Order); Product.OrderSet.Remove(Order); DbContext.ChangeTracker.DetectChanges(); } {code:c#} {{FetchObject1}}, {{FetchObject2}}, and {{LoadM2M}} are extension methods specific for DbContext that simplify the implementation of the Insert and Delete methods.

  • (*) Make sure you include the M2M associations in the query results of your entities. E.g., add "Include" expressions to your queries, like so: {code:c#} public IQueryable GetOrderSet() { return DbContext.OrderSet.Include(x => x.ProductSet); } {code:c#}