Have a list of objects with the object structure as following
public class Schedule
{
public int ID { get; set; }
public string Name { get; set; }
public Schedule() {}
}
Executing a linq query on data I can see properly populated list of objects
var schedule = (from d in data
select new Schedule
{
ID = d.id,
Name = ""
}).ToList();
later in code I want to change property Name depends on a condition. A few examples I found
schedule.ForEach(s => { s.Name = "Condition Name"; });
schedule.Select(s => { s.Name = "Condition name"; return s; });
after execution leave Name parameter "null" when I refresh schedule in the watch window.
Can anyone see what's wrong with this?
Looping through collection and trying to change Property doesn't change it either
foreach (var sch in schedule)
{
sch.Name = "New name";
}
schedule.ToList()[0].Name == ""
UPDATE
.ToList() call in the snippet below is important to make code work.
var schedule = (from d in data
select new Schedule
{
ID = d.id,
Name = ""
}).ToList();
Your LINQ query that assigns a value to schedule creates independent objects based on the original collection (it effectively clones the objects); changing the properties of the clones does not change the original objects.
The code works - after executing schedule.ForEach() the Name property is updated. Maybe there is something that you've left out.
LINQ is not the right tool to modify collections, it is a tool to query collections. If you want to modify it you need a loop, for example a foreach:
var schedule = data.Select(d => new Schedule{ ID = d.id }).ToList();
foreach(var s in schedule)
s.Name = "Condition Name";
If you want to "modify" a collection with LINQ you have to create a new one and assign that to your variable which is inefficient.
Below, you are create new Schedules without setting its Name.
var schedule = (from d in data
select new Schedule
{
ID = d.id
}).ToList();
To start, you may want to give the new Schedules a Name
var schedule = (from d in data
select new Schedule
{
ID = d.id,
Name = d.NameProperty /* Switch out with real name property */
}).ToList();
Related
I have this code to select the city for each person whose Id matches from the list of cities:
public List<PersonelDto> MapPersonelDto(List<Personel> personels,List<City> cities)
{
var result = new List<PersonelDto>();
foreach (var item in personels)
{
var personel = new PersonelDto
{
Id = item.Id,
Maaş = item.Salary,
MedeniHal = item.MartialStatus,
Meslek = item.Job,
Soyisim = item.LastName,
Şehir = (from s in cities where s.Id == item.CityId select s.name).ToString()
};
result.Add(personel);
}
return result;
}
But City's value come out like this:
System.Linq.Enumerable+WhereSelectListIterator`2[Personnel_Registration.City,System.String]
How can I fix this?
The error is because there's nothing in the type system metadata to guarantee you won't have more than one city match, and so the result of the expression is a potential collection of 0, 1, or more cities. What you see is the result of calling .ToString() on that collection.
To fix it, you can do this:
public IEnumerable<PersonelDto> MapPersonelDto(IEnumerable<Personel> personels, IEnumerable<City> cities)
{
return personels.Select( p => {
new PersonelDto() {
Id = item.Id,
Maaş = item.Salary,
MedeniHal = item.MartialStatus,
Meslek = item.Job,
Soyisim = item.LastName,
Şehir = string.Join(",", cities.Where(c => c.Id == p.CityId).Select(c=> c.name));
}
});
}
Or, if you're confident you only want one city, you could do this:
public IEnumerable<PersonelDto> MapPersonelDto(IEnumerable<Personel> personels, IEnumerable<City> cities)
{
return personels.Select( p => {
new PersonelDto() {
Id = item.Id,
Maaş = item.Salary,
MedeniHal = item.MartialStatus,
Meslek = item.Job,
Soyisim = item.LastName,
Şehir = cities.FirstOrDefault(c => c.Id == p.CityId)?.name;
}
});
}
Note the complete lack of Lists. You really ought to get out of the habit of using List<T> everywhere, and instead let things remain as an IEnumerable<T> as much as possible. This can make your code faster and MUCH more efficient with RAM use.
Another benefit is it makes your code more flexible. For example, you can still pass the existing Lists to this new method. And if for some reason you need the result to be a List<> for the next method call (hint: you probably don't really need this at all) you can always add a .ToList() after calling the method. But, again, don't do that unless you really have to!
from s in cities where s.Id == item.CityId select s.name
return an IEnumerable - there might be more than one city
do instead
Şehir = (from s in cities where s.Id == item.CityId select s.name).FirstOrDefault().ToString()
which selects the first element
Note that this assumes that there is always a matching city. If not then you should supply a default value
Şehir = (from s in cities where s.Id == item.CityId select s.name).FirstOrDefault("unknow city").ToString()
I would like to create an anonymous type from linq. Then change the value of a single property(status) manually and give the list to a repeater as data source. But doesn't let me do that as theay are read-only. Any suggestion?
var list = from c in db.Mesai
join s in db.MesaiTip on c.mesaiTipID equals s.ID
where c.iseAlimID == iseAlimID
select new
{
tarih = c.mesaiTarih,
mesaiTip = s.ad,
mesaiBaslangic = c.mesaiBaslangic,
mesaiBitis = c.mesaiBitis,
sure = c.sure,
condition = c.onaylandiMi,
status = c.status
};
foreach (var item in list)
{
if (item.condition==null)
{
item.status == "Not Confirmed";
}
}
rpCalisanMesai.DataSource = list.ToList();
rpCalisanMesai.DataBind();
Instead of trying to change the value after creating the list, just set the right value while creating the list.
var list = from c in db.Mesai
join s in db.MesaiTip on c.mesaiTipID equals s.ID
where c.iseAlimID == iseAlimID
select new
{
tarih = c.mesaiTarih,
mesaiTip = s.ad,
mesaiBaslangic = c.mesaiBaslangic,
mesaiBitis = c.mesaiBitis,
sure = c.sure,
condition = c.onaylandiMi,
status = c.onaylandiMi != null ? c.status : "Not Confirmed"
};
Also, if you could change the property, your problem would be executing the query twice: first in the foreach-loop, and then again by calling list.ToList() (which would create new instances of the anonymous type).
You cannot, anonymous type's properties are read-only.
You need to set it during object creation. See #Dominic answer for code sample.
You can. For instance:
var data = (from a in db.Mesai select new { ... status = new List<string>() .. }).ToList();
Next, compute your status:
foreach (var item in data) {
item.status.Add("My computed status");
}
And then on rendering:
foreach (var item data) {
Response.Write(item.status[0]);
}
EDIT: The list can even be intialized as per your requirement:
var data = (from a in db.Mesai select new { ... status = new List<string>(new
string[] { c.status }) .. }).ToList();
foreach (var item in data) {
item.status[0] = "My computed status";
}
EDIT2: Seems like you must initialize the list, preferably with e.g. c.rowid.ToString(), otherwise the optimizer assigns the same new List() to all items, thinking that this might be some game or something.
I have Two classes Named OfflineOrderLineItem.cs and OnlineOrderLineItem.cs both have diff Order table named offline and Online
In that i want to Combine the two tables data to search and Display the Fields from both tables
How to do that using linq in mvc4 ??? any idea.....
public virtual IPagedList<OnlineOrderLineItem> SearchOrderLineItems(string PoNumber)
{
var query1 = (from ol in _offlineOrderLineItemRepository.Table
select new
{
ol.Name
}).ToList();
var query2 = (from opv in _onlineOrderLineItemRepository.Table
select new
{
opv.Name
}).ToList();
var finalquery = query1.Union(query2);
if (!String.IsNullOrWhiteSpace(Name))
finalquery = finalquery.Where(c => c.Name == Name);
var orderlineitems = finalquery.ToList(); //its not working it throw a error
return new PagedList<OnlineOrderLineItem>(orderlineitems);//error
}
Error
cannot convert from 'System.Collections.Generic.List<AnonymousType#1>'
to 'System.Linq.IQueryable<Nop.Core.Domain.Management.OnlineOrderLineItem>'
to 'System.Linq.IQueryable<Nop.Core.Domain.Management.OnlineOrderLineItem>'
query1 and query2 are lists of an anonymous type with a single property of type string. (I assmume the ol.Name and opv.Name are strings.) Hence finalQuery and orderlineitems are collections of this anonymous as well. By specifying PagedList<T> you require that the collection passed into the constructor is an enumeration of type T. T is OnlineOrderLineItem, but the enumeration passed into the constructor is the anonymous type which is a different type. Result: compiler error.
To solve the problem I suggest that you define a named helper type that you can use to union the two different types OfflineOrderLineItem and OnlineOrderLineItem:
public class OrderLineItemViewModel
{
public int Id { get; set; }
public string PoNumber { get; set; }
public string Name { get; set; }
// maybe more common properties of `OfflineOrderLineItem`
// and `OnlineOrderLineItem`
}
Then your SearchOrderLineItems method should return a paged list of that helper type:
public virtual IPagedList<OrderLineItemViewModel> SearchOrderLineItems(
string PoNumber)
{
var query1 = from ol in _offlineOrderLineItemRepository.Table
select new OrderLineItemViewModel
{
Id = ol.Id,
PoNumber = ol.PoNumber,
Name = ol.Name,
// maybe more properties
};
// don't use ToList here, so that the later Union and filter
// can be executed in the database
var query2 = from opv in _onlineOrderLineItemRepository.Table
select new OrderLineItemViewModel
{
Id = opv.Id,
PoNumber = opv.PoNumber,
Name = opv.Name,
// maybe more properties
};
// don't use ToList here, so that the later Union and filter
// can be executed in the database
var finalquery = query1.Union(query2);
// again no ToList here
if (!string.IsNullOrWhiteSpace(PoNumber))
finalquery = finalquery.Where(c => c.PoNumber == PoNumber);
var orderlineitems = finalquery.ToList(); // DB query runs here
return new PagedList<OrderLineItemViewModel>(orderlineitems);
}
It is important to use ToList only at the very end of the query. Otherwise you would load the whole tables of all OnlineOrderLineItems and all OfflineOrderLineItems into memory and then filter out the items with the given PoNumber in memory which would be a big overhead and performance desaster.
Instead of
var orderlineitems = finalquery.ToList();
Try
var orderlineitems = finalquery.AsQueryable();
From https://github.com/TroyGoode/PagedList/blob/master/src/PagedList/PagedList.cs, PagedList takes a IQueryable<T>
Queryable.AsQueryable<TElement> Method
What's the equivalent LINQ instruction for a Datatable of the following SQL query:
SELECT code_direction, count(TP) AS CN
FROM table1
WHERE cod_time = 'A011'
GROUP BY TP,code_direction;
and how to get the result into a new datatable?
I tried to convert it but I there're some errors. Someone could take a look on this:
var query = from t in table1.AsEnumerable()
group t by new { t.TP, t.code_direction }
into grp
select new
{
grp.Key.code_direction,
CN = grp.Count(t.TP)
};
foreach (var x in query)
{
Console.Write(x.code_direction);
Console.Write(x.CN);
}
As far as your first question goes. The LINQ equivalent of the SQL query is:
var query = from t in table1.AsEnumerable()
where t.cod_time == "A011"
group t by new { t.TP, t.code_direction }
into grp
select new
{
grp.Key.code_direction,
CN = grp.Count()
};
Note that you don't have to pass any argument to grp.Count(). (For the obvious reason that in SQL COUNT(TP) is the same as COUNT(*), i.e. just count the number of rows. The story would be different if you'd use COUNT(DISTINCT TP) or similar.)
As far as the second question goes, if your query just returned an IEnumerable<T> where T is DataRow (i.e. a query like table1.AsEnumerable().Where(r => r.cod_time == "A011")) then you could just the DataTableExtensions.CopyToDataTable extension method. As your query returns an anonymous type however, you will have to follow these instructions found on MSDN.
I Have been using LINQ to work on a JSON object returned from a remote sharepoint web service. I have posted this because most of the answers I found online were slightly different from what I needed.
a json list of daily activities is returned from a remote sharepoint list & is then summarised using LINQ
The simplified version of a custom object definition is shown below( & which is defined in the models area of an MVC application)
public class MyCustomObjectList
{
public string eventdate { get; set; }
public string userid { get; set; }
public string action { get; set; }
}
The JSON object is serialised into a MyCustomObjectList array.
var customobject = serializer.Deserialize<MyCustomObjectList>(jsonobject);
I wanted to work out how many actions of each type happened on a given day. NB eventdate is stored as a string in format yyyy-mm-dd hh:MM:ss. This was to simplify conversions between c#, JSON & Jquery ( where required I create DateTime objects elsewhere in the code using the
eventdate.
Some will argue this is inefficient, but I prefer to split processes into a sequential set of really simple operations, for the sake of easier debugging & to help other people follow my code. Thats why there are 2 Linq queries .
querya strips out the time component from the eventdate This ensures our later grouping happens by day, & not by second. To be doubly sure that there is no caching, I create it in a new field called actionday. I also rename action to activity, because intellisense was getting confused!! The other columns are copied as is.
var querya =
from c in customobject.rows
select new { actionday = c.eventdate.Substring(0, 10), activity = c.action, c.userid,
c.eventdate };
/* queryb produces a grouped count of querya, grouped on actionday & activity, creating new columns actionkey,ActionCount,Dte,action & DetailList ( which is a summary for debugging purposes)
*/
var queryb=
from p in querya group p by new { p.actionday, p.activity} into idGroup
actionkey = idGroup.Key,
ActionCount = idGroup.Count(),
Dte = idGroup.Key.actionday,
action = idGroup.Key.activity,
DetailList = idGroup
};
Here’s a version that sumarises by 3 columns
var queryc = from p in querya
group p by new { p.actionday, p.userid, p.activity} into idGroup
select new
{
actionday = idGroup.Key,
ActionCount = idGroup.Count(),
userid = idGroup.Key.userid,
Dte = idGroup.Key.actionday,
action = idGroup.Key.activity,
DetailList = idGroup
};
I have two lists comprised of different complex-objects, and each one is from 2 separate data-sources. One list may-or-may-not contain records. When any records exist in the "optional" list I need the "normal" list to be further-filtered.
Unfortunately, I can only find very simple examples here and online, which is why I am asking this question.
The Pseudo-Logic Goes Like This:
When QuickFindMaterial records exist, get all DataSource records where query.Name is in the QuickFindMaterial.Material collection. If no QuickFindMaterial records exist do not affect the final result. Lastly, select all distinct DataSourcerecords.
The Classes Looks Like:
public class QuickFindMaterial
{
public string SiteId { get; set; }
public string Material { get; set; }
}
The Code Looks Like:
I have commented-out my failed WHERE logic below
var dataSource = DocumentCollectionService.ListQuickFind();
var quickFindMaterial = ListMaterialBySiteID(customerSiteId);
var distinct = (from query in dataSource
select new
{
ID = query.DocumentID,
Library = query.DocumentLibrary,
ModifiedDate = query.DocumentModifiedDate,
Name = query.DocumentName,
Title = query.DocumentTitle,
Type = query.DocumentType,
Url = query.DocumentUrl,
})
//.Where(x => x.Name.Contains(quickFindMaterial.SelectMany(q => q.Material)))
//.Where(x => quickFindMaterial.Contains(x.Name))
.Distinct();
I think this is what you want:
.Where(x => !quickFindMaterial.Any() || quickFindMaterial.Any(y => x.Name == y.Material))
You could join on Name -> Material
Example:
var distinct = (from query in dataSource
join foo in quickFindMaterial on query.Name equals foo.Material
select new
{
ID = query.DocumentID,
Library = query.DocumentLibrary,
ModifiedDate = query.DocumentModifiedDate,
Name = query.DocumentName,
Title = query.DocumentTitle,
Type = query.DocumentType,
Url = query.DocumentUrl,
}).Distinct();