简体   繁体   中英

Entity framework code first map stored procedures with composite primary keys

I want to use EF code first to connect my model classes to my database, for example:

public partial class Header
{
    public int Id { get; set; }

    public int DeviceId { get; set; }
    ...
}

and in my DbContext

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<Header>().HasKey(k => new { k.Id, k.DeviceId });
    modelBuilder.Entity<Header>().MapToStoredProcedures(s =>
    {
        s.Update(u => u.HasName("dbo.SpUpdateHeader"));
        s.Insert(i =>
            i.HasName("dbo.SpInsertHeader")
            .Result<int>(r => r.Id, "Id"));
        s.Delete(d => d.HasName("dbo.SpDeleteHeader"));
     });
}

The DeviceId is fixed to the device that the record is generated, and the Id is the identity generated on that device. This DbContext is used to sync all the devices data on the server.

The problem with this code is that on insert, i get this error: A result binding for the property 'Id' was not found on the modification function 'SpInsertHeader'. Ensure that the property is database generated.

And if i set [DatabaseGenerated(DatabaseGeneratedOption.Computed)] attribute or [DatabaseGenerated(DatabaseGeneratedOption.Identity)] that property is never sent to the stored procedure

What can i do to enable this scenario?

EDIT

PROCEDURE [dbo].[SpInsertHeader] 
  @Id INT,
  @DeviceId INT
AS
BEGIN
--get new id
IF(COALESCE(@Id, 0) = 0)
BEGIN
    SELECT TOP 1 @Id = COALESCE(h.Id, 0) + 1
    FROM Header h
    WHERE h.DeviceId = @DeviceId
    ORDER BY h.Id DESC;
END

IF(COALESCE(@Id, 0) = 0)
BEGIN
    SET @Id = 1;
END

INSERT INTO Header(Id, DeviceId) VALUES (@Id, @DeviceId);

SELECT @Id AS 'Id';
END

After much testing and googling, i found this blog post with help me found a solution to my problem. I realize that i cannot get the database generated id, on the server created objects, and app generated id, for the diferent devices with the same DbContext.

So i need a way to have to 2 diferent model mappings, so i created a UseApiModel property on my DbContext and on the OnModelCreating method i set

modelBuilder.Entity<Header>().Property(t => t.Id)
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

and

s.Insert(i => i.HasName("dbo.SpInsertHeader").Result<int>(r => r.Id, "Id"));

or

modelBuilder.Entity<Header>().Property(t => t.Id)
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);

and

s.Insert(i => i.HasName("dbo.SpApiInsertHeader"));

Depending on the UseApiModel property.

The Api SP expect a valid Id key and the other SP will generate one, i need two SP's because EF will send the Id property or not depending on the DatabaseGeneratedOption.

I also use the Colin Angus Mackay advise to generate the Id on a INSTEAD OF INSERT

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM