Search code examples
c#asp.netlinq-to-sqlasmx

A circular reference error with ASMX with linked table


I have the below code for an ASMX service in a Web Application. Attempting to get the data in XML format. I have removed some data for clarity

    /// <summary>
    /// Summary description for CompanyServices
    /// </summary>
    [WebService(Namespace = "http://tempuri.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    [System.ComponentModel.ToolboxItem(false)]
    // To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line. 
    // [System.Web.Script.Services.ScriptService]
    public class CompanyServices: System.Web.Services.WebService
    {
        [WebMethod]
        public List<Product> GetData(int companyId, int custId)
        {
          return ProductService.GetCompanyData(companyId, custId).ToList();
        }

In debug mode all runs but i keep getting the error

System.InvalidOperationException: There was an error generating the XML document. ---> System.InvalidOperationException: A circular reference was detected while serializing an object of type Company

So i created the same type and had a for each loop around it similar to

foreach (Product p in ProductService.GetCompanyData(companyId, custId).ToList())
{
  Product newProd = new Product
  newProd.Name = p.Name;
  newProd.Department = p.Department;
}

This worked until i added Department in, which is linked to another table (of Departments)

Googled around but no idea whats causing this or how to resolve?


Solution

  • Circular reference means you have an object a that references b (e.g. a.B = b) but somehow b references a back (like b.A = a).

    Unfortunately, it's not always that two objects point each to another. Your chain that leads to circularity can be longer (like product points to another product that points to department that points back to product).

    Most of the times the root cause is that your service exposes your raw Entity Framework (or other ORM) objects that are joined using parent-child relations. Because ORM navigation properties are lazily loaded, anytime you have a product, it has its parent (like product.Department) but a department has the Products property that points back to products and one of the products is the very same product you already visited at the beginning. There's your cycle.

    The solution is to create another set of classes, the DTO classes, where you maintain only one-way navigation properties. So for example, a ProductDTO has its parent, the DepartmentDTO but the DepartmentDTO intentionally lacks the IEnumerable<ProductDTO> Products property.

    This way the serializer that follows your navigation properties stops at some point as there are no cycles. Your service exposes these DTO classes

    [WebMethod]
    public List<ProductDTO> GetData(int companyId, int custId)
    {
       return ProductService.GetCompanyData(companyId, custId).ToDTO();
    }