【问题标题】:ASP.NET WebApi JSON Response and Entities with foreign keyASP.NET WebApi JSON 响应和具有外键的实体
【发布时间】:2015-07-28 19:58:10
【问题描述】:

我正在开发 WebApi 项目,我的域中有 2 个实体:
街道

public class Street
{
  public int ID { get; set; }
  public string Name { get; set; }
  public int StreetTypeID { get; set; }
  public virtual StreetType StreetType { get; set; }
}

和 StreetType:

public class StreetType
{
  public int ID { get; set; }
  public string Name { get; set; }
  public virtual ICollection<Street> Streets { get; set; }
}

我使用 FluenApi 来映射这些实体:

public class StreetTypeMap : EntityTypeConfiguration<StreetType>
{
  public StreetTypeMap()
  {
     HasKey(t => t.ID);
     Property(t => t.ID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
     Property(t => t.Name).IsRequired().HasMaxLength(50);
     HasMany(a => a.Streets).WithRequired(p => p.StreetType).HasForeignKey(p => p.StreetTypeID);
     ToTable("StreetType");
   }
 }

与 Street 实体类似。
现在我得到 JSON:

{
  "id":1,
  "name":"Street1",
  "streettypeid":3
}

我怎样才能得到这样的 JSON:

{
  "id":1,
  "name":"Street1",
  "streettypeid":
   {
    "id":3,
    "name":"Type3"
   }
}

或一些类似的结构。如何在 .NET 中完成此操作?

我的控制器:

StreetController : BaseApiController<Street>

    public class BaseApiController<T> : ApiController where T : BaseEntity
    {
        protected UnitOfWork unitOfWork = new UnitOfWork();

        protected IRepository<T> repository;

        public BaseApiController()
        {
            repository = unitOfWork.EFRepository<T>();
        }
        public virtual IQueryable<T> Get()
        {
          var entity = repository.Table;

          if (entity == null)
          {
              throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NoContent));
          }
          return entity;
      }

}

【问题讨论】:

    标签: c# json serialization foreign-keys asp.net-web-api2


    【解决方案1】:

    要在 JSON 中保留对象引用,请将以下代码添加到 Global.asax 文件中的 Application_Start 方法:

    var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
    json.SerializerSettings.PreserveReferencesHandling = 
        Newtonsoft.Json.PreserveReferencesHandling.All;
    

    更多数据请阅读this文章。

    另一种方法是尝试在您的 webapi 中使用OData。 您必须安装 OData 包:

    Install-Package Microsoft.AspNet.Odata
    

    然后您就可以使用$expand 导致相关实体被内嵌在响应中。
    您可以保持原样,但我认为添加IgnoreDataMember 是正确的(不要忘记添加using System.Runtime.Serialization;)

    public class StreetType
    {
      public int ID { get; set; }
      public string Name { get; set; }
      [IgnoreDataMember]
      public virtual ICollection<Street> Streets { get; set; }
    }
    

    还需要给你添加属性Get方法:

    [EnableQuery]
    public virtual IQueryable<T> Get()
    

    之后你可以像这样创建http-request:

    http://blablabla/Street?$expand=StreetType
    

    并使用他们的StreetType 获取所有Streets

    【讨论】:

    • 谢谢。当我尝试它时,我得到了一个错误:Could not find a property named 'StreetTypes' on type 'Domain.Entities.Street'."
    • 你使用哪个 http-request?
    • 好吧,为什么是$expand=StreetTypes?没有名称为 StreetTypes 的属性。在您的Street 类中存在public virtual StreetType StreetType { get; set; }。所以,你必须使用http://mysite/Street?$expand=StreetType
    • 哦,是的,你完全正确。我很不专心。感谢它的工作!!!非常感谢你。你真的帮了我。谢谢。这是最好的简单解决方案
    【解决方案2】:

    您需要创建一个视图模型以返回包含包含的实体。

    编辑:这是一个完整的 api 控制器方法,包括建议的路由。

        [HttpGet, Route("/api/streets/{id:int:min(1)}")]
        public IHttpActionResult GetYourJsonData(int id)
        {
            try
            {
                //from your unit of work class
                return uow.GetEntities<Street>(x => x.ID == id)
                    .FirstOrDefault()
                    .Select(viewModel => new {
                        ID = viewModel.ID,
                        Name = viewModel.Name,
                        StreetTypeID = viewModel.StreetType //consider renaming this to streeytype and not street type id
                    });
            }
            catch(Exception)
            {
                 return InternalServerError();
            }
        }
    
    
    
    //Edit: in your repo class
    public IEnumerable<T> GetEntities<T>(Func<T, bool> predicate) where T : class
    {
        return yourContext.Set<T>().Where(predicate);
    }
    

    【讨论】:

    • 谢谢,但我不能在我的控制器中使用 dbContext,因为实现了模式 UnitOfWork 而我没有 dbContext
    • 您能否更新您的问题以显示您使用什么来获取数据?最终它会是一样的,数据上下文部分将只是移动到由您的工作单元公开的存储库中。
    • 我的 UnitOfWork 不适用于具体实体。 UnitOfWork 使用 dbcontext:IDbSet&lt;TEntity&gt; Set&lt;TEntity&gt;() where TEntity : BaseEntity
    • 那么你只需要添加一个谓词。我更新了我的答案,向你展示你需要在你的仓库中做什么。
    • 没问题,我已经更新了我的答案来告诉你如何使用它。
    猜你喜欢
    • 1970-01-01
    • 2013-03-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-06
    • 1970-01-01
    • 1970-01-01
    • 2017-04-20
    相关资源
    最近更新 更多