.net 使用所需外键在OData中插入实体

dy2hfwbg  于 2023-01-22  发布在  .NET
关注(0)|答案(5)|浏览(98)

**EDIT-2:**经过几个小时的研究,谷歌上几乎所有与odata相关的链接都变成了紫色,我发现“deep-inserts”(链接)的概念存在于OData规范中。所以毕竟,我正在做的事情应该是有效的,即使没有链接。有人知道如何在微软OData客户端上启用这个功能吗?有没有其他OData客户端支持这个概念?
**编辑:**也许这是错误的方法,所以请告诉我,如果我这样做是完全错误的。不能保存真的阻碍了我们的进步!

OData v3有一个问题。我有一个类Associate,它有一个必需的Address。当我尝试POST一个新的Associate时,由于Address属性为null而失败(EF 6抛出DbUpdateException,并出现外键冲突)。我的Associate类如下所示:

public class Associate
{
    public int Id { get; set; }

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

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

    public bool IsMailReceiver { get; set; }
    public bool IsLegalRepresentative { get; set; }

    [ForeignKey("AddressId")]
    public virtual Address Address { get; set; }
    public int AddressId { get; set; }
}

我使用Microsoft OData客户端,并尝试通过以下方式添加助理:

var associate = new Associate { /* ... */ };
context.AddObject("Associates", associate);
context.AddObject("Addresses", associate.Address);

/* UI fills associate data */

context.SetLink(associate, "Address", associate.Address);
context.UpdateObject(associate);
context.UpdateObject(associate.Address);

/* at this point the associate has the address set! */

context.SaveChanges(); // << Exception

然而,在服务器上的控制器中,Associate到达时没有外键,当我用Fiddler检查POST请求时,我发现了原因:

{
    "odata.type" : "xxx.Data.Entities.Associate",
    "AddressId" : 0,
    "Id" : 0,
    "IsLegalRepresentative" : false,
    "IsMailReceiver" : false,
    "Name" : "John Doe",
    "Role" : "Father"
}

即使客户端上生成的类具有Address属性,也不会传输地址。
我怎样才能解决这个问题?

trnvg8h3

trnvg8h31#

我也找不到任何关于这方面的信息--这真的感觉像是OData中的一个问题。下面是我如何设法让它工作的。
显式定义外键

class Student {
        public int TeacherId { get; set; }

        [Required, ForeignKey("TeacherId")]
        public virtual Teacher Teacher { get; set; }
}

执行插入时,获取相关记录并修复模型状态:

public IHttpActionResult Post(Student student)
  {
        student.Teacher = this.db.Teacher.FirstOrDefault(i => i.TeacherId == student.TeacherId);
        if (student.Teacher != null)
        {
            this.ModelState.Remove("student.Teacher");
        }

        if (!this.ModelState.IsValid)
        {
            return this.BadRequest(this.ModelState);
        }
  }

因此,从那时起发布学生,您忽略教师字段,只需使用教师ID发布。
我还没有在OData客户端上测试过,但是我想不出为什么它不能工作,你只需要使用Id字段而不是对象。

wf82jlnq

wf82jlnq2#

基本上当你创建对象时

var associate = new Associate { /* ... */ };

它不是插入到数据库中。它是在内存中创建的。当您调用

context.SaveChanges();

它将被保存在数据库中。此时将进行数据库验证并生成键。假设您的ID是在数据库中生成的唯一标识符,请注意,为了使它从数据库中获取更新值,您需要在实体模型视图中将StoreGeneratedPattern设置为Identity
如果不这样做,您的本地上下文和数据库上下文将不再匹配。如果您在引用其他内容时使用该对象,则会失败。
我想像这样的东西会起作用:

Address address = new Address{ City = "Tallinn" /*etc*/};
context.SaveChanges();
//At this point Address will be in database context and has Id 
associate = new Associate {  
  name = "Margus",
  role = "Admin",
  receiver = true,
  representative = true,
  AddressId = address.id 
};
context.SaveChanges();
5rgfhyps

5rgfhyps3#

这个问题没有解决方案,我会用一个与web-api一起工作的变更集的概念来滚动我自己的上下文,当我完成的时候,我会把它放在github上。

mklgxw1f

mklgxw1f4#

addlink和setlink工作的唯一方式是外键可以为空,并且您可以创建一个postput函数调用create link请参见here

k4ymrczo

k4ymrczo5#

我遇到了这个问题,我可以确认这确实是OData客户端(代理)的问题,尽管我还没有找到任何关于它的参考。我设法使用一个变通方案修复了它,这个解决方案不是100%完美,但对我来说很有效。下面是代码,我将解释它的缺点。

public static class DataServiceContextExtensions
{
    public static int PostChanges(this DataServiceContext context)
    {
        using (var client = new WebClient())
        {
            client.Credentials = context.Credentials;
            client.Headers[HttpRequestHeader.ContentType] = "application/json";

            var entities = context.Entities.Where(x => x.State == EntityStates.Added);

            foreach (var descriptor in entities)
            {
                var url = $"{context.BaseUri}{descriptor.Entity.GetType().Name}";
                var data = JsonSerializer.Serialize(descriptor.Entity);

                var response = client.UploadString(url, data);

                context.ChangeState(descriptor.Entity, EntityStates.Detached);
            }

            return entities.Count();
        }
    }
}

如您所见,我使用DataServiceContext上的扩展方法,其中我迭代存储在更改跟踪器中标记为已添加的所有实体,然后使用WebClient将它们的JSON序列化版本POST到OData端点,使用代理可能拥有的任何凭据。
问题:

  • 首先,它只处理添加的实体,这一点我可以接受,但其他人可能需要一个更全面的解决方案。我看到的唯一方法是用其他可以执行任意HTTP predicate 的客户端替换WebClient。
  • 第二,它不会将实体与生成的主键水合,同样,我的例子中不需要它,但这个问题将很难解决,因为OData似乎不会在UploadString的结果中返回它。
  • 第三,所有的URL总是,按照约定,假设为BaseUri+实体类型(例如,https://foo.bar/MyEntity),这可能是也可能不是总是这样,不是100%肯定,再次,在我的情况下工作。

相关问题