How would I go about creating my desired list by using LINQToXml?
I am close with both attempts so far. I should be able to do this without creating to separate queries right?
This is my XML:
<main>
<cars>
<car name="Honda">
<feature door="4" name="Accord" />
<feature door="2" name="Civic"/>
<feature door="4" name="CRV"/>
</car>
<car name="Ford"/>
<car name="Kia"/>
<car name="Subaru">
<feature door="4" name="Outback"/>
<feature door="4" name="Legacy"/>
</car>
</cars>
</main>
Attempt #1
This will return the first car with feature.
var listCars = (from c in doc.Root.Descendants("cars")
select new Car
{
Model = (p.Element("car") != null) ? p.Element("car").Attribute("name").Value : null,
Door = (p.Element("car") != null) ? p.Element("car").Attribute("door").Value : null,
Name = p.Attribute("name").Value
}).ToList();
Attempt #2
This will return all of the cars that have features
Ford and Kia would be missing
var cars = from c in doc.Root.Descendants("cars")
select c;
var listPermissions = (from c in cars.Descendants("car")
let cName = p.Parent.Attribute("name").Value
select new Car
{
Model = p.Attribute("name").Value,
Door = p.Attribute("door").Value,
Name = pgn
}).ToList();
What I am trying to do is to create a list of cars that look like:
Honda, 4, Accord
Honda, 2, Civic
Honda, 4, CRV
Ford, null, null
Kia, null, null
Subaru, 4, Outback
Subaru, 4, Legacy
You can do this by using a LEFT JOIN in LINQ as outlined on 101 LINQ Samples.
var makes =
(from doc in document.Root.Descendants("cars").Descendants("car")
join f in document.Root.Descendants("cars").Descendants("car").Descendants("feature")
on doc.Attribute("name").Value.ToLowerInvariant() equals f.Parent.Attribute("name").Value.ToLowerInvariant() into ps
from f in ps.DefaultIfEmpty()
select new
{
Model = doc.Attribute("name").Value,
Door = f == null ? string.Empty : f.Attribute("door").Value,
Name = f == null ? string.Empty : f.Attribute("name").Value
})
.ToList();
Related
This is the first time that I created a XML document using LINQToXML.
I am trying to understand how can I conditionally create attributes(or elements) when creating my document?
In this example a given car may/may not have a feature to it, so in that case I would not want to create that element, I also may have certain attributes in the feature node that could be missing. How could I handle these scenarios?
XDocument xDoc = new XDocument(
new XElement("root",
new XElement("NodeA"),
new XElement("Cars",
from p in listCars
select new XElement("Car", new XAttribute("name", p.CarName),
new XElement("Feature", new XAttribute("door", p.Door), new XAttribute("model", p.Model))
)
)
)
);
Desired result #1 (All features are missing for a given car):
<root>
<NodeA />
<Cars>
<Car name="Honda">
<Feature door="4" model="Accord" />
</Car>
<Car name="Ford" />
</Cars>
</root>
Desired result #2 (Some features could exist)
<root>
<NodeA />
<Cars>
<Car name="Honda">
<Feature door="4" model="Accord" />
</Car>
<Car name="Ford">
<Feature model="Focus" />
</Car>
</Cars>
</root>
2 seperate solutions in here. Either use a method to create the features node, or do it all in one:
static void Main(string[] args)
{
var listCars = new List<Car>();
listCars.Add(new Car { CarName = "test 1", Door = "0", Model = "" });
listCars.Add(new Car { CarName = "test 2", Door = "", Model = "" });
listCars.Add(new Car { CarName = "test 3", Door = "0", Model = "0" });
XDocument xDoc2 = new XDocument(
new XElement("root",
new XElement("NodeA"),
new XElement("Cars",
from p in listCars
select new XElement("Car",
new XAttribute("name", p.CarName),
p.Door != "" || p.Model != "" ?
new XElement("Feature",
p.Door != "" ? new XAttribute("door", p.Door) : null,
p.Model != "" ? new XAttribute("model", p.Model) : null) : null
)
)
)
);
XDocument xDoc = new XDocument(
new XElement("root",
new XElement("NodeA"),
new XElement("Cars",
from p in listCars
select new XElement("Car",
new XAttribute("name", p.CarName),
CreateFeature(p)
)
)
)
);
}
static XElement CreateFeature(Car p)
{
var el = new XElement("Feature",
p.Door != "" ? new XAttribute("door", p.Door) : null,
p.Model != "" ? new XAttribute("model", p.Model) : null);
return !el.Attributes().Any() ? null : el;
}
If you supply null instead of an element, it will be ignored, so you can use constructs like the following.
p.CarName != null ? new XAttribute("name", p.CarName) : null
If you're using C# 6, you can use null propagation.
i have the following database-model: http://i.stack.imgur.com/gRtMD.png
the many to many relations for Kunde_Geraet/Kunde_Anwendung are in explicit Mapping-Table with additional Information.
i want to optimize the following LINQ-query:
var qkga = (from es in db.Eintrag_Systeme.Where(es => es.Eintrag_ID == id)
from kg in db.Kunde_Geraet.Where(kg => es.Geraet_ID == kg.Geraet_ID)
select new { Kunde = kg.Kunde, Geraet = es.Geraet, Anwendung = es.Anwendung })
.Union(
from es in db.Eintrag_Systeme.Where(es => es.Eintrag_ID == id)
from ka in db.Kunde_Anwendung.Where(ka => es.Anwendung_ID == ka.Anwendung_ID)
select new { Kunde = ka.Kunde, Geraet = es.Geraet, Anwendung = es.Anwendung })
.GroupBy(kga => kga.Kunde, kga => new {Geraet = kga.Geraet, Anwendung = kga.Anwendung});
it would be better, when the result is a IEnumerable(Kunde, IEnumerable(Geraet), IEnumerable(Anwendung)) without the null-Values for the union.
i try it as SQL command
select Count(es.Geraet_ID), null as Anwendung_ID
from Eintrag_Systeme es cross join Kunde_Geraet where es.Geraet_ID = Kunde_Geraet.Geraet_ID AND es.Eintrag_ID = #id
union
select null as Geraet_ID, Count(es.Anwendung_ID)
from Eintrag_Systeme es cross join Kunde_Anwendung where es.Anwendung_ID = Kunde_Anwendung.Anwendung_ID AND es.Eintrag_ID = #id
group by Kunde_ID
but donĀ“t get the Count() of Anwendungen(Apps)/Geraete(Devices) to Lists grouped by Key Kunde(Client)
Don't use join but navigation properties:
from k in context.Kunden
select new
{
Kunde = k,
Geraete = k.Kunde_Geraete.Select(kg => kg.Geraet),
Anwendungen = k.Kunde_Anwendungen.Select(ka => ka.Anwendung)
}
Now you have a basis from which you get counts, etc.
I want to group by category, show it's name, then show the highest id that is related to it. Here's some data and the result that I want further down. Any ideas? I've been playing around with GroupJoin but can't seem to get it to work.
My Data
var stuff = new[] {
new {id = 5, catId = 2},
new {id = 56, catId = 2},
new {id = 56, catId = 2},
new {id = 8, catId = 1},
new {id = 9, catId = 3}};
var categories = new[] {
new {catId = 1, Name = "Water"},
new {catId = 4, Name = "Wind"},
new {catId = 2, Name = "Fire"}};
What I want my results to look like
Water - 8
Wind - null
Fire - 56
categories
.GroupJoin
(
stuff,
c=>c.catId,
s=>s.catId,
(c,s)=>new
{
c.Name,
Max = s.Any() ? (int?)s.Max (m => m.id) : null
}
);
It seems that you want a "LEFT OUTER JOIN" with LINQ:
var query = from cat in categories
join s in stuff
on cat.catId equals s.catId into gj
from stuffJoin in gj.DefaultIfEmpty()
group stuffJoin by new { cat.catId, cat.Name } into catGroup
select new {
Category = catGroup.Key.Name,
MaxID = catGroup.Max(s => s == null ? 0 : s.id) // stuff is null for Wind
};
foreach (var x in query)
Console.WriteLine("Category: {0} Max-ID: {1}", x.Category, x.MaxID);
Outputs:
Category: Water Max-ID: 8
Category: Wind Max-ID: 0
Category: Fire Max-ID: 56
I have the following Linq to Sql:
var subscribers = (from s in dataContext.Employees
where s.RowType == "Edit"
select new SubscriberExportData
{
ID = s.ID.ToString(),
GroupNum = s.GroupNumber,
DivisionNum = s.DivisionNumber,
HireDate = s.HireDate != null ? Convert.ToDateTime(s.HireDate).ToShortDateString() : string.Empty,
EffDate = s.EffectiveDate != null ? Convert.ToDateTime(s.EffectiveDate).ToShortDateString() : string.Empty
}
Essentially, if the date value is not null then convert them to short date format. But I am getting the following error:
System.InvalidOperationException was unhandled
Message=Could not translate expression 'Table(Employee).Where(s => (s.RowType == "Edit")).Select(s => new SubscriberExportData() { HireDate = IIF((s.HireDate != null), ToDateTime(Convert(s.HireDate)).ToShortDateString(), Invoke(value(System.Func`1[System.String]))), EffDate = IIF((s.EffectiveDate != null), ToDateTime(Convert(s.EffectiveDate)).ToShortDateString)' into SQL and could not treat it as a local expression.
Source=System.Data.Linq
Please let me know how to resolve it.
You can split the query into two, the first being operations that sql understands and the second being the string conversions that will be performed locally
var list = (from s in dataContext.Employees
where s.RowType == "Edit"
select new
{
s.ID
s.GroupNumber,
s.DivisionNumber,
s.HireDate
s.EffectiveDate
}).ToList();
var subscribers = (from s in list
select new SubscriberExportData
{
ID = s.ID.ToString(),
GroupNum = s.GroupNumber,
DivisionNum = s.DivisionNumber,
HireDate = s.HireDate != null ? Convert.ToDateTime(s.HireDate).ToShortDateString() : string.Empty,
EffDate = s.EffectiveDate != null ? Convert.ToDateTime(s.EffectiveDate).ToShortDateString() : string.Empty
}
I have a heavily nested XML document that I need to load into my db for additional processing. For various reasons beyond the scope of this discussion I need to 'flatten' that structure down, then load it into a DataTables and then I can SQLBulkCopy it into the db where it will get processed. So assume my original XML looks something like this (mine is even more heavily nested, but this is the basic idea):
<data>
<report id="1234" name="XYZ">
<department id="234" name="Accounting">
<item id="ABCD" name="some item">
<detail id="detail1" value="1"/>
<detail id="detail2" value="2"/>
<detail id="detail3" value="3"/>
</item>
</department>
</report>
</data>
and I want to flatten that down into a single (albeit redundant) table structure where each attribute becomes a column (i.e. ReportId, ReportName, DepartmentId, DepartmentName, ItemId, ItemName, Detail1, Detail2, Detail3).
So my question is simply 'is it possible to accomplish this with a simple Linq query'? In the past I would just write some XSLT and be done with it but I'm curious if the Linq library can accomplish the same thing?
thanks!
Is this what you're looking for?
var doc = XDocument.Load(fileName);
var details =
from report in doc.Root.Elements("report")
from department in report.Elements("department")
from item in department.Elements("item")
from detail in item.Elements("detail")
select new
{
ReportId = (int)report.Attribute("id"),
ReportName = (string)report.Attribute("name"),
DepartmentId = (int)department.Attribute("id"),
DepartmentName = (string)department.Attribute("name"),
ItemId = (string)item.Attribute("id"),
ItemName = (string)item.Attribute("name"),
DetailId = (string)detail.Attribute("id"),
DetailValue = (int)detail.Attribute("value"),
};
If you want it as a DataTable, you can use the following extension method:
public static DataTable ToDataTable<T>(this IEnumerable<T> source)
{
PropertyInfo[] properties = typeof(T).GetProperties()
.Where(p => p.CanRead && !p.GetIndexParameters().Any())
.ToArray();
DataTable table = new DataTable();
foreach (var p in properties)
{
Type type = p.PropertyType;
bool allowNull = !type.IsValueType;
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
{
allowNull = true;
type = Nullable.GetUnderlyingType(type);
}
DataColumn column = table.Columns.Add(p.Name, type);
column.AllowDBNull = allowNull;
column.ReadOnly = !p.CanWrite;
}
foreach (var item in source)
{
DataRow row = table.NewRow();
foreach (var p in properties)
{
object value = p.GetValue(item, null) ?? DBNull.Value;
row[p.Name] = value;
}
table.Rows.Add(row);
}
return table;
}
Use it like this:
var table = details.CopyToDataTable();