<?xml version="1.0" standalone="yes"?>
<CompanyInfo>
<Employee name="Jon" deptId="123">
<Region name="West">
<Area code="96" />
</Region>
<Region name="East">
<Area code="88" />
</Region>
</Employee>
</CompanyInfo>
public class Employee
{
public string EmployeeName { get; set; }
public string DeptId { get; set; }
public List<string> RegionList {get; set;}
}
public class Region
{
public string RegionName { get; set; }
public string AreaCode { get; set; }
}
I am trying to read this XML data, so far I have tried this:
XDocument xml = XDocument.Load(#"C:\data.xml");
var xElement = xml.Element("CompanyInfo");
if (xElement != null)
foreach (var child in xElement.Elements())
{
Console.WriteLine(child.Name);
foreach (var item in child.Attributes())
{
Console.WriteLine(item.Name + ": " + item.Value);
}
foreach (var childElement in child.Elements())
{
Console.WriteLine("--->" + childElement.Name);
foreach (var ds in childElement.Attributes())
{
Console.WriteLine(ds.Name + ": " + ds.Value);
}
foreach (var element in childElement.Elements())
{
Console.WriteLine("------->" + element.Name);
foreach (var ds in element.Attributes())
{
Console.WriteLine(ds.Name + ": " + ds.Value);
}
}
}
}
This enables me to get each node, its attribute name and value and so I can save these data into the relevant field in database, but this seems a long winded way and
not flexible, for instance if the XML structure changes all those foreach statements needs revisiting, also it is difficult to filter the data this way,
I need to write certain if statements to filter the data (e.g get employees from West only etc...)
I was looking for a more flexible way, using linq, something like this:
List<Employees> employees =
(from employee in xml.Descendants("CompanyInfo")
select new employee
{
EmployeeName = employee.Element("employee").Value,
EmployeeDeptId = ?? get data,
RegionName = ?? get data,
AreaCode = ?? get data,,
}).ToList<Employee>();
But I am not sure how I can get the values from the child nodes and apply the filtering (to get the certain employees only). Is this possible? Any help is appreciated.
Thanks
var employees = (from e in xml.Root.Elements("Employee")
let r = e.Element("Region")
where (string)r.Attribute("name") == "West"
select new Employee
{
EmployeeName = (string)e.Attribute("employee"),
EmployeeDeptId = (string)e.Attribute("deptId"),
RegionName = (string)r.Attribute("name"),
AreaCode = (string)r.Element("Area").Attribute("code"),
}).ToList();
But it will still require query revision when XML file structure changes.
Edit
Query for multiple regions per employee:
var employees = (from e in xml.Root.Elements("Employee")
select new Employee
{
EmployeeName = (string)e.Attribute("employee"),
DeptId = (string)e.Attribute("deptId"),
RegionList = e.Elements("Region")
.Select(r => new Region {
RegionName = (string)r.Attribute("name"),
AreaCode = (string)r.Element("Area").Attribute("code")
}).ToList()
}).ToList();
You can then filter the list for employees from given region only:
var westEmployees = employees.Where(x => x.RegionList.Any(r => r.RegionName == "West")).ToList();
You can track the structure:
from employee in xml
.Element("CompanyInfo") // must be root
.Elements("Employee") // only directly children of CompanyInfo
or less strictly
from employee in xml.Descendants("Employee") // all employees at any level
And then get the information you want:
select new Employee
{
EmployeeName = employee.Attribute("name").Value,
EmployeeDeptId = employee.Attribute("deptId").Value,
RegionName = employee.Element("Region").Attribute("name").Value,
AreaCode = employee.Element("Region").Element("Area").Attribute("code").Value,
}
And with the additional info about multiple regions, assuming a List<Region> Regions property:
select new Employee
{
EmployeeName = employee.Attribute("name").Value,
EmployeeDeptId = employee.Attribute("deptId").Value,
//RegionName = employee.Element("Region").Attribute("name").Value,
//AreaCode = employee.Element("Region").Element("Area").Attribute("code").Value,
Regions = (from r in employee.Elements("Region") select new Region
{
Name = r.Attribute("name").Value,
Code = r.Element("Area").Attribute("code").Value,
}).ToList();
}
You can do the selection in one query and then the filtering in second or combine them both to one query:
Two queries:
// do te transformation
var employees =
from employee in xml.Descendants("CompanyInfo").Elements("Employee")
select new
{
EmployeeName = employee.Attribute("name").Value,
EmployeeDeptId = employee.Attribute("deptId").Value,
Regions = from region in employee.Elements("Region")
select new
{
Name = region.Attribute("name").Value,
AreaCode = region.Element("Area").Attribute("code").Value,
}
};
// now do the filtering
var filteredEmployees = from employee in employees
from region in employee.Regions
where region.AreaCode == "96"
select employee;
Combined one query (same output):
var employees2 =
from selectedEmployee2 in
from employee in xml.Descendants("CompanyInfo").Elements("Employee")
select new
{
EmployeeName = employee.Attribute("name").Value,
EmployeeDeptId = employee.Attribute("deptId").Value,
Regions = from region in employee.Elements("Region")
select new
{
Name = region.Attribute("name").Value,
AreaCode = region.Element("Area").Attribute("code").Value,
}
}
from region in selectedEmployee2.Regions
where region.AreaCode == "96"
select selectedEmployee2;
But there is one little thing you should consider adding. For robustness, you need to check existence of your elements and attributes then the selection will look like that:
var employees =
from employee in xml.Descendants("CompanyInfo").Elements("Employee")
select new
{
EmployeeName = (employee.Attribute("name") != null) ? employee.Attribute("name").Value : string.Empty,
EmployeeDeptId = (employee.Attribute("deptId") != null) ? employee.Attribute("deptId").Value : string.Empty,
Regions = (employee.Elements("Region") != null)?
from region in employee.Elements("Region")
select new
{
Name = (region.Attribute("name")!= null) ? region.Attribute("name").Value : string.Empty,
AreaCode = (region.Element("Area") != null && region.Element("Area").Attribute("code") != null) ? region.Element("Area").Attribute("code").Value : string.Empty,
}
: null
};
Related
I have 3 tables structured in the following way:
CREATE TABLE [User](
Id int NOT NULL,
Name varchar(50)
PRIMARY KEY (Id)
)
CREATE TABLE [Role](
Id int NOT NULL,
UserId int NOT NULL,
Name varchar(50),
PRIMARY KEY (Id),
FOREIGN KEY (UserId) REFERENCES [User](Id)
)
CREATE TABLE [Description](
Id int NOT NULL,
RoleId int NOT NULL,
Name varchar(50)
FOREIGN KEY (RoleId) REFERENCES [Role](Id)
)
As you can see it is one to many relationship nested twice. In the code I have the following classes to represent them:
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public IEnumerable<Role> Roles { get; set; }
}
public class Role
{
public int Id { get; set; }
public int UserId { get; set; }
public string Name { get; set; }
public IEnumerable<Description> Descriptions { get; set; }
}
public class Description
{
public int Id { get; set; }
public int RoleId { get; set; }
public string Name { get; set; }
}
Now I need to query for the user and also get all fields that come with it. I have figured out a way to do that using QueryMultiple such as this:
var queryOne = "SELECT Id, Name FROM [User] WHERE Id = 1";
var queryTwo = "SELECT r.Id, r.UserId, r.Name FROM [User] u INNER JOIN [Role] r ON u.Id = r.UserId WHERE u.Id = 1";
var queryThree = "SELECT d.Id, d.RoleId, d.Name FROM [User] u INNER JOIN [Role] r ON u.Id = r.UserId INNER JOIN [Description] d ON r.Id = d.RoleId WHERE u.Id = 1";
var conn = new SqlConnection();
using (var con = conn)
{
var result = con.QueryMultiple(queryOne + " " + queryTwo + " " + queryThree);
var users = result.Read<User>().FirstOrDefault();
var roles = result.Read<Role>();
var descriptions = result.Read<Description>();
if (users != null && roles != null)
{
users.Roles = roles;
Console.WriteLine("User: " + users.Name);
foreach (var role in users.Roles)
{
Console.WriteLine("Role: " + role.Name);
if (descriptions != null)
{
role.Descriptions = descriptions.Where(d => d.RoleId == role.Id);
foreach (var roleDescription in role.Descriptions)
{
Console.WriteLine("Description: " + roleDescription.Name);
}
}
}
}
}
The result is:
User: Bob
Role: Tester
Description: Tester First Description
Description: Tester Second Description
Description: Tester Third Description
Role: Manager
Description: Manager First Description
Description: Manager Second Description
Description: Manager Third Description
Role: Programmer
Description: Programmer First Description
Description: Programmer Second Description
Description: Programmer Third Description
Main Question:
While the above code works it feels too messy. I was wondering if there is a better/easier way to achieve this?
Bonus Points:
Please also feel free to suggest a better way to query this than using inner joins. My goal is to improve performance.
EDIT:
I came up with option two as well, but again I don't think its a good solution. With option 2 I create a 4th object that will contain results of the 3 objects combined such as this:
public class Combination
{
public int UserId { get; set; }
public string UserName { get; set; }
public int RoleId { get; set; }
public string RoleName { get; set; }
public int DescriptionId { get; set; }
public string DescriptionName { get; set; }
}
Then I process it like this:
var queryFour = "SELECT u.Id as 'UserId', u.Name as 'UserName', r.Id as 'RoleId', r.Name as 'RoleName', d.Id as 'DescriptionId', d.Name as 'DescriptionName' FROM [User] u INNER JOIN [Role] r ON u.Id = r.UserId INNER JOIN [Description] d ON r.Id = d.RoleId WHERE u.Id = 1";
var conn = new SqlConnection();
using (var con = conn)
{
var myUser = new User();
var result = con.Query<Combination>(queryFour);
if (result != null)
{
var user = result.FirstOrDefault();
myUser.Id = user.UserId;
myUser.Name = user.UserName;
var roles = result.GroupBy(x => x.RoleId).Select(x => x.FirstOrDefault());
var myRoles = new List<Role>();
if (roles != null)
{
foreach (var role in roles)
{
var myRole = new Role
{
Id = role.RoleId,
Name = role.RoleName
};
var descriptions = result.Where(x => x.RoleId == myRole.Id);
var descList = new List<Description>();
foreach (var description in descriptions)
{
var desc = new Description
{
Id = description.DescriptionId,
RoleId = description.RoleId,
Name = description.DescriptionName
};
descList.Add(desc);
}
myRole.Descriptions = descList;
myRoles.Add(myRole);
}
}
myUser.Roles = myRoles;
}
Console.WriteLine("User: " + myUser.Name);
foreach (var myUserRole in myUser.Roles)
{
Console.WriteLine("Role: " + myUserRole.Name);
foreach (var description in myUserRole.Descriptions)
{
Console.WriteLine("Description: " + description.Name);
}
}
}
The resulting output is the same in both methods and the second method uses 1 query as opposed to 3.
EDIT 2: Something to consider, my data for these 3 tables are updated often.
EDIT 3:
private static void SqlTest()
{
using (IDbConnection connection = new SqlConnection())
{
var queryOne = "SELECT Id FROM [TestTable] With(nolock) WHERE Id = 1";
var queryTwo = "SELECT B.Id, B.TestTableId FROM [TestTable] A With(nolock) INNER JOIN [TestTable2] B With(nolock) ON A.Id = B.TestTableId WHERE A.Id = 1";
var queryThree = "SELECT C.Id, C.TestTable2Id FROM [TestTable3] C With(nolock) INNER JOIN [TestTable2] B With(nolock) ON B.Id = C.TestTable2Id INNER JOIN [TestTable] A With(nolock) ON A.Id = B.TestTableId WHERE A.Id = 1";
var gridReader = connection.QueryMultiple(queryOne + " " + queryTwo + " " + queryThree);
var user = gridReader.Read<Class1>().FirstOrDefault();
var roles = gridReader.Read<Class2>().ToList();
var descriptions = gridReader.Read<Class3>().ToLookup(d => d.Id);
user.Roles= roles;
user.Roles.ForEach(r => r.Properties = descriptions[r.Id].ToList());
}
}
Your first option can be simplified to the following. It removes the deep control structure nesting. You can generalize it to deeper nesting without adding more nesting/complexity.
var queryOne = "SELECT Id, Name FROM [User] WHERE Id = 1";
var queryTwo = "SELECT r.Id, r.UserId, r.Name FROM [User] u INNER JOIN [Role] r ON u.Id = r.UserId WHERE u.Id = 1";
var queryThree = "SELECT d.Id, d.RoleId, d.Name FROM [User] u INNER JOIN [Role] r ON u.Id = r.UserId INNER JOIN [Description] d ON r.Id = d.RoleId WHERE u.Id = 1";
var conn = new SqlConnection();
using (var con = conn)
{
var gridReader = con.QueryMultiple(queryOne + " " + queryTwo + " " + queryThree);
var user = gridReader.Read<User>().FirstOrDefault();
if (user == null)
{
return;
}
var roles = gridReader.Read<Role>().ToList();
var descriptions = gridReader.Read<Description>().ToLookup(d => d.RoleId);
user.Roles = roles;
roles.ForEach(r => r.Descriptions = descriptions[r.Id]);
}
Performance-wise it behaves the same as your first option.
I would't go with your second option (or the similar view based one): in case you have R roles, and on average D descriptions per roles, you would query 6*R*D cells instead of 2+3*R+3*D. If R and D are high, you'd be querying a lot more data, and the cost of deserialization will be to high compared to running 3 queries instead of 1.
An alternative approach (Working code snippet with .Net Core):
Why not create a database View like this -
create view myview
As
SELECT u.Id as 'UserId', u.Name as 'UserName', r.Id as 'RoleId',
r.Name as 'RoleName', d.Id as 'DescriptionId', d.Name as 'DescriptionName'
FROM User u INNER JOIN Role r ON u.Id = r.UserId
INNER JOIN Description d ON r.Id = d.RoleId;
Then you can use your cleaner query as below :
public List<Combination> GetData(int userId)
{
String query = "select * from myview" + " where userId = " + userId + ";";
using (System.Data.Common.DbConnection _Connection = database.Connection)
{
_Connection.Open();
return _Connection.Query<Combination>(query).ToList();
}
}
And your processing code will look like this :
[Note: This can be enhanced even further.]
public static void process (List<Combination> list)
{
User myUser = new User(); myUser.Id = list[0].UserId; myUser.Name = list[0].UserName;
var myroles = new List<Role>(); var r = new Role(); string currentRole = list[0].RoleName;
var descList = new List<Description>(); var d = new Description();
// All stuff done in a single loop.
foreach (var v in list)
{
d = new Description() { Id = v.DescriptionId, RoleId = v.RoleId, Name = v.DescriptionName };
if (currentRole == v.RoleName)
{
r = new Role() { Id = v.RoleId, Name = v.RoleName, UserId = v.UserId, Descriptions = descList };
descList.Add(d);
}
else
{
myroles.Add(r);
descList = new List<Description>(); descList.Add(d);
currentRole = v.RoleName;
}
}
myroles.Add(r);
myUser.Roles = myroles;
Console.WriteLine("User: " + myUser.Name);
foreach (var myUserRole in myUser.Roles)
{
Console.WriteLine("Role: " + myUserRole.Name);
foreach (var description in myUserRole.Descriptions)
{
Console.WriteLine("Description: " + description.Name);
}
}
}
From the performance standpoint, Inner Joins are better than other forms of query like subquery, Co-related subquery, etc. But ultimately everything boils down to the SQL execution plan.
The tl;dr of my answer to you:
Abstract. You should break-up things into the different jobs they do. C# gives you functions and classes, they can help clean up code a lot.
If you can look at a chunk of code and say "this chunk is basically taking something and doing blah with it to get some result..", then make a function called SomeResult DoBlah(SomeThing thing)
Don't let a little more code scare you, it may make the big picture seem more complex at first glance. But it will make small chunks of code way easier to understand, and that makes the big picture easier to understand.
My full answer:
There are a couple of things you could do that would make this cleaner. Probably the biggest thing you could do for yourself, is to separate out some of the responsibilities/concerns. You have a lot of different things going on, and all of it is in one place, relying on everything staying exactly like it is, forever. If you change something, you will have to change things all over the place to make sure everything works. This is called "coupling", and coupling is bad... um-kay.
At the broadest level, you have three different things going on: 1. You are trying to apply logic to users and roles (i.e. You want to get info on all of the users in a role.) 2. You are trying to pull info from a database, and fit it into User, Role, and Description objects. 3. You are trying to display information about Users, Roles, and Descriptions.
So if I were you, I would have at least 3 classes right there. \
Program that is the driver of your application. It should have a main function and should use the other 3 classes.
DisplayManager that can be responsible for writing info out to a console. It should be constructed with an IEnumerable<User> and should have a public method public void DisplayInfo() that will write its list of users to a console.
UserDataAccess that can be used for fetching a list of users from a database. I would have it expose a public method for public IEnumerable<User> GetUsersByRoleID(int roleID)
This way, your main program would look something like this:
public static void Main(String[] args)
{
int roleID = 6;
UserDataAccess dataAccess = new UserDataAccess();
IEnumerable<User> usersInRole = dataAccess.GetUsersByRoleID(roleID);
DisplayManager displayManager = new DisplayManager(usersInRole);
displayManager.DisplayInfo();
}
Honestly, this is a simplified version of what I would do if the problem had more functionality then just getting a single specific piece of info. You should look into SOLID principles. Specifically "Single Responsibility" and "Dependency Inversion", these principles can really clean up some "messy"/"smelly" code.
Next, as far as your actually data access goes, since you are using dapper, you should be able to map nested objects using build in dapper functionality. I believe this link should be of some help to you in this aspect.
I have the following XML. What is the best way to get the data?
<?xml version='1.0' encoding='UTF-8'?>
<Root>
<EmployeeDataRoot>
<EmployeeData>
<Employee_id>123456</Employee_id>
<Employee_Status>A</Employee_Status>
<Business_Unit>EN00</Business_Unit>
<Cost_Center>0904/1992</Cost_Center>
<Work_Location>DFW</Work_Location>
<Location>DFW-HDQ1</Location>
<Job_Category>0003</Job_Category>
<Last_Name>John</Last_Name>
<First_Name>Doe</First_Name>
<Middle_Name />
<Preferred_Name />
<Position_Title>Programmer/Analyst</Position_Title>
<Legal_Entity>EN00</Legal_Entity>
<Department_Unit>IT HR & Employee Technology</Department_Unit>
<Run_Date>2016-12-12</Run_Date>
</EmployeeData>
</EmployeeDataRoot>
<Footer_No_of_Records>
<Records>1</Records>
</Footer_No_of_Records>
</Root>
After looking at some examples online, I tried these two iterations but get an error
object not set to an instance of an object
I looked over the properties of my Employee class as well as the Nodes for any misspellings and didn't see any. I think the error is that I'm not querying the XML properly.
var xDoc = XDocument.Load(file.FullName);
listEmployee =
(from e in xDoc.Descendants("EmployeeData")
select new Employee
{
EmployeeID = e.Element("Employee_ID").Value,
EmployeeStatus = e.Element("Employee_Status").Value,
BusinessUnit = e.Element("Business_Unit").Value,
CostCenter = e.Element("Cost_Center").Value,
WorkLocation = e.Element("Work_Location").Value,
Location = e.Element("Location").Value,
JobCategory = e.Element("Job_Category").Value,
FirstName = e.Element("First_Name").Value,
LastName = e.Element("Last_Name").Value,
LegalEntity = e.Element("Legal_Entity").Value
}
).ToList();
and I also tried
listEmployee =
(from e in xDoc.Element("Root").Elements("EmployeeDataRoot/EmployeeData")
select new Employee
{
EmployeeID = e.Element("Employee_ID").Value,
EmployeeStatus = e.Element("Employee_Status").Value,
BusinessUnit = e.Element("Business_Unit").Value,
CostCenter = e.Element("Cost_Center").Value,
WorkLocation = e.Element("Work_Location").Value,
Location = e.Element("Location").Value,
JobCategory = e.Element("Job_Category").Value,
FirstName = e.Element("First_Name").Value,
LastName = e.Element("Last_Name").Value,
LegalEntity = e.Element("Legal_Entity").Value
}
).ToList();
Your attempt, ist right, but you write "Employee_ID" wrong. Try this out:
var xDoc = XDocument.Load(file.FullName);
listEmployee =
(from e in xDoc.Descendants("EmployeeData")
select new Employee
{
EmployeeID = e.Element("Employee_id").Value,
EmployeeStatus = e.Element("Employee_Status").Value,
BusinessUnit = e.Element("Business_Unit").Value,
CostCenter = e.Element("Cost_Center").Value,
WorkLocation = e.Element("Work_Location").Value,
Location = e.Element("Location").Value,
JobCategory = e.Element("Job_Category").Value,
FirstName = e.Element("First_Name").Value,
LastName = e.Element("Last_Name").Value,
LegalEntity = e.Element("Legal_Entity").Value
}
).ToList();
I have a problem with LINQ query (see comment) there is a First method and it only shows me the first element.
When I write in the console "Sales Representative" it shows me only the first element of it as in
I would like to get all of data about Sales Representative. How can I do it?
public PracownikDane GetPracownik(string imie)
{
PracownikDane pracownikDane = null;
using (NORTHWNDEntities database = new NORTHWNDEntities())
{
//Employee matchingProduct = database.Employees.First(p => p.Title == imie);
var query = from pros in database.Employees
where pros.Title == imie
select pros;
// Here
Employee pp = query.First();
pracownikDane = new PracownikDane();
pracownikDane.Tytul = pp.Title;
pracownikDane.Imie = pp.FirstName;
pracownikDane.Nazwisko = pp.LastName;
pracownikDane.Kraj = pp.Country;
pracownikDane.Miasto = pp.City;
pracownikDane.Adres = pp.Address;
pracownikDane.Telefon = pp.HomePhone;
pracownikDane.WWW = pp.PhotoPath;
}
return pracownikDane;
}
Right now you are just getting the .First() result from the Query collection:
Employee pp = query.First();
If you want to list all employees you need to iterate through the entire collection.
Now, if you want to return all the employee's you should then store each new "pracownikDane" you create in some sort of IEnumerable
public IEnumerable<PracownikDane> GetPracownik(string imie) {
using (NORTHWNDEntities database = new NORTHWNDEntities())
{
var query = from pros in database.Employees
where pros.Title == imie
select pros;
var EmployeeList = new IEnumerable<PracownikDane>();
foreach(var pp in query)
{
EmployeeList.Add(new PracownikDane()
{
Tytul = pp.Title,
Imie = pp.FirstName,
Nazwisko = pp.LastName,
Kraj = pp.Country,
Miasto = pp.City,
Adres = pp.Address,
Telefon = pp.HomePhone,
WWW = pp.PhotoPath
});
}
return EmployeeList;
}
Then, with this returned List you can then do what ever you wanted with them.
The sample below is parsing an XML document then looping through the members and storing them in a list of objects (The data ultimately ends up in an SQL database):
public static void Parse(XDocument xml)
{
XNamespace ns = "http://somenamespace.com/ns";
var Locations =
from Continents in xml.Descendants(ns + "Continent")
from Countries in Continents.Elements(ns + "Country")
select new
{
Continent1 = (string) Continents.Element(ns + "Europe"),
Country1 = (string) Countries.Element(ns + "United_Kingdom"),
Cities = from Cities in Countries.Elements(ns + "City")
select new
{
City1 = (string) Cities.Element(ns + "London")
}
};
List<Location> locationColl = new List<Location>();
loc_Entity_FrameworkContainer context = new loc_Entity_FrameworkContainer();
var i = 0;
foreach (var location in Locations)
{
Location l = new Location();
locationColl.Add(l);
locationColl[i].Continent = (string) location.Continent1;
locationColl[i].Country = (string) location.Country1;
locationColl[i].City = (string) location.City1; // Can't access "City1"
context.Location.Add(locationColl[i]);
i++;
}
context.SaveChanges();
}
The statement: locationColl[i].City = (string)location.City1;
doesn't find "City1". (This is the issue, I can't access all the members from "Locations" in one loop)
Location Class:
namespace locationProject
{
using System;
using System.Collections.Generic;
public partial class Location
{
public string Continent { get; set; }
public string Country { get; set; }
public string City { get; set; }
}
}
XML Example:
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns:ns="http://somenamespace.com/ns">
<ns:Continent>
<ns:Europe>21c99a56-4b3d-4571-802a-76cdb6b81a01</ns:Europe>
<ns:Country>
<ns:United_Kingdom>eb2e9eec-dc3b-4636-bcf5-dba0024e62f3</ns:United_Kingdom>
<ns:City>
<ns:London>109b48ec-d829-4a87-b200-4dc9a94db48c</ns:London>
</ns:City>
</ns:Country>
</ns:Continent>
<ns:Continent>
<ns:Europe>a11ed925-dc0d-4dfd-b1c2-52eb697ad689</ns:Europe>
<ns:Country>
<ns:United_Kingdom>a61d02ef-7b80-4390-926a-49c6d9af9634</ns:United_Kingdom>
<ns:City>
<ns:London>dbb9c5cc-b08f-4223-b32c-acb4ed9ce97c</ns:London>
</ns:City>
</ns:Country>
</ns:Continent>
</feed>
I'm trying to find a way of looping through all the elements (Continent1, Country1, City1) that doesn't involve multiple loops and doesn't break the nested structure of the LINQ statements.
There are questions on here similar to this one, but I haven't found one I understand well enough to integrate with my code.
Thanks a lot!
Your anonymous type contained in the Locations list has a .Cities property that contains a City1 member:
Cities = from Cities in Countries.Elements(ns + "City")
select new
{
City1 = (string) Cities.Element(ns + "London")
}
Try this:
var Locations =
from Continents in xml.Descendants(ns + "Continent")
from Countries in Continents.Elements(ns + "Country")
from Cities in Countries.Elements(ns + "City")
select new
{
Continent1 = (string) Continents.Element(ns + "Europe"),
Country1 = (string) Countries.Element(ns + "United Kingdom"),
City1 = (string) Cities.Element(ns + "London")
};
Using WCF RIA I have a query that returns a Query of names
public class WitnessInfo
{
[Key]
public Guid WCFId { get; set; }
public string witnessName { get; set; }
public string AllNames {get; set;}
}
Here's my Linq Query
[Query]
public IQueryable<WitnessInfo> getWitnessInfo(int? id)
{
IQueryable<WitnessInfo> witnessQuery = from witness in this.Context.witness
where witness.DAFile.Id == id
select new WitnessInfo
{
WCFId = Guid.NewGuid(),
witnessName = witness.Person.FirstName,
};
return witnessQuery;
}
I want to take all the names and return them in a single string i.e "John, James, Tim, Jones". Tried taking AllNames and looping through but that didn't work. Any suggestions?
First grab all of the information that you need in a single query, then use String.Join to map the collection of names to a single string:
var firstQuery = from witness in Context.witness
where witness.DAFile.Id == id
select new
{
WCFId = Guid.NewGuid(),
witnessName = witness.Person.FirstName,
Names = Context.witness.Select(w => w.FirstName),
})
.AsEnumerable(); //do the rest in linq to objects
var finalQuery = from witness in firstQuery
//do the string manipulation just once
let allNames = string.Join(", ", witness.Names)
select new WitnessInfo
{
WCFId = witness.WCFId,
witnessName = witness.witnessName,
AllNames = allNames,
});
By having the AllNames property in the WitnessInfo class, it is seems like you want each WitnessInfo object to contain the all of the squence names again and again repeatedly, and if this is your case then do it like that:
var names = (from witness in this.Context.witness
select witness.Person.FirstName).ToArray();
var allNames = string.Join(", ", names);
IQueryable<WitnessInfo> witnessQuery = from witness in this.Context.witness
where witness.DAFile.Id == id
select new WitnessInfo
{
WCFId = Guid.NewGuid(),
witnessName = witness.Person.FirstName,
AllNames = allNames
};
You can concatenate like this:
string.Join(", ", getWithnessInfo(666).Select(wq => wq.witnessName))
this.Context.witness.Select(a => a.Person.Firstname).Aggregate((a, b) => a + ", " + b);