How do I write this so that I can get the Id and the Fist and Last name all in one query? Everything I have found via Search is database related and involves a Join or a Group, which I don't think is applicable to what I am trying to do. I want to print the order Id and the associated first and last name to the screen.
static void Main()
{
XDocument document = XDocument.Parse(GetXml());
var query = from el in document.Root.Elements("Order")
select new Orders
{
Id = (int)el.Element("Id")
};
foreach (var cc in query)
{
Console.WriteLine(cc.Id);
}
var info = from el in document.Root.Elements("Order").Elements("BillingAddress")
select new BillingAddress
{
FirstName = (string)el.Element("FirstName"),
LastName = (string)el.Element("LastName")
};
foreach (var cc in info)
{
Console.WriteLine("{0} {1}", cc.FirstName, cc.LastName);
}
Console.ReadLine();
}
private static String GetXml()
{
return
#"<ArrayOfOrder>
<Order>
<Id>1</Id>
<OrderGuid />
<BillingAddress>
<FirstName>Harvey</FirstName>
<LastName>Danger</LastName>
</BillingAddress>
</Order>
<Order>
<Id>2</Id>
<OrderGuid />
<BillingAddress>
<FirstName>Brian</FirstName>
<LastName>Brakes</LastName>
</BillingAddress>
</Order>
</ArrayOfOrder>";
}
Add a BillingAddress property to your Orders class then you can do the following:
var query = from el in document.Root.Elements("Order")
select new Orders
{
Id = (int) el.Element("Id"),
BillingAddress = new BillingAddress
{
FirstName = (string) el.Element("BillingAddress").Element("FirstName"),
LastName = (string) el.Element("BillingAddress").Element("LastName")
}
};
var query = from el in document.Root.Elements("Order")
select new Orders
{
Id = (int)el.Element("Id"),
Names = el.Elements("BillingAddress")
.Select(ba=>
new { FirstName = (string)ba.Element("FirstName"),
LastName = (string)ba.Element("LastName")
}
};
Related
i need to add an object to a database several times with a different Sku code.
I have the Sku code changing in the for each loop but i don't know how to add the products to the database in the same method without getting this error,
i think i might need to make it async but not sure how.
here is the code
public static PetersContext db = new PetersContext();
static void Main(string[] args)
{
var sizeList = from ProductSizes in db.Sizes
where ProductSizes.SizeScale == 1//product.SizeScale.SizeScaleId
select (ProductSizes.SizeDesc);
var products = from Product in db.Products
select Product;
Product p1 = new Product()
{
ProductBrand = 1,
ProductCode = "Ts102",
CostPrice = 1,
SellPrice = 2,
ProductDescription = "Ted Smith Shirt",
ProductSeason = 1,
ProductType = 1,
};
foreach (var size in sizeList)
{
p1.ProductSkus = (p1.ProductCode + p1.ProductBrand.ToString() + p1.ProductColour.ToString() + size.ToString());
Console.WriteLine(p1.ProductSkus);
db.Products.Add(p1);
db.SaveChanges();
}
Console.ReadLine();
}
}
There are three things I would do differently in your approach.
You should create your context within a using statement.
Move the save changes function outside your for loop.
Create your p1 within the loop.
See the changes below:
using(var db = new PetersContext()) //open connection
{
var sizeList = from ProductSizes in db.Sizes
where ProductSizes.SizeScale == 1//product.SizeScale.SizeScaleId
select (ProductSizes.SizeDesc);
var products = from Product in db.Products
select Product;
foreach (var size in sizeList)
{
Product p1 = new Product() //Moving to inside of loop creates new instance every time
{
ProductBrand = 1,
ProductCode = "Ts102",
CostPrice = 1,
SellPrice = 2,
ProductDescription = "Ted Smith Shirt",
ProductSeason = 1,
ProductType = 1,
};
p1.ProductSkus = (p1.ProductCode + p1.ProductBrand.ToString() + p1.ProductColour.ToString() + size.ToString());
Console.WriteLine(p1.ProductSkus);
db.Products.Add(p1);
}
db.SaveChanges(); //save changes after everything is done.
}
Console.ReadLine();
I think your issue is the fact your foreach loop is evaluating the query during the loop when you are trying to call Savechanges() which wants to generate another transaction.
If you just change your SizeList and Product queries to have .ToList() at the end, this will force evaluation and you will then be using lists in your foreach, not a transactional query.
Updated to reflect the comments:
Looking at your code it looks like the ProductSku is a property of Product - you should perhaps consider making SKU a separate table so that you don't need to repeat all the standard product properties. However, to give what I think you are asking for you need something like this;
static void Main(string[] args)
{
using (PetersContext db = new PetersContext()) {
var sizeList = from ProductSizes in db.Sizes
where ProductSizes.SizeScale == 1//product.SizeScale.SizeScaleId
select (ProductSizes.SizeDesc);
var products = from Product in db.Products
select Product;
foreach (var size in sizeList)
{
foreach (var product in products)
{
Product newProduct = new Product()
{
ProductSkus = (product.ProductCode + product.ProductBrand.ToString() + product.ProductColour.ToString() + size.ToString()),
ProductBrand = product.ProductBrand,
ProductCode = product.ProductCode,
CostPrice = product.CostPrice,
SellPrice = product.SellPrice,
ProductDescription = produce.ProductDescription,
ProductSeason = product.ProductSeason,
ProductType = product.ProductType
};
Console.WriteLine(p1.ProductSkus);
db.Products.Add(newProduct);
}
}
db.SaveChanges();
Console.ReadLine();
}
}
There are other changes you could make, but the the big one I'm seeing is there's only one product reference. That same product object is added to the product collection several times in a loop. Each time, the loop also sets a new Sku... but since they are all the same object, the references from prior iterations of the loop reflect the new data.
To fix this, you need a new product object each time through the loop. You can offset that performance by moving the db.SaveChanges() call to after the loop.
Thanks for yer help people,
I created a new object in the for each, changed the sku in the new object, i forgot i need to give the products a size so i did that in a linq query in the same loop and added it to a product list, the product was then looped through in another for each and added to the db, it could do with a refactoring but it will do for now, thanks again guys
public static PetersContext db = new PetersContext();
static void Main(string[] args)
{
var sizeList = from ProductSizes in db.Sizes
where ProductSizes.SizeScale == 1//product.SizeScale.SizeScaleId
select (ProductSizes.SizeDesc);
var sizeIdList = from ProductSizes in db.Sizes
where ProductSizes.SizeScale == 1
select (ProductSizes.SizeId);
var products = from Product in db.Products
select Product;
Product p1 = new Product()
{
ProductBrand = 1,
ProductCode = "Ts102",
CostPrice = 27,
SellPrice = 79,
ProductDescription = "Ted Smith Shirt",
ProductSeason = 1,
ProductType = 1,
ProductColour=1
};
IList<Product> newProductList = new List<Product>();
foreach (var size in sizeList)
{
string newSku = (p1.ProductCode + p1.ProductBrand.ToString() + p1.ProductColour.ToString() + (size.ToString()));
Product newProduct = new Product()
{
ProductBrand = p1.ProductBrand,
ProductCode = p1.ProductCode,
CostPrice = p1.CostPrice,
SellPrice = p1.SellPrice,
ProductDescription = p1.ProductDescription,
ProductSeason = p1.ProductSeason,
ProductType = p1.ProductType,
ProductColour = p1.ProductColour,
ProductSkus= newSku,
};
newProduct.ProductSkus = newSku;
var SizeId =(from ProductSize in db.Sizes
where ProductSize.SizeDesc == size
select ProductSize.SizeId).First();
newProduct.ProductSize = SizeId;
newProductList.Add(newProduct);
}
foreach (var product in newProductList)
{
db.Products.Add(product);
db.SaveChanges();
}
Console.ReadLine();
I have the following responses from the API. How can I group them into the following structure?
Student[]
- Name
- Classes[]
- ClassName
- ClassId
- ClassCategories[]
- CategoryName
- CategoryWeight
- Assignments[]
- AssignmentName
- Score
I was managed to group them until the "Classes" level but unable to get the ClassCategories for each of the classes
var data = (from result in results
group result by new { result.StudentId, result.FirstName, result.LastName, result.MiddleInitial }
into StudentGroup
select new GroupedStudent
{
StudentId = StudentGroup.Key.StudentId,
FullName = string.Format("{0} {1} {2}", StudentGroup.Key.FirstName, StudentGroup.Key.MiddleInitial, StudentGroup.Key.LastName).Replace(" ", " "),
Classes = from result in results
group result by new { result.ClassId, result.ClassName } into ClassGroup
select new groupedClass
{
ClassName = ClassGroup.Key.ClassName,
ClassId = ClassGroup.Key.ClassId,
ClassCategories = ...
})
}).ToList();
Can anyone please assists me? Thank you.
First, you have make ClassGroup from StudentGroup not from results.
Classes = from s in StudentGroup group result by new { s.ClassId, s.ClassName } into ClassGroup
The complete linq query is as follows:
var data =
(from result in results
group result by new { result.StudentId, result.FirstName, result.LastName, result.MiddleInitial } into StudentGroup
select new
{
StudentId = StudentGroup.Key.StudentId,
FullName = string.Format("{0} {1} {2}", StudentGroup.Key.FirstName, StudentGroup.Key.MiddleInitial, StudentGroup.Key.LastName).Replace(" ", " "),
Classes = (from s in StudentGroup
group s by new { s.ClassId, s.ClassName } into ClassGroup
select new
{
ClassId = ClassGroup.Key.ClassId,
ClassName = ClassGroup.Key.ClassName,
ClassCategories = (from c in ClassGroup
group c by new { c.CategoryName, c.CategoryWeight } into CategoryGroup
select new
{
CategoryName = CategoryGroup.Key.CategoryName,
CategoryWeight = CategoryGroup.Key.CategoryWeight,
Assignments = (from ct in CategoryGroup
group ct by new { ct.AssignmentName, ct.Score } into AssingnmentGroup
select new
{
AssignmentName = AssingnmentGroup.Key.AssignmentName,
Score = AssingnmentGroup.Key.Score
}).ToList()
}).ToList()
}).ToList()
}).ToList();
For example, if you want to access to the first Assignment's score, you can get it like this:
var student = data.FirstOrDefault();
var score = student.Classes[0].ClassCategories[0].Assignments[0].Score;
This is usually how I do It.
Create a class to store your data
Create a list of that class type
In your case instead of string dataRow maybe you can use a sub class
.
// get data from webservice
var json = webClient.DownloadString(url);
var values = JsonConvert.DeserializeObject<JArray>(json);
// create a list to save all the element
List<myClass> classList = new List<myClass>();
// process every row
foreach (string dataRow in values)
{
string[] dataField = dataRow.Split(',');
// have a constructor to assign each value to this element
myClass ctROW = new myClass(dataField);
classList.add(ctROW );
I am trying to parse this data:
<Product>
<ProductName>Climate Guard</ProductName>
<Tag>ClimateGuard</Tag>
<SupportPage>~/Support/ClimateGuard.aspx</SupportPage>
<ProductPage>~/Products/ClimateGuard.aspx</ProductPage>
<ProductCategories>
<ProductCategory>Climate Guard</ProductCategory>
<PartNumbers>
<PartNumber Primary="true">CLIMATE GUARD</PartNumber>
<PartNumber>CLIMATEGUARD LT</PartNumber>
<PartNumber>CLIMATE GUARD STARTER KIT</PartNumber>
<PartNumber>SENSOR MODULE</PartNumber>
<PartNumber>SWCH INP MODULE</PartNumber>
<PartNumber>TEMP SENSOR</PartNumber>
<PartNumber>HUMIDITY SENSOR</PartNumber>
<PartNumber>DOOR CONTACT</PartNumber>
<PartNumber>MOTION SENSOR</PartNumber>
<PartNumber>FLOOD DETECTOR</PartNumber>
<PartNumber>SMOKE DETECTOR</PartNumber>
<PartNumber>TILT SENSOR</PartNumber>
<PartNumber>SENSOR CABLE</PartNumber>
<PartNumber>PWR INP CABLE</PartNumber>
<PartNumber>100FT 2-WIRE</PartNumber>
<PartNumber>RJ25 COUPLER</PartNumber>
</PartNumbers>
</ProductCategories>
<Downloads>
<Download>
<Version>1.0.27</Version>
<Url>~/Files/Downloads/ClimateGuard_Firmware_1_0_27.bin</Url>
<Comment>Firmware</Comment>
</Download>
<Download>
<Version>1.0.6</Version>
<Url>~/Files/Downloads/ClimateGuard_BuiltInModule_1_0_6.bin</Url>
<Comment>Built-in Module</Comment>
</Download>
<Download>
<Version>1.0.2</Version>
<Url>~/Files/Downloads/ClimateGuard_SensorModule_1_0_2.bin</Url>
<Comment>Sensor Module</Comment>
</Download>
<Download>
<Version>1.0.0</Version>
<Url>~/Files/Downloads/ClimateGuard_SwitchInputModule_1_0_0.bin</Url>
<Comment>Switch Input Module</Comment>
</Download>
</Downloads>
</Product>
I am trying to get a List of part numbers, however, only the first appears:
Product Category Climate Guard
Part Number Climate Guard
What is wrong with my part numbers code:
public List<Products> GetProducts()
{
XElement myElement = XElement.Load(HttpContext.Current.Server.MapPath("~/App_Data/products.xml"));
var query = from a in myElement.Elements("Product")
select new Products
{
ProductName = a.Element("ProductName").Value,
Tag = a.Element("Tag").Value,
SupportPage = a.Element("SupportPage").Value,
ProductPage = a.Element("ProductPage").Value,
ProductCategories = from b in a.Elements("ProductCategories")
select new ProductCategories
{
ProductCategory = b.Element("ProductCategory").Value,
//PartNumbers = GetPartNumbers(myElement.Elements("Product").Elements("ProductCategories").Elements("PartNumbers").Elements("PartNumber"))
PartNumbers = from c in b.Elements("PartNumbers")
select new PartNumbers
{
PartNumber = c.Element("PartNumber").Value
}
},
Downloads = from bb in a.Elements("Downloads").Elements("Download")
select new Downloads
{
Comment = bb.Element("Comment").Value,
Url = bb.Element("Url").Value,
Version = bb.Element("Version").Value
},
};
return query.ToList();
}
All of the types (ProductName, Tag, etc.) are strings. PartNumbers is an IEnumerable.
Currently instead of getting collection of PartNumber element values, you are getting only element for their parent PartNumbers with value of first PartNumber child inside. If you want to have PartNumbers class instead of simple list of string values, then it should look like:
public class PartNumbers
{
// list instead of single value
public List<string> Numbers { get; set; }
}
And it should be parsed this way:
PartNumbers = new PartNumbers {
Numbers = b.Element("PartNumbers").Elements()
.Select(c => (string)c).ToList()
}
BTW why are you choosing so strange range variable names (b for ProductCategories elements, a for products, etc)? Also you can use simple List<string> to store part numbers (without creating class for that):
PartNumbers = b.Element("PartNumbers").Elements().Select(c => (string)c).ToList()
You may have forgotten the ToList() for ProductCategories, PartNumbers and Downloads.
public List<Products> GetProducts()
{
XElement myElement = XElement.Load(HttpContext.Current.Server.MapPath("~/App_Data/products.xml"));
var query = from a in myElement.Elements("Product")
select new Products
{
ProductName = a.Element("ProductName").Value,
Tag = a.Element("Tag").Value,
SupportPage = a.Element("SupportPage").Value,
ProductPage = a.Element("ProductPage").Value,
ProductCategories = (from b in a.Elements("ProductCategories")
select new ProductCategories
{
ProductCategory = b.Element("ProductCategory").Value,
//PartNumbers = GetPartNumbers(myElement.Elements("Product").Elements("ProductCategories").Elements("PartNumbers").Elements("PartNumber"))
PartNumbers = (from c in b.Elements("PartNumbers")
select new PartNumbers
{
PartNumber = c.Element("PartNumber").Value
}).ToList()
}).ToList(),
Downloads = (from bb in a.Elements("Downloads").Elements("Download")
select new Downloads
{
Comment = bb.Element("Comment").Value,
Url = bb.Element("Url").Value,
Version = bb.Element("Version").Value
}).ToList(),
};
return query.ToList();
}
Consider this XML:
I store this XML in XElemnt.How I can loop throw Person elements and get value ID,Name,LastName for each person?
var doc = XDocument.Load(<filePath>);
var people = from person in doc.Descendents("Person")
select new Person{
ID = (int)person.Element("ID"),
Name = (string)person.Element("Name"),
LastName = (string)person.Element("LastName");
};
return people.ToList();
using XElement, you will get all the people in people variable.
XElement d = XElement.Load("D:\\people.xml");
var people = (from p in d.Descendants("Person")
select new
{
ID = Convert.ToInt32(p.Element("ID").Value),
Name = p.Element("Name").Value,
LastName = p.Element("LastName").Value
}).ToList();
what is the best way of reading xml file using linq and the below code you will see that, I have three different loops and I feel like its not elegant or do I have options to retrofit the below code?
public static void readXMLOutput(Stream stream)
{
XDocument xml = new XDocument();
xml = LoadFromStream(stream);
var header = from p in xml.Elements("App").Elements("Application")
select p;
foreach (var record in header)
{
string noym = record.Element("nomy").Value;
string Description = record.Element("Description").Value;
string Name = record.Element("Name").Value;
string Code = record.Element("Code").Value;
}
var appRoles = from q in xml.Elements("App").Elements("Application").Elements("AppRoles").Elements("Role")
select q;
foreach (var record1 in appRoles)
{
string Name = record1.Element("Name").Value;
string modifiedName = record1.Element("ModifiedName").Value;
}
var memeber = from r in xml.Elements("App").Elements("Application").Elements("AppRoles").Elements("Role").Elements("Members")
select r;
foreach (var record2 in memeber)
{
string ExpirationDate = record2.Element("ExpirationDate").Value;
string FullName = record2.Element("FullName").Value;
}
}
UPDATED:
foreach (var record in headers)
{
..............
string Name1 = record.Attribute("Name").Value;
string UnmodifiedName = record.Attribute("UnmodifiedName").Value;
string ExpirationDate = record.Attribute("ExpirationDate").Value;
string FullName = record.Attribute("FullName").Value;
...............
}
Is that your actual code ? All those string variables you are assigning in the foreach loops only have a scope of one iteration of the loop. They are created and destroyed each time.
This may not work precisely in your case depending on the xml structure. Play around with it. Try it using LinqPad
var applications = from p in xml.Descendants("Application")
select new { Nomy = p.Element("nomy").Value
, Description = p.Element("Description").Value
, Name = p.Element("Name").Value
, Code = p.Element("Code").Value
};
var appRoles = from r in xml.Descendants("Role")
select new { Name = r.Element("Name").Value
, ModifiedName = r.Element("ModifiedName").Value
};
This answer is a hierarchical query.
var headers =
from header in xml.Elements("App").Elements("Application")
select new XElement("Header",
new XAttribute("noym", header.Element("nomy").Value),
new XAttribute("Description", header.Element("Description").Value),
new XAttribute("Name", header.Element("Name").Value),
new XAttribute("Code", header.Element("Code").Value),
from role in header.Elements("AppRoles").Elements("Role")
select new XElement("Role",
new XAttribute("Name", role.Element("Name").Value),
new XAttribute("ModifiedName", role.Element("ModifiedName").Value),
from member in role.Elements("Members")
select new XElement("Member",
new XAttribute("ExpirationDate", member.Element("ExpirationDate").Value),
new XAttribute("FullName", member.Element("FullName").Value)
)
)
);