0

FINAL EDIT : I will put the answer here at the top for other people who are searching. The main problem was the namespace mismatch caused by db-first generated EDM attaching '.Models' to the end of the model namespace. This namespace did not matched with the odata namespace so the route was failing. I just edited out all the occurances of '.Models' from the namespace and now it's working.


A newbie trying out Breeze with webApiOdata set up. Sorry if this question is a trivial one.

I have a db generated edmx model with webapi odata controllers. I was having problem with getting the correct metadata to show until I read about the new Breeze EdmBuilder.

That solved the problem of getting the correct metadata to show but now, I can not route to any of the tables. If I try /odata/Customers I get a 406 error.

Before, I was using ODataConventionModelBuilder to set the EntitySets and that worked fine.

ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
    builder.EntitySet<Detail>("Details");
    builder.EntitySet<Item>("Items");
    builder.EntitySet<Order>("Orders");
    builder.EntitySet<Customer>("Customers");

Now, since I am using EdmBuilder, how do I set the EntitySets so that I can route to proper data?

I hope the question makes sense.

* EDIT : I have added the listing of GCSodContext and a snippet from the Customers controller.

namespace GCSbz3.Models
{
    using System;
    using System.Data.Entity;
    using System.Data.Entity.Infrastructure;

    public partial class GCSodContext : DbContext
    {
        public GCSodContext()
            : base("name=GCSodContext")
        {
        }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            throw new UnintentionalCodeFirstException();
        }

        public virtual DbSet<Customer> Customers { get; set; }
        public virtual DbSet<Detail> Details { get; set; }
        public virtual DbSet<Item> Items { get; set; }
        public virtual DbSet<Order> Orders { get; set; }
    }
}

and Here is the Customers controller

...
using System.Web.Http;
using System.Web.Http.ModelBinding;
using System.Web.Http.OData;
using System.Web.Http.OData.Routing;
using GCSbz3.Models;

namespace GCSbz3.Controllers
{
    public class CustomersController : ODataController
    {
        private GCSodContext db = new GCSodContext();

        // GET odata/Customers
        [Queryable]
        public IQueryable<Customer> GetCustomers()
        {
            return db.Customers;
        }
...

Here is the Customer class.

namespace GCSbz3.Models
{
    using System;
    using System.Collections.Generic;

    public partial class Customer
    {
        public Customer()
        {
            this.Orders = new HashSet<Order>();
        }

        public int CustID { get; set; }
        public string FName { get; set; }
        public string LName { get; set; }
        public string Phone { get; set; }

        public virtual ICollection<Order> Orders { get; set; }
    }
}
Chi Row
  • 1,106
  • 7
  • 18

1 Answers1

1

Check your ODataController name, it needs to be aligned with EntitySet name in your edmx.

If the set name is CustomerSet, controller needs to be CustomerSetController with a public GetCustomerSet method.

For instance this works;

TestDbContext.cs

public partial class TestDbContext : DbContext
{
    public TestDbContext()
        : base("name=TestDbContext")
    {}

    public virtual DbSet<A1> A1Set { get; set; }
}

A1SetController.cs

public class A1SetController : ODataController
{
    private TestDbContext db = new TestDbContext();

    // GET odata/A1Set
    [Queryable]
    public IQueryable<A1> GetA1Set()
    {
        return db.A1Set;
    }
}

And routing setup in WebApiConfig.cs

// OData routes
config.Routes.MapODataRoute(
    routeName: "odata",
    routePrefix: "odata",
    model: EdmBuilder.GetEdm<TestDbContext>(),
    batchHandler: new DefaultODataBatchHandler(GlobalConfiguration.DefaultServer));

And last, edmx and actual model (class) namespaces need to be aligned. When you make a request to $metadata, you see the defined namespace for your model. You can change it in your Model Designer, right click to an empty area, click to Properties. In Properties window, you can see Namespace attribute. In my case;

<Schema Namespace="Web">
    <EntityType Name="Customer">

And Customer.cs

namespace Web
{
    using System;
    using System.Collections.Generic;

    public partial class Customer
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }
}
coni2k
  • 2,535
  • 2
  • 20
  • 21
  • I am getting 406 error when I try '/odata/Customers'. '/odata' and '/odata/$metadata' both work but not a route to the controllers – Chi Row Apr 05 '14 at 00:20
  • My context and controllers all look like your sample. The only thing that I can think which might cause a problem is that my context name does not end with the word 'Context' Let me try changing that and see if that makes a difference. – Chi Row Apr 05 '14 at 00:24
  • Still getting 406 error after the name change. I have added the snippets to my original question. Why is it that when using ODataConventionModelBuilder to build the model, I need to add '.EntitySet' to the model to route but the new EdmBuilder does not require it? – Chi Row Apr 05 '14 at 01:19
  • @ChiRow Probably 406 is happening because of namespace difference. I've updated my answer, please check the last part. Also check your route config, you don't use ODataConventionModelBuilder anymore, right? Also you see the metadata when you make a request to /odata/$metadata? – coni2k Apr 05 '14 at 10:24
  • @ChiRow EdmBuilder directly reads from your edmx file, it defines all these entities, sets and their relations. So no need to define again. It seems to me, default OData template is there only single entities. I'm assuming they will improve it later on. – coni2k Apr 05 '14 at 10:26
  • 1)Only the new template. No default OData template. 2)$metadata shows namespace to be "GCSbz3" which matches the edmx namespace. 3)The controllers were in GCSbz3.Controllers namespace. I changed it to just GCSbz3 and still no luck. Just getting 406 error. – Chi Row Apr 05 '14 at 12:06
  • edmx context entitycontainer does show the entitysets are in there. However, shouldn't that container belong to the model or at least the model also has a copy of that container? – Chi Row Apr 05 '14 at 12:08
  • 1
    @ChiRow Customer class is in GCSbz3 or GCSbz3.Models namespace? This namespace and edmx needs to be aligned. – coni2k Apr 05 '14 at 17:59
  • @ChiRow Could you add Customer class to your question? – coni2k Apr 05 '14 at 21:53
  • Ah the Customer class is in GCSbz3.Models namespace. Let me change that and see if it works. – Chi Row Apr 06 '14 at 01:51
  • Yay! that fixed it. THANK YOU VERY MUCH!!! The problem was that the DB-first generated code always attaches '.Models' to the end of the namespace. I got rid of them and all works now. – Chi Row Apr 06 '14 at 02:34
  • @ChiRow That's true, usually default namespaces of edmx and the generated models are different. Well, glad that your problem is solved. – coni2k Apr 06 '14 at 11:04
  • Nice thread it help me. Anyway, does anyone know if it's possible to don't have the same namespace for db context and Models ? – Dragouf Jun 28 '14 at 15:11
  • @Dragouf As far as I know that's not possible – coni2k Jun 30 '14 at 07:35