Search code examples
c#foreachlinq-to-xmlxmldocument

Creating XmlDocument without for loop


Hello i am working on creating XmlDocument with product data. My code

foreach (var pv in pvs)
{
   XmlElement product = xml.CreateElement("Product");
   product.SetAttribute("SKU", pv.Sku);
   root.AppendChild(product);        

   var pname = xml.CreateElement("Name");
   pname.InnerText = pv.Product.Name;

   product.AppendChild(pname);

   var MRP = xml.CreateElement("Mrp");

   MRP.InnerText = Math.Round(pv.OldPrice, 2).ToString();

   var SKU_MRP = _barcodeService.GetMRPsBySku(pv.Sku);

   var mrps = SKU_MRP.AsEnumerable().Select(s => s.MRP).ToList();

   if (mrps.Count > 0)
   {    
      mrpstring = string.Join<string>(",", mrps.Select(x => Math.Round(x, 2).ToString()).Where(x => x != Math.Round(pv.OldPrice, 2).ToString()).ToList());
   }

   if (!string.IsNullOrEmpty(mrpstring))
   {
      MRP.InnerText += "," + mrpstring;
   }
   product.AppendChild(MRP);
}

xml.Save(path);

Above code I have pasted is only part of my whole function. Note that I am calling method from my function of barcodeservice which returns IList.

My pv count exceeds 5000. So it taked 5-7 minutes to generate complete file. Now is there any workaround to this to minimize load or how can i generate same file without using foreach.

Any Solutions?


Solution

  • If you need to generate big xml file, then use XmlWriter instead of XmlDocument. XmlDocument builds in-memory xml representation before you can save it to file. XmlWriter writes data directly to file.

    With XmlWriter your loop will look like:

    using(XmlWriter writer = XmlWriter.Create(fileName))
    {
       writer.WriteStartDocument();
       writer.WriteStartElement("Products");
    
       foreach (var pv in pvs)
       {
           writer.WriteStartElement("Product");
           writer.WriteAttributeString("SKU", pv.Sku);
           writer.WriteElementString("Name", pv.Product.Name);           
           // ...
           writer.WriteEndElement();
       }
    
       writer.WriteEndElement();
       writer.WriteEndDocument();
    }
    

    Also you can optimize mrpstring creation (if price is stored as decimal value):

    decimal oldPrice = Math.Round(pv.OldPrice, 2);
    var SKU_MRP = _barcodeService.GetMRPsBySku(pv.Sku);
    var newPrices = SKU_MRP.AsEnumerable()
                           .Select(s => Math.Round(s.MRP, 2))
                           .Where(mrp => mrp != oldPrice); 
    
    mrpstring = String.Join(",", newPrices);
    

    Thus you need only enumerate new prices, then you don't need to save them to list, also you don't need to check count, because if there is nothing to join, join will return empty string. Also if you are using decimals for price, then avoid converting them to strings, or consider make rounding only during mrpstring creation.