【问题标题】:Add related database entry in Azure Mobile Services controller在 Azure 移动服务控制器中添加相关的数据库条目
【发布时间】:2015-08-24 11:33:24
【问题描述】:

在我的 Azure 移动服务中,我有一个控制器类 UserController : TableController<User>,其中有一个 get 方法:

// GET tables/User/48D68C86-6EA6-4C25-AA33-223FC9A27959
public SingleResult<User> GetUser(string id)
{
    return Lookup(id);
}

我想记录每次访问用户的时间,所以我在模型中添加了一个简单的类型:

public class UserVisit : Microsoft.WindowsAzure.Mobile.Service.EntityData
{
    public string VisitingUser { get; set; }
    public DateTime TimeOfVisit { get; set; }
}

并在我的VCollectAPIContext : DbContext 类中包含属性public DbSet&lt;UserVisit&gt; UserVisits { get; set; }(并使用代码优先迁移更新数据库)。

要在查询用户 ID 时向数据库添加 UserVisit,我将控制器方法更改为

// GET tables/User/48D68C86-6EA6-4C25-AA33-223FC9A27959
public async Task<SingleResult<User>> GetUser(string id)
{
    var userVisit = new UserVisit { VisitingUser = id, TimeOfVisit = DateTime.UtcNow };
    context.UserVisits.Add(userVisit);
    await context.SaveChangesAsync();
    return Lookup(id);
}

但是SaveChangesAsync 失败并显示System.Data.Entity.Validation.DbEntityValidationException。挖掘异常的 EntityValidationErrors 属性,我发现问题是“需要 Id 字段。”

这有点奇怪。 Id 字段是基类Microsoft.WindowsAzure.Mobile.Service.EntityData 中的属性之一,我希望在插入时自动添加它。无论如何,我可以添加它和其他几个基类的属性:

// GET tables/User/48D68C86-6EA6-4C25-AA33-223FC9A27959
public async Task<SingleResult<User>> GetUser(string id)
{
    var userVisit = new UserVisit { Id = Guid.NewGuid().ToString(), Deleted = false, VisitingUser = id, TimeOfVisit = DateTime.UtcNow, CreatedAt = DateTimeOffset.Now };
    context.UserVisits.Add(userVisit);
    await context.SaveChangesAsync();
    return Lookup(id);
}

这次我得到一个System.Data.Entity.Infrastructure.DbUpdateException,因为我们“无法将值 NULL 插入到列 'CreatedAt'”。在对Add 的调用中它不为空。所以CreatedAt 在我的代码之外的某个地方被设置为null,结果插入失败!

我还尝试在控制器的初始化程序中设置一个EntityDomainManager&lt;UserVisit&gt; userVisitDomainManager; 实例变量,然后将我的控制器get方法重写为

// GET tables/User/48D68C86-6EA6-4C25-AA33-223FC9A27959
public async Task<SingleResult<User>> GetUser(string id)
{
    var userVisit = new UserVisit { VisitingUser = id, TimeOfVisit = DateTime.UtcNow };
    await userVisitDomainManager.InsertAsync(userVisit);
    return Lookup(id);
}

失败并显示相同的消息,“无法将值 NULL 插入 'CreatedAt' 列”

我应该如何执行在我的控制器方法中插入相关数据项这一看似简单的任务?

【问题讨论】:

  • 数据库中的 UserVisit 表是什么样的?我想知道问题是否是移动服务 SqlGenerator 没有运行。您描述的 context.UserVisits.Add() 行为非常奇怪!
  • 尝试记录生成的 SQL 语句:msdn.microsoft.com/en-us/data/dn469464.aspx#Log

标签: entity-framework azure-mobile-services


【解决方案1】:

解决方案可能类似于this answer。我猜您的迁移没有使用移动服务 SqlGenerator,因此某些自定义 SQL 设置没有得到应用。这意味着:

  • Id 没有获得 NEWID() 的默认值 - 这解释了您的“Id field is required”错误。
  • CreatedAt 没有获得 SYSUTCDATETIME() 的默认值 - 这与 EntityData.CreatedAt 上的 [DatabaseGenerated] 属性相结合,解释了“NULL CreatedAt”错误。

尝试根据上面的链接更新您的迁移,看看是否适合您。

【讨论】:

  • 谢谢布雷特 - 我会试试的。如何强制我的迁移使用移动服务 SqlGenerator?
  • 查看我上面答案中的链接——它向你展示了这个:SetSqlGenerator("System.Data.SqlClient", new EntityTableSqlGenerator());
  • 我没有机会检查这一点,因为我陷入了需要导航的其他服务器错误,但无论如何我都会在超时之前奖励赏金,因为这看起来很有希望。
  • 仅供参考,我们现在在这里记录了这一点:azure.microsoft.com/en-in/documentation/articles/…
  • 现在我在尝试将新的 UserVisit 保存到数据库时收到 System.Data.Entity.Validation.DbEntityValidationException
【解决方案2】:

按照brettsam的说明解决“Id字段为必填项”的问题。

在你的模型中添加这个:

[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[TableColumn(TableColumnType.Id)]
public new string Id { get; set; }

添加实体时会自动生成 GUID。

【讨论】:

  • 看起来不对。正如问题中提到的,我的模型类继承自 Microsoft.WindowsAzure.Mobile.Service.EntityData,它提供了 Id 属性(除其他外)。确定会生成 GUID?
  • 我同意,但是当我使用 SetSqlGenerator("System.Data.SqlClient", new EntityTableSqlGenerator()); 完成初始迁移时,它不会在我的测试中生成 GUID;
  • 谢谢多米尼克,这很有用。我认为当我因为它们失败而覆盖 SDK 方法时,是时候质疑使用 SDK 了。我目前正在探索使用实体框架但没有 Azure 移动服务在 ASP.Net API 2 中重写我的服务器端代码。
  • 适用于手机的 Azure SDK 运行良好,但我什至没有使用 Azure,只是使用自托管 API 中的表控制器。也许这个 SDK 应该叫别的名字,因为它太有用了。
  • 这真的很有趣 - 我将检查它以减少我们在 EF 中编写的代码量
猜你喜欢
  • 1970-01-01
  • 2016-08-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-08
  • 1970-01-01
相关资源
最近更新 更多