I am learning LINQ and I am stuck on how to go about printing out the department name and the manager's name seperate.
The code I have below prints out:
Department name: Account
Joe
Department name: Sales
Jack
Department name: Pre-Sales
Sam
Department name: Marketing
Jim
How can I seperate it , so that it will only print the department name on that line.
string myXML = #"<Departments>
<Department>Account
<Manager>Joe</Manager>
</Department>
<Department>Sales
<Manager>Jack</Manager>
</Department>
<Department>Pre-Sales
<Manager>Sam</Manager>
</Department>
<Department>Marketing
<Manager>Jim</Manager>
</Department>
</Departments>";
XDocument xdoc = new XDocument();
xdoc = XDocument.Parse(myXML);
var DeptResult = xdoc.Element("Departments").Descendants("Department");
var ManagerResult = xdoc.Element("Departments").Descendants("Manager");
foreach (XElement item in DeptResult)
{
Console.WriteLine("Department Name - " + item.Value);
}
You can do this using Zip extension method:
var departments=xdoc.Descendants("Department");
var managers=xdoc.Descendants("Manager");
foreach( var s in departments.Zip(managers, (d,m)=>"Department name: "+
d.Value+"#\n"+
"Manager name: "+ m.Value ))
Console.WriteLine(s);
Now, as far I remember Linq to XML returns the elements at the same order that are in your document and you said there is the same amount of departments and managers, so, you could also iterate for one of the results and use the index:
var departments=xdoc.Descendants("Department").ToList();
var managers=xdoc.Descendants("Manager").ToList();
for(int i=0;i<departments.Count;i++)
{
Console.WriteLine("Department Name - " + departments[i].Value);
Console.WriteLine("Manager Name - " + managers[i].Value);
}
Related
I'm implementing paging using CamlQuery and ListItemCollection, sorting results by the name field. I would like folders to come first, as they do in UI, e.g. "folder A2, folder B2, file A1, file B1", but instead I get "file A1, folder A2, file B1, folder B2".
What is the best way to accomplish such sorting and paging? Note that for paging I have to specify the value of the sorting field which will be the first record of the page – I've considered adding two sorting fields into CAML, but I'm not sure whether I can use two fields in ListItemCollectionPosition.PagingInfo.
The code I'm currently using is like this:
var queryText = #"
<View>
<Query>
<OrderBy Override='TRUE'>
<FieldRef Name='FileLeafRef' Ascending='True' />
</OrderBy>
</Query>
...
<RowLimit>
10
</RowLimit>
</View>";
var camlQuery = new CamlQuery();
camlQuery.ViewXml = queryText;
camlQuery.ListItemCollectionPosition = new ListItemCollectionPosition
{
PagingInfo = "Paged=TRUE&p_ID=1002&p_FileLeafRef=A1"
};
var items = list.GetItems(camlQuery);
For getting results sorted by object type you could utilize FSObjType property, for example the following CAML expressions tells server to return folder items first and then file items:
<OrderBy Override='TRUE'>
<FieldRef Name='FSObjType' Ascending='False' />
</OrderBy>
Regarding ListItemCollectionPosition.PagingInfo property, the following expression tells to return items that come after the item with ID specified via p_ID parameter and sorted by object type:
var camlQuery = new CamlQuery();
camlQuery.ListItemCollectionPosition = new ListItemCollectionPosition
{
PagingInfo = "Paged=TRUE&p_FSObjType=1&p_ID=200"
};
Example
The following example returns 200 items:
with with ID starting from 100
and sorted by object type (folder items comes first)
Code:
var itemsCount = 200;
var startItemId = 100;
var queryText = #"
<View>
<Query>
<OrderBy Override='TRUE'>
<FieldRef Name='FSObjType' Ascending='False' />
</OrderBy>
</Query>
<RowLimit>
{0}
</RowLimit>
</View>";
var camlQuery = new CamlQuery
{
ViewXml = string.Format(queryText, itemsCount),
ListItemCollectionPosition = new ListItemCollectionPosition
{
PagingInfo = $"Paged=TRUE&p_FSObjType=1&p_ID={startItemId - 1}"
}
};
var items = list.GetItems(camlQuery);
ctx.Load(items);
ctx.ExecuteQuery();
Update
The example of PagingInfo expression for items to be sorted by type first and then by name:
Paged=TRUE&p_FSObjType=1&p_FileLeafRef=B2&p_ID=100
Ok, I'm really struggling with finding a good example of what I need to do. So, I'll ask here.
Let's say I have a entity class (EF) named Customer and a corresponding view-model class named CustomerViewModel.
Using AutoMapper, I have created the following mappings:
Mapper.CreateMap<CustomerViewModel, Customer>();
Mapper.CreateMap<Customer, CustomerViewModel>();
How would I modify the following code to make use of this mapping?
public static List<CustomerViewModel> GetCustomers()
{
using (var context = new GCSBaseEntities())
{
var query = from c in context.Customers
select new CustomerViewModel
{
CompanyName = c.CompanyName,
Id = c.Id,
EmailAddress = c.EmailAddress,
FirstName = c.FirstName,
LastName = c.LastName,
MiddleName = c.MiddleName,
ModifiedDate = c.ModifiedDate,
Phone = c.Phone,
SalesPerson = c.SalesPerson,
Suffix = c.Suffix,
Title = c.Title,
FullName = c.FirstName + " " + c.LastName
};
return query.ToList();
}
}
Thanks in advance.
When you register your mappings, you must provide any complex mapping operations that have to occur. In your case, I believe all your properties match up, except for FullName = c.FirstName + " " + c.LastName. Here's how your Customer-to-CustomerViewModel mapping should look:
Mapper.CreateMap<Customer, CustomerViewModel>()
.ForMember(custVm => custVm.FullName,
mapper => mapper.MapFrom(cust => cust.FirstName + " " + cust.LastName));
You'll have to figure out how to shove the FullName prop from the ViewModel back into the FirstName & LastName fields on the EF class, though. But when you decide how to implement it, follow the pattern from above for the other mapping.
Your query can now be MUUUCH smaller:
using (var context = new GCSBaseEntities())
{
return from c in context.Customers
select Mapper.Map<CustomerViewModel>(c);
}
Figured it out. In order to avoid the aforementioned error, you have to Add the call the .AsEnumerable() after Customers like so:
return from c in context.Customers.AsEnumerable()
select Mapper.Map<CustomerViewModel>(c);
I got this from this thread: LINQ and AutoMapper
I have a fairly simple MVC webapp connected to a database. The user needs a search page that will pull certain records from a single table. The user can give 1 or more keywords. The search function must search for records containing those keywords in 3 different columns: title, description or poc.
I've got the following setup but its incorrect. It is providing records containing any of the keywords. The results must instead be records containing all keywords. Also, I'm not sure if this is the best way of writing a search function...
// searchString contains all keywords delimited by spaces
string[] keywordArray = Regex.Split(searchString, "\\s");
var model = new List<MyTable>();
foreach (string word in keywordArray)
{
foreach (var record in myTableRepository.MyTable.Where(x => x.title.ToLower().Contains(word.ToLower()) || (x.description != null && x.description.ToLower().Contains(word.ToLower())) || (x.poc != null && x.poc.ToLower().Contains(word.ToLower()))).ToList())
{
model.Add(new MyTable
{
id = record.id,
title = record.title,
description = record.description,
poc = record.poc
});
}
}
return View(model);
For example, if the user gave the following search criteria "john test phase", then the results could look like this:
title description poc
Lorem Ipsum Test A Phase A lorem ipsum john doe
Lorem Ipsum phase This is john test for jack jane doe
Lorem Ipsum John test for jim clark phase
etc..
Thanks in advance for advice and tips!
It might be better to search one record at a time and check for all the keywords within one record. Perhaps you could collect all the words associated with a particular entry in one variable and then search that for all the keywords.
// searchString contains all keywords delimited by spaces
string[] keywordArray = Regex.Split(searchString, "\\s");
var model = new List<MyTable>();
Boolean searchResult;
foreach (var record in myTableRepository.MyTable)
{
searchResult = true;
var recordTerms = record.title + " " + record.description + " " + record.poc;
recordTerms = recordTerms.toLower();
recordArray = Regex.Split(recordTerms, "\\s");
foreach (var word in keywordArray)
{
if (!recordArray.asList.contains(word))
searchResult = false;
}
}
if (searchResult) {
model.Add(new MyTable
{
id = record.id,
title = record.title,
description = record.description,
poc = record.poc
});
}
}
return View(model);
this is my code:
ViewBag.idprofesor = new SelectList(db.Profesor, "IDProfesor", "nombre");
this code generate a dropdown that only shows the name (nombre) of the teachers (profesor) in the database, id like the dropdown to show the name and the lastname of the teacher.
You may have to manually create a ist of SelectListItems that manually specify the fields you want. Like so:
List<SelectListItem> professors = new List<SelectListItem>();
foreach(var professor in db.Professors) {
professors.Add( new SelectListItem() { Text = professor.Fn + " " + professor.Ln, Value = professor.Id } );
}
ViewVag.Professors = professors;
Then in your view:
Html.DropDownListFor( m => m.Professor, ViewBag.Professors );
You just have to pass an IEnumerable of SelectListItem to the constructor. Using Linq to select from the Professor collection:
#{
var mySelectList =
db.Professor.Select(prof => new SelectListItem(
Text = prof.lastName + ", " + prof.nombre,
Value = prof.IDProfessor
)
).AsEnumerable();
}
Using Razor, you should be able to create the drop down list like this:
#Html.DropDownList("mydropdownlist", mySelectList)
To bind it to a property in your model, the syntax is slightly different:
#Html.DropDownListFor(model => model.someproperty, mySelectList)
In my code I have the following Linq Query:
IQueryable<Data> charts = (from report in ctx.Charts group report by new
{
Name = report.ChartTitle.ChartType.ChartCategory.CategoryName,
id = report.ChartTitle.ChartType.ChartCategory.ChartCategoryId,
Period = report.Period
} into d
select new Data
{
Name = d.Key.Name,
Id = d.Key.id,
Period = d.Key.Period,
Reports = from r in d group r by new
{ Title = r.ChartTitle.Name, id = r.ChartTitle.ChartTitleId } into rs
select new Report
{
Title = rs.Key.Title,
Id = rs.Key.id,
Charts = (from c in rs group c by new
{
ChartId = c.ChartId,
FiscalYear = c.FiscalYear,
ModifiedDate = c.ChartView.ModifiedDate,
Function = c.Function.DisplayName,
ChartData=c.ChartView.ViewData
} into cs
select new ChartInfo
{
ChartId = cs.Key.ChartId,
FiscalYear = cs.Key.FiscalYear,
ModifiedDate = cs.Key.ModifiedDate,
Function = cs.Key.Function,
ChartData=cs.Key.ChartData
})
}});
In the above code if I exclude the "ChartData" field (which is of XML datatype), the query executes fine. But when ever I include this field it throws the following error :"The xml data type cannot be selected as DISTINCT because it is not comparable."
Let me know what I am missing here?
You can't group by XML types. This is a SQL restriction, not a LINQ-to-SQL retriction. (See Group by on XML column field with LINQ and select an xml type column in select query with group by SQL Server 2008)
Do you need to group by the XML column? The alternative would be to group by your other columns and then select the first XML value as a result.
Charts = (from c in rs group c by new
{
ChartId = c.ChartId,
FiscalYear = c.FiscalYear,
ModifiedDate = c.ChartView.ModifiedDate,
Function = c.Function.DisplayName,
} into cs
select new ChartInfo
{
ChartId = cs.Key.ChartId,
FiscalYear = cs.Key.FiscalYear,
ModifiedDate = cs.Key.ModifiedDate,
Function = cs.Key.Function,
ChartData=cs.Value.FirstOrDefault().ChartData
})
When using LINQ-to-SQL the items being grouped are still accessible - you don't need to include every 'selected' property / column in the group by` clause like you would in SQL.
You did not tell us what is the actual datatype of the ChartData, but from the error you are describing it looks like the problem is that whatever this datatype is it does not implement the IComparable interface which is a required interface if you want instances of the datatype to be comparable