Query for selecting and anonymous object with where clause - c#

This is my code:
var tree = new
{
id = "0",
item = new List<object>()
};
foreach ()
{
tree.item.Add(new
{
id = my_id,
text = my_name,
parent = my_par
});
}
But I want to replace the code in the foreach with the following:
foreach ()
{
tree.item.Where(x => x.id == 2).First().Add(new
{
id = my_id,
text = my_name,
parent = my_par
});
}
How to do this? I get exception that the type doesn't contain a definition for id.
The problem here is the anonymous type.
I tried creating a new class which would have 2 properties: id, text and parent and the syntax worked, but the tree's definition was invalid.
So the question here is how to make a query to an anonymous type, without adding a new class which would represent the anonymous type.

If you want to do it without creating a new class you can use dynamic for the filtering.
tree.item.Where(x => ((dynamic)x).id == 2).First()....
Although that will give you a single anonymous object and not a collection so you can not add anything to it.

One, this is really ugly. You should think of declaring a class for this (you got a downvote from some purist for this I assume ;))
Two, you're doing something that's impossible. Think about this, in your first loop, when you do tree.item.Where(x => x.id == 2).First(), you're getting x back, which is an object and object doesn't have an Add method. To illustrate, take this example:
var tree = new
{
id = "0",
item = new List<object>
{
new
{
id = 2,
text = "",
parent = null
}
}
};
Now when you do
var p = tree.item.Where(x => x.id == 2).First(); //even if that was compilable.
you are getting this
new
{
id = 2,
text = "",
parent = null
}
back. Now how are you going to Add something to that? It really is an anonymous type with no method on it.
I can only assume, but you might want this:
var treeCollection = new
{
id = 0,
item = new List<object> // adding a sample value
{
new // a sample set
{
id = 2,
text = "",
parent = null // some object
}
}
}.Yield(); // an example to make it a collection. I assume it should be a collection
foreach (var tree in treeCollection)
{
if (tree.id == 0)
tree.item.Add(new
{
id = 1,
text = "",
parent = null
});
}
public static IEnumerable<T> Yield<T>(this T item)
{
yield return item;
}
Or in one line:
treeCollection.Where(x => x.id == 0).First().item.Add(new
{
id = 1,
text = "",
parent = null
});

Related

Some values in LINQ Query Statement aren't saved correctly to class with subsonic 3

I am developing a MVC 3 Application which uses Subsonic 3 for accessing the database.
My Problem is, i don't understand why the Enum "GlobalType" is not being written into the property.
Everytime i check, the value is 0 instead of "One".
The "Name" property contains the "DateCreated" value.
The "DateCreated" property contains a new DateTime instance.
No other fields, as far as i'm aware of, are doing this.
There is no logic inside of the ViewItemModel, it's just a class with properties.
If i add them after this method manually, everything works.
Maybe someone encountered something similar with subsonic (if it even is subsonic itself, maybe i'm making a mistake)?
I have this method in the Backend:
public IEnumerable<ViewItemModel> LoadView(int registratorId)
{
var itemModel = from item in _itemQuery
join header in _headerQuery on item.HeaderID equals header.ID
where header.RegistratorID == registratorId && !(from hidden in _headerHiddenQuery where hidden.ItemID == item.ID && hidden.Type == GlobalType.One && hidden.RegistratorID == registratorId select hidden.ID).Any()
orderby item.ID descending
select new ViewItemModel()
{
Type = GlobalType.One,
ID = item.ID,
Name = header.Name,
DateCreated = header.DateCreated,
TypeOfTransport = header.TypeOfTransport,
TransportType = item.TransportType,
Count = (from subItems in _subItemQuery where subItems.ItemID == item.ID select subItems.ID).Count(),
// For Status
IsArchived = header.IsArchived,
IsCanceled = header.IsCanceled,
Process = header.Process,
End = header.End,
IsPublished = header.IsPublished,
OpenFrom = header.OpenFrom,
OpenTill = header.OpenTill,
IsNextStarted = header.IsNextStarted
};
return itemModel.ToList();
}
Update:
The GlobalType enum looks like this
public enum GlobalType
{
One = 1,
Two = 2,
Individual = 3
}
If i add them manually, i changed the return statement for this:
var result = itemModel.ToList();
foreach (var item in result)
{
var headerId = _itemQuery.Where(it => it.ID == item.ID).Select(it => it.HeaderID).FirstOrDefault();
var created = _itemQuery.Where(it => it.ID == item.ID).Select(it => it.DateCreated).FirstOrDefault();
var name = _headerQuery.Where(it => it.ID == headerId).Select(it => it.Name).FirstOrDefault();
item.AnnouncementType = GlobalType.One;
item.Name = name;
item.DateCreated = created;
}
return result;
try sample code:
public int enum GlobalType
{
One = 1,
Two = 2,
Individual = 3
}
//enum value Convert to int or other data type using casting
item.AnnouncementType = (int) GlobalType.One;
//Suppose if condition using
if((GlobalType)item.AnnouncementType==GlobalType.One)
{
//your code
}
Thanks to DaveParsons comment, i managed to create a workaround.
In this case, the code will have to iterate twice through the list of found elements, but won't load the entire table into memory.
Since there is a bug (throwing exception) with creating an anonymous object containing multiple classes like so:
select new { item, header, subItems }
I managed to get all the data needed, by manually assigning what i need like so:
public IEnumerable<ViewItemModel> LoadView(int registratorId)
{
var itemModel = from item in _itemQuery
join header in _headerQuery on item.AnnouncementHeaderID equals header.ID
where header.RegistratorID == registratorId && !(from hidden in _headerHiddenQuery where hidden.ItemID == item.ID && hidden.Type == GlobalType.One && hidden.RegistratorID == registratorId select hidden.ID).Any()
orderby item.ID descending
select new {
Type = GlobalType.One,
ID = item.ID,
Name = header.Name,
DateCreated = header.DateCreated,
TypeOfTransport = header.TypeOfTransport,
TransportType = item.TransportType,
Count = (from subItems in _subItemQuery where subItems.ItemID == item.ID select subItems.ID).Count(),
// For Status
IsArchived = header.IsArchived,
IsCanceled = header.IsCanceled,
Process = header.Process,
End = header.End,
IsPublished = header.IsPublished,
OpenFrom = header.OpenFrom,
OpenTill = header.OpenTill,
IsNextStarted = header.IsNextStarted
};
return itemModel
.ToList()
.Select(it => new ViewItemModel() {
Type = it.Type,
ID = it.ID,
Name = it.Name,
DateCreated = it.DateCreated,
TypeOfTransport = it.TypeOfTransport,
TransportType = it.TransportType,
Count = it.Count,
// For Status
IsArchived = it.IsArchived,
IsCanceled = it.IsCanceled,
Process = it.Process,
End = it.End,
IsPublished = it.IsPublished,
OpenFrom = it.OpenFrom,
OpenTill = it.OpenTill,
IsNextStarted = it.IsNextStarted
})
.ToList();
}
Notice: The return value of the query is an anonymous object with every single necessary field declared.
After the database returned all fields with the same name as in the database (model), we then have to force execution with ".ToList()" or something similar (deferred execution?).
Since the data is now in memory, we can assign the values from the anonymous object to the original class that was intended for this purpose.
I am sure there is a more reliable way using reflection, but this is what i have come up with.

Add/Update IEnumerable List

I just want to ask how can I pass/update an IEnumerable List that will be used to show in dropdown cause I am missing a set of lines to add it and update enumlist so that I can only get the Text and Value that is Arrived and Completed. Here is my Enumerable list in my Model
public enum DeliveryPermitStatus
{
Arrived = 1,
Approved = 2,
Cancelled = 3,
Completed = 4,
Submitted = 5
}
Code in my Controller so I can add the filtered enumlist and I am missing code to viewbag the updated enumlist that will be used to show in dropdown
var enumlist = Enum.GetValues(typeof(DeliveryPermitStatus)).Cast<DeliveryPermitStatus>().Select(v => new SelectListItem
{
Text = v.ToString(),
Value = ((int)v).ToString()
});
if (User.IsInRole(StaticRoleNames.Admin)) //your condition here
{
foreach(var item in enumlist)
{
if(item.Text == "Arrived" || item.Text == "Completed")
{
//Missing Code Here
}
}
}
ViewBag.enumlist = enumlist;
I think what you are asking/wanting to do is edit the ViewBag.enumlist to be a reduced set of values based on a user's role? If so, then you could filter and assign the list inside your if statement and forego the foreach loop:
var enumlist = Enum.GetValues(typeof(DeliveryPermitStatus)).Cast<DeliveryPermitStatus>().Select(v => new SelectListItem
{
Text = v.ToString(),
Value = ((int)v).ToString()
});
if (User.IsInRole(StaticRoleNames.Admin)) //your condition here
{
ViewBag.enumlist = enumlist.Where(t => t.Text == "Arrived" || t.Text == "Completed");
}
OR, if you need to keep the foreach loop, then you'll need to create a separate list to keep track of the "available values" for the enum list:
var enumlist = Enum.GetValues(typeof(DeliveryPermitStatus)).Cast<DeliveryPermitStatus>().Select(v => new SelectListItem
{
Text = v.ToString(),
Value = ((int)v).ToString()
});
List<SelectListItem> availableOptions = new List<SelectListItem>();
if (User.IsInRole(StaticRoleNames.Admin)) //your condition here
{
foreach(var item in enumlist)
{
if(item.Text == "Arrived" || item.Text == "Completed")
{
availableOptions.Add(item);
}
}
}
ViewBag.enumlist = availableOptions;
You can't add elements to an IEnumerable, but you can easily get a List from it.
Just call ToList() before assign to your var enumlist.
Now you can add elements to it (be careful, don't add elements while enumerating it)

how to declare anonymous type using a variable's value as key in c#?

Is there anyway to succeed this sample?
string[] columns = ["a","b","c","d"];
var headers = from column in columns
select new
{
title = column,
filter = new { column = "select" },
show = true
};
After debug this code block, I see header.filter.column property.
How can I see header.filter.a ?
This is not possible, since anynonymous types are anonymous, but they still exist at compile time. So you can not create an anonymous class with runtime member names.
What you are actually doing is to set a property named column with the value "select".
What you can do though is to create a dictionary from your result and access the data with the dictionary:
var headerFilterDictionary = headers.ToDictionary(item => item.title, item => item.filter.column);
var columnFilterValue = headerFilterDictionary["a"];
This probably isn't what you really want to do (at all), but you can make the syntax (mostly) work by using dynamic:
static void Main(string[] args)
{
string[] columns = { "a", "b", "c", "d" };
var headers = from column in columns
let c_a = (dynamic)(column == "a" ? new { a = column } : null)
let c_b = (dynamic)(column == "b" ? new { b = column } : null)
let c_c = (dynamic)(column == "c" ? new { c = column } : null)
let c_d = (dynamic)(column == "d" ? new { d = column } : null)
select new
{
title = column,
filter = c_a ?? c_b ?? c_c ?? c_d,
show = true
};
var filter_0 = headers.ElementAt(0).filter.a;
var filter_1 = headers.ElementAt(1).filter.b;
var filter_2 = headers.ElementAt(2).filter.c;
var filter_3 = headers.ElementAt(3).filter.d;
}
Obviously, this doesn't scale very well; and since it's dynamic, you don't get IntelliSense on filter.
This is probably best considered for entertainment purposes only.

How to update a single item of liist of objects using LINQ in C#

I want like to update the Value of the list which has property Text="ALL".
public class Season
{
public string Text {get;set;}
public string Value {get;set;}
public bool ValueSelected {get;set;}
}
The 'Q' in LINQ stands for "Query". LINQ is not meant to update objects.
You can use LINQ to find the object you want to update and then update it "traditionally".
var toUpdate = _seasons.Single(x => x.Text == "ALL");
toUpdate.ValueSelected = true;
This code assumes that there is exactly one entry with Text == "ALL". This code will throw an exception if there is none or if there are multiple.
If there is either none or one, use SingleOrDefault:
var toUpdate = _seasons.SingleOrDefault(x => x.Text == "ALL");
if(toUpdate != null)
toUpdate.ValueSelected = true;
If it's possible that there are multiple, use Where:
var toUpdate = _seasons.Where(x => x.Text == "ALL");
foreach(var item in toUpdate)
item.ValueSelected = true;
You could use something like this:
// Initialize test list.
List<Season> seasons = new List<Season>();
seasons.Add(new Season()
{
Text = "All"
});
seasons.Add(new Season()
{
Text = "1"
});
seasons.Add(new Season()
{
Text = "2"
});
seasons.Add(new Season()
{
Text = "All"
});
// Get all season with Text set to "All".
List<Season> allSeasons = seasons.Where(se => se.Text == "All").ToList();
// Change all values of the selected seasons to "Changed".
allSeasons.ForEach(se => se.Value = "Changed");

Iterating and creating new anonymous types dynamically

I have an anonymous type of this form:
new List<MyList>()
{
new Column { Name = "blah", Width = 100, Hidden = true },
new Column { Name = "blah1", Width = 60, Hidden = false }
}
How can I go about creating the content within the list dynamically, like:
new List<MyList>()
{
foreach (var columns in col)
{
new Column { Name = columns.Name ... }
}
}
Even with col returning the right sort of data, the above example isn't acceptable and I can't see why.
You try to loop over the collection inside the object initializer block (thx to Luke).
Try creating the list first and than filling it,
var list = new List<MyList>();
foreach (var columns in col)
{
list.Add(new Column { Name = columns.Name ... });
}
Not sure exactly what you're asking, but have you tried something of the form:
col.Select(c => new Column {Name = c.Name ... etc}).ToList();
maybe something like
var theList = new List<MyList>();
col.ForEach(c=> theList.Add( new Column(){ Name=c.Name ... etc } ));

Categories