1

Using .Net Core 3.1 MVC pattern, I have been developing a back-end system. There are two tables, one is "TranslationInfo", another one is "TranslationContent". TranslationInfo has many TranslationContent, so I configured the Dbcontext like this.

TranslationInfo

    public class TranslationInfo
    {
        [Key]
        public int Index { get; set; }
        public DateTime InsertedDate { get; set; }
        public DateTime UpdatedDate { get; set; }
    }

TranslationContent

   public class TranslationContent
    {
        [Key]
        public int Code { get; set; }
        public string English { get; set; }
        public string French { get; set; }
        public string Status { get; set; }
        public string Message { get; set; }

        [ForeignKey("Index")]
        public TranslationInfo TranslationInfo { get; set; }
    }

Everything is alright except for getting a list of TranslationContent based on TranslationInfo.Index or not. The result displays the list of TranslationContent w/o TranslationInfo even though it enables to retrieve the result based on TranslationInfo.Index.

Result of 'https://localhost:44311/contentsList/3'

[
    {
        "code": 1,
        "english": "string",
        "french": "string",
        "status": "",
        "message": "",
        "translationInfo": null
    },
    {
        "code": 2,
        "english": "string",
        "french": "string",
        "status": "string",
        "message": "string",
        "translationInfo": null
    },
    {
        "code": 4,
        "english": "cvzxvc",
        "french": "asdfasdfas",
        "status": "Passed",
        "message": null,
        "translationInfo": null
    },
    {
        "code": 10,
        "english": "string",
        "french": "string",
        "status": "string",
        "message": "string",
        "translationInfo": null
    }
]

Here is Application area.

ITranslationService.cs

  public interface ITranslationServices
    {
        List<TranslationInfo> GetTranslationInfos();

        TranslationInfo GetTranslationInfo(int index);
        TranslationInfo CreateTranslationInfo(TranslationInfo translationInfo);

        TranslationInfo EditTranslationInfo(TranslationInfo translationInfo);
        List<TranslationContent> GetTranslationContents();
        List<TranslationContent> GetTranslationContentsByIndex(int index);

        TranslationContent GetTranslationContent(int code);
        TranslationContent CreateTranslationContent(TranslationContent translationContent);

        TranslationContent EditTranslationContent(TranslationContent translationContent);
    }

TranslationService.cs

using Excel.DB;
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Excel.Core
{
    public class TranslationService : ITranslationServices
    {
        private readonly AppDbContext _context;

        public TranslationService(AppDbContext context)
        {
            _context = context;
        }



        /*     Translation Info Area */
        public List<TranslationInfo> GetTranslationInfos()
        {
            return _context.TranslationInfos.ToList();
        }
        public TranslationInfo GetTranslationInfo(int index)
        {
            return _context.TranslationInfos.First(t => t.Index == index);
        }

        public TranslationInfo CreateTranslationInfo(TranslationInfo translationInfo)
        {
            translationInfo.InsertedDate = DateTime.UtcNow;
            translationInfo.UpdatedDate = DateTime.UtcNow;

            _context.Add(translationInfo);
            _context.SaveChanges();
            return translationInfo;
        }

        public TranslationInfo EditTranslationInfo(TranslationInfo translationInfo)
        {
            var dbTranslationInfo = _context.TranslationInfos.First(t => t.Index == translationInfo.Index);
            // update process will be added.

            dbTranslationInfo.UpdatedDate = DateTime.UtcNow;
            _context.SaveChanges();
            return dbTranslationInfo;

        }


        /*     Translation Content Area */
        public List<TranslationContent> GetTranslationContents()
        {
            return _context.TranslationContents.ToList();
        }
        public List<TranslationContent> GetTranslationContentsByIndex(int index)
        {
            return _context.TranslationContents.Where(t => t.TranslationInfo.Index == index).ToList();
        }

        public TranslationContent GetTranslationContent(int code)
        {
            return (TranslationContent)_context.TranslationContents.First(t => t.Code == code);
        }

        public TranslationContent CreateTranslationContent(TranslationContent translationContent)
        {
            var dbTranslationInfo = _context.TranslationInfos.First(t => t.Index == translationContent.TranslationInfo.Index);

            if (dbTranslationInfo == null)
            {
                throw new Exception("Cannot find translation info");
            }

            translationContent.TranslationInfo = dbTranslationInfo;

            _context.Add(translationContent);
            _context.SaveChanges();
            return (TranslationContent)translationContent;
        }

        public TranslationContent EditTranslationContent(TranslationContent translationContent)
        {
            var dbTranslationContent = _context.TranslationContents.First(t => t.Code == translationContent.Code);
            // update process will be added.

            dbTranslationContent.English = translationContent.English;
            dbTranslationContent.French = translationContent.French;
            dbTranslationContent.Message = translationContent.Message;
            dbTranslationContent.Status = translationContent.Status;
            dbTranslationContent.TranslationInfo = translationContent.TranslationInfo;
            _context.SaveChanges();
            return dbTranslationContent;
        }
    }
}

Here is API(Controller) area TranslationController

using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Excel.Core;
using Excel.DB;

namespace Excel.API.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class TranslationController : ControllerBase
    {
        private readonly ITranslationServices _translationServices;

        public TranslationController(ITranslationServices translationServices)
        {
            _translationServices = translationServices;
        }

        [HttpGet("/infos")]
        public IActionResult GetTranslationInfos()
        {
            return Ok(_translationServices.GetTranslationInfos());
        }

        [HttpGet("/info/{index}", Name = "GetTranslationInfo")]
        public IActionResult GetTranslationInfo(int index)
        {
            return Ok(_translationServices.GetTranslationInfo(index));
        }

        [HttpPost("/info")]
        public IActionResult CreateTranslationInfo(TranslationInfo translationInfo)
        {
            var newTranslationInfo = _translationServices.CreateTranslationInfo(translationInfo);
            return CreatedAtRoute("GetTranslationInfo", new { newTranslationInfo.Index }, newTranslationInfo);
        }

        [HttpPut("/info")]
        public IActionResult EditTranslationInfo(TranslationInfo translationInfo)
        {
            return Ok(_translationServices.EditTranslationInfo(translationInfo));
        }

        [HttpGet("/contents",Name = "GetTranslationContents")]
        public IActionResult GetTranslationContents()
        {
            return Ok(_translationServices.GetTranslationContents());
        }

        [HttpGet("/contentsList/{index}", Name = "GetTranslationContentsByIndex")]
        public IActionResult GetTranslationContentsByIndex(int index)
        {
            return Ok(_translationServices.GetTranslationContentsByIndex(index));
        }

        [HttpGet("/content/{code}", Name = "GetTranslationContent")]
        public IActionResult GetTranslationContent(int code)
        {
            return Ok(_translationServices.GetTranslationContent(code));
        }

        [HttpPost("/content")]
        public IActionResult CreateTranslationContent(TranslationContent translationContent)
        {
            var newTranslationContent = _translationServices.CreateTranslationContent(translationContent);
            return CreatedAtRoute("GetTranslationContent", new { newTranslationContent.Code }, newTranslationContent);
        }

        [HttpPut("/content")]
        public IActionResult EditTranslationContent(TranslationContent translationContent)
        {
            return Ok(_translationServices.EditTranslationContent(translationContent));
        }
    }
}

Can you let me know how I can get the TranslationInfo when getting the list of TranslationContent?

CalgaryFlames
  • 678
  • 2
  • 10
  • 30

1 Answers1

1

Oh, that seems to be an easy one:

You have to have the TranslationInfo included in your request. The TranslationContext is the table you are asking for, so EFCore, which you are using, is cutting all other tables out.

Change this:

 public List<TranslationContent> GetTranslationContentsByIndex(int index)
 {
            return _context.TranslationContents.Where(t => t.TranslationInfo.Index == index).ToList();
 }

to this:

 public List<TranslationContent> GetTranslationContentsByIndex(int index)
 {
            return _context.TranslationContents.Where(t => t.TranslationInfo.Index == index).Include(x => x.TranslationInfo).ToList();
 }

Or you could do it the other way around:

 public List<TranslationInfo> GetTranslationContentsByIndex(int index)
 {
            return _context.TranslationInfo.Where(t => t.Index == index).Include(x => x.TranslationContent).ToList();
 }
PassionateDeveloper
  • 14,558
  • 34
  • 107
  • 176
  • Thats very common, you have to care about. An easy way is to use the Attribute to not serialize the second deep path, OR get a global setting into your JSON config that self references should not be fullfilled OR use Automapper to ignore the cycle. – PassionateDeveloper Apr 20 '21 at 06:22
  • How about this case? Using your mention, when I call 'GetTranslationInfos()', I want to display the response with the result including TranslationContents on each TranslationInfo. So I updated like this 'return _context.TranslationInfos.Include(x =>x.TranslationContents).ToList();', but failed to get it with the error 'System.Text.Json.JsonException: A possible object cycle was detected which is not supported. This can either be due to a cycle or if the object depth is larger than the maximum allowed depth of 32.' – CalgaryFlames Apr 20 '21 at 06:40
  • 1
    Well, as I said, you will have this problem on each tables that have a reference to each other. It is well known in EFCore and you have 3 possible solutions here: 1) Disable the reference globaly in JSON config once 2) Use Automapper and disable each data model when it gets mapped 3) Use an Attribute that tells the JSON converter not to render it. – PassionateDeveloper Apr 20 '21 at 06:42
  • 1
    Here is a link that could help you:https://stackoverflow.com/questions/59199593/net-core-3-0-possible-object-cycle-was-detected-which-is-not-supported – PassionateDeveloper Apr 20 '21 at 06:43
  • You're right! After applying 'Microsoft.AspNetCore.Mvc.NewtonsoftJson' to API, it shows the result properly. I will try using Automapper instead of this package next time as you mentioned. Thanks a lot!!! – CalgaryFlames Apr 20 '21 at 06:59