frank
frank

Reputation: 1302

Web Api for OData V4 doesn't Work With HasKey

I'm using web api 2.2 for odata v4, and below is the models:

[Table("User")]
public partial class User
{
    public User()
    {
        UserRoles = new HashSet<UserRole>();
    }

    [StringLength(50)]
    public string Id { get; set; }

    [Required]
    [StringLength(50)]
    public string Name { get; set; }

    public virtual ICollection<UserRole> UserRoles { get; set; }
}

[Table("UserRole")]
public partial class UserRole
{
    [Key]
    [Column(Order = 0)]
    [StringLength(50)]
    public string UserId { get; set; }

    [Key]
    [Column(Order = 1)]
    [StringLength(50)]
    public string RoleName { get; set; }

    public virtual User User { get; set; }
}

and below is the WebApiConfig.cs code:

        ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
        builder.EntitySet<User>("Users");
        builder.EntitySet<UserRole>("UserRoles");            
        config.MapODataServiceRoute("odata", "odata", builder.GetEdmModel());
        config.AddODataQueryFilter();

Till now, every thing works well. But, if I remove the [Key] form the UserRole definition. and use the code :

builder.EntitySet<UserRole>("UserRoles").EntityType.HasKey(r => new { r.UserId, r.RoleName });

The when I query on User, I always get the error that tells UserRoles has no key definition.

I have upload the source porject files @ https://cloud.seafile.com/f/bdff340ec3/ Please check the webapiconfig.cs User.cs UserRole.cs and web.config (for database connection string). When you access /odata/Users?$expand=UserRoles you will get the error

Upvotes: 0

Views: 6540

Answers (3)

Brixon
Brixon

Reputation: 11

I have the same problem. Here is what I am doing right now, not finished so I do not know if there will be other issues.

In my model I changed UserId to Id so OData convention will use it as the key and then in my dbcontext map file I used:

public class DbLdapMap : EntityTypeConfiguration<DbLdap>
{
    public DbLdapMap()
    {
        ToTable("LDAP");

        //Primary Key
        HasKey(t => t.Id);

        // Properties
        Property(t => t.Id).HasColumnName("UserId");

    }
}

I really want to keep all the database definitions in the Fluent API.

Upvotes: 1

Feng Zhao
Feng Zhao

Reputation: 2995

I tried your project, and the odata metadata "/odata/$metadata" seems fine to me.

<?xml version="1.0" encoding="utf-8"?>
<edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx">
  <edmx:DataServices>
    <Schema Namespace="WebApplication1.Models.OData" xmlns="http://docs.oasis-open.org/odata/ns/edm">
      <EntityType Name="User">
        <Key>
          <PropertyRef Name="Id" />
        </Key>
        <Property Name="Id" Type="Edm.String" Nullable="false" />
        <Property Name="Name" Type="Edm.String" Nullable="false" />
        <NavigationProperty Name="UserRoles" Type="Collection(WebApplication1.Models.OData.UserRole)" />
      </EntityType>
      <EntityType Name="UserRole">
        <Key>
          <PropertyRef Name="UserId" />
          <PropertyRef Name="RoleName" />
        </Key>
        <Property Name="UserId" Type="Edm.String" Nullable="false" />
        <Property Name="RoleName" Type="Edm.String" Nullable="false" />
        <NavigationProperty Name="User" Type="WebApplication1.Models.OData.User" />
      </EntityType>
    </Schema>
    <Schema Namespace="Default" xmlns="http://docs.oasis-open.org/odata/ns/edm">
      <EntityContainer Name="Container">
        <EntitySet Name="Users" EntityType="WebApplication1.Models.OData.User">
          <NavigationPropertyBinding Path="UserRoles" Target="UserRoles" />
        </EntitySet>
        <EntitySet Name="UserRoles" EntityType="WebApplication1.Models.OData.UserRole">
          <NavigationPropertyBinding Path="User" Target="Users" />
        </EntitySet>
      </EntityContainer>
    </Schema>
  </edmx:DataServices>
</edmx:Edmx>

The problem is that UserRole has no keys defined in the DB model rather than the OData EDM model.

If you remove the Key attribute on UserId and RoleName, the DBModelBuilder doesn't know what the keys are.

So you need to add the following code to Database.OnModelCreating without [Key]:

modelBuilder.Entity<UserRole>()
                .HasKey(r => new { r.UserId, r.RoleName });

What is more, you need to add EnableQuery attribute on the action if you want to support to apply $expand on the result:

        [EnableQuery]
        public IQueryable<User> GetUsers()
        {
            var db = new Database();
            return db.Users;
        }

Upvotes: 0

Tan Jinfu
Tan Jinfu

Reputation: 3347

Just change

builder.EntitySet<UserRole>("UserRoles").EntityType.HasKey(r => new { r.UserId, r.RoleName });

To

EntityTypeConfiguration<UserRole> userRoleType=builder.EntitySet<UserRole>("UserRoles").EntityType;
userRoleType.HasKey(p=>p.UserId);
userRoleType.HasKey(p=>p.RoleName);

Upvotes: 2

Related Questions