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?
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();
}