I have a linq query where I am creating several classes which have a Parent property on them. I am looking for a way to set the parent property to be the class I just created. My explanation sucks; here's code of what I'm trying to do.
var query = from states in xml.Elements()
select new State
{
Children = from cities in states.Elements()
select new City()
{
ParentState = **???**;
}
};
How do I set the ParentState property? If I could do something like
select new State as newState
{
...
}
that would be cool, but I can't. I know I can do this with a foreach loop, but I would like to learn how, if possible, to do this with LINQ. Help :(
EDIT: I tried
let x = new State{ } but that didn't help much. I was hoping I could refer to x in the constructor like this but it didn't work out:
let x = new State
{
Children = from cities in states.Elements()
select new City{ ParentState = x }
}
select x;
In F#, there is something similar to this where you can simply say let rec x = ...
and then you can refer to the variable inside of the assignment statement. But this is C# not F# so whatever.
Interesting problem, and there may certainly be a way to do it by setting the properties right in the query statement, but I think another possibility is to move some of that logic to a constructor within State. Consider the following code example
class State
{
public int Id { get; set; }
public List<City> Children { get; set; }
public State(int id, IEnumerable<City> childrenCities)
{
this.Id = id;
this.Children = childrenCities.ToList();
foreach (City city in this.Children)
city.ParentState = this;
}
}
This State class has a constructor which accepts an enumerable of City objects. It then loops over the objects and sets the ParentState property.
And then instead of setting the properties with the query expression, you instead invoke the constructor.
// not LINQ-to-XML, just an example
var states = from i in Enumerable.Range(0, 10)
select new State(
i,
from j in Enumerable.Range(0, 5)
select new City
{
Id = j
}
);
Hey, I think this is what you need
var query = from states in xml.Elements()
select new State
{
Children = from cities in states.Elements()
select new City()
{
ParentState = new State{
Property1 = states.XElement("Property1").Value
}
}
};
the variable states is the current state. I presume the "states" var is an XElement and holds the data to populate the parentstate property
Hope this helps
Related
I want to get an property value from an object that is in a list and put it into textbox.text
Below i have an example of my code:
The object:
public class Incident
{
public int Incident_id { get; set; }
public string Description { get; set; }
public string Caller { get; set; }
}
Below is my code in my form class:
List<Incident> incidentById = new List<Incident>();
incidentById = db.GetIncidentById(ID);
when my list is filled i want to put the string Caller into an textbox somewhat like below:
textBoxCaller.Text = incidentById[1].Caller;
I'm stuck at this point so i hope someone can help me out.
Thanks!
EDIT:
public List<Incident> GetIncidentById(int id)
{
using (IDbConnection connection = new System.Data.SqlClient.SqlConnection(Helper.CnnVal("IncidentLog")))
{
var output = connection.Query<Incident> ($"select * from Incidents where Incident_id like #id", new { id = "%id%" }).ToList();
return output;
}
}
I wasn't passing the right value into my query
this did the trick!
What you want is $"select * from Incidents where Incident_id = #id", new { id }
do you want first value should go?
check like.
if(incidentById.Count>0)
{
textBoxCaller.Text = incidentById.First().Caller;
}
// or you can join all Caller in list like
if(incidentById.Count>0)
{
textBoxCaller.Text = string.join(",",incidentById.Select(x=>x.Caller));
}
The issue that you are facing is that you are trying to access the second element in the list when there are not two or more elements in the list. If you are trying to access the first element in the list, then you could do either
textBoxCaller.Text = incidentById[0].Caller;
or
textBoxCaller.Text = incidentById.First().Caller;
If you do in fact want the second element of the list, then you should be checking to verify that it's length is at least two:
if(incidentById.Count >= 2)
{
...
}
Finally, as mentioned in a comment, you should rename GetIncidentById to something that makes clear it is returning a list, like GetIncidentsById or something similar.
In this code, I am trying to check what rooms are available at the time that has been entered by the user. To test whether the room is free, I change the roomname to all the options and do a clash check. The problem is, is when I change the room name, it also updates it on my list of existingBookings. Is there a way to make sure that the existingBookings do not change?
var existingBookings = _context.Bookings.ToList();
var booking = await _context.Bookings.SingleOrDefaultAsync(m => m.BookingID == id);
List<Room> roomlist = new List<Room>();
roomlist = (from product in _context.Room
select product).ToList();
List<Room> availableRooms = new List<Room>();
foreach (var item in roomlist)
{
booking.RoomName = item.RoomName;
if (BusinessLogic.BookingChecker.DoesBookingClash(booking, existingBookings) == null)
{
availableRooms.Insert(0, new Room { RoomID = item.RoomID, RoomName = item.RoomName });
}
booking.RoomName = "UNDEF";
}
Thanks for your help!
In order to do so you have to create a new instance of the Booking. You can call the constructor and copy the necessary properties, or implement a ICloneable.Clone method.
public class Booking : ICloneable
{
public Booking Clone()
{
return new Booking() { RoomID = this.RoomID, etc. };
}
object ICloneable.Clone()
{
return this.Clone();
}
}
Then you call it:
booking = booking.Clone();
// use the new booking instance.
The way I fixed this, was to add a new bool field called proposed into the Booking.cs file. When creating the proposed booking, it will automatically set it to true. In my DoesRoomBookingClash I added an if statement to ignore a database entry if proposed was true. Once all the changes are made, proposed is set to false.
I am trying to populate a list of a custom class using the AddRange method.
Here is the custom class:
public class Hook
{
public Hook(System.Data.DataRow values)
{
this.text = values[0].ToString();
this.category = values[1].ToString();
this.weather = values[2].ToString();
this.timeofday = values[3].ToString();
}
public string text { get; set; }
public string category { get; set; }
public string[] associatedLandmarks { get; set; }
public string weather { get; set; }
public string timeofday { get; set; }
}
Here is my object array that I need to filter objects from, I am populating it using values from a datatable:
int numberofhooks = result.Tables[1].Rows.Count - 1;
Hook[] Hooks = new Hook[numberofhooks];
for (int i = 0; i < numberofhooks; i++)
{
Hooks[i] = new Hook(result.Tables[1].Rows[i]);
}
I need to filter out a subset of these objects using a variable that gets updated on the fly, which is a property of the class.
'category' is the property
I am using the AddRange method to do this, but it isn't working. What can be the issue with this? Or is there another way to accomplish this?
List<Hook> RelevantHooks = new List<Hook>();
foreach (string category in relevantHookCategories)
{
RelevantHooks.AddRange(from item in Hooks
where item.category == category
select item);
}
Thanks
I see two problems here:
If you want all the rows from your table why are you using:
int numberofhooks = result.Tables[1].Rows.Count - 1;
It should be:
int numberOfHooks = result.Tables[1].Rows.Count;
Perhaps the last item that you are excluding has the category you are looking for.
When you say "on the fly" I'm assuming that you want to have the subset RelevantHooks updated even after your query, when modifying Hooks, if that's the case then you don't have to use a List, just define your relevantHooks as:
IEnumerable<Hook> relevantHooks = Hooks.Where(h => relevantHookCategories.Contains(h.category));
e.g. If you modify the category property for any of Hooks items:
Hooks[0].category="Other Category Value";
Will either exclude or include the first hook from relevantHooks
RelevantHooks.AddRange(Hooks.Where(hook => category == hook.category));
should be enough. You don't necessarily need the select clause in this case.
No need to use AddRange. Just iterate Hooks once and check if it's one of the categories.
RelevantHooks = Hooks.Where(h => relevantHookCategories.Contains(h.category)).ToList();
Try this:
RelevantHooks.AddRange((from item in Hooks
where item.category == category
select item).ToArray());
I don't think it should matter, but it could be the AddRange method is failing because it's being passed IQueryable when it is expecting IEnumerable
Given my last Comment, here's what I meant:
for (int i = 0; i < numberofhooks; i++)
{
Hooks[i] = new Hook(result.Tables[1].Rows[i]);
if(relevantHookCategories.Contains(Hooks[i].category)
RelevantHooks.Add(Hook[i]);
}
I'm trying to convert a List<Topic> to an anonymous or dynamic type via linq projection... I'm am using the following code, but it doesn't seem to work properly. It returns the dynamic type without error, however, if I try to enumerate the children field (list<object/topic>) then it says
Results View = The type '<>f__AnonymousType6<id,title,children>' exists in both 'MyWebCore.dll' and 'MvcExtensions.dll'
Strange.
Here is the code I am using:
protected dynamic FlattenTopics()
{
Func<List<Topic>, object> _Flatten = null; // satisfy recursion re-use
_Flatten = (topList) =>
{
if (topList == null) return null;
var projection = from tops in topList
select new
{
id = tops.Id,
title = tops.Name,
children = _Flatten(childs.Children.ToList<Topic>())
};
dynamic transformed = projection;
return transformed;
};
var topics = from tops in Repository.Query<Topic>().ToList()
select new
{
id = tops.Id,
title = tops.Name,
children = _Flatten(tops.Children.ToList<Topic>())
};
return topics;
}
All i'm doing is flattening a list of self containing objects - basically it's a list of POCOs that will be stuffed into a tree view (jstree).
The Topic class is defined as:
public class Topic
{
public Guid Id {get;set;}
public string Name {get;set;}
public List<Topic> Children {get;set;}
}
And here is an example of what the first member of the returned dynamic object looks like:
[0] = {
id = {566697be-b336-42bc-9549-9feb0022f348},
title = "AUTO",
children = {System.Linq.Enumerable.SelectManyIterator
<MyWeb.Models.Topic,
MyWeb.Models.Topic,
<>f__AnonymousType6<System.Guid,string,object>
>}
}
Why do you have the same LINQ code twice? After you define your _Flatten func, you can just call it immediately - var topics = _Flatten(Repository.Query<Topic>().ToList().
It looks like you're creating two identical anonymous types, one inside the _Flatten func and one outside it. I would think the compiler is smart enough to handle that, but try changing your call to explicitly use _Flatten, see if it solves the problem.
Here is the proper way - have to load into the a DTO / POCO and return that:
_Flatten = (topList) =>
{
if (topList == null) return null;
var projection = from tops in topList
//from childs in tops.Children
select new JsTreeJsonNode
{
//id = tops.Id.ToString(),
data = tops.Name,
attr = setAttributes(tops.Id.ToString(), tops.URI),
state = "closed",
children = _Flatten(tops.Children)
};
return projection.ToList();
};
I have a constructor something like the following:
using Microsoft.Data.Extensions;
public class Complaint
{
public int Id {get; set;}
public int Transcript {get; set;}
//... etc. ... Lots more properties
public Complaint(int id)
{
var command = dataContext.CreateStoreCommand(
"dbo.stp_Complaint_Get",
CommandType.StoredProcedure,
new SqlParameter("Id", id));
var complaint = command.Materialize(x =>
new Complaint
{
Id = x.Field<int>("Id"),
Transcript = x.Field<string>("Transcript");
//... etc. ... Lots more fields from db
}
this.Id = complaint.Id;
this.Transcript = complaint.Transcript;
//... etc. ... Lots more properties to set
}
}
Is there a syntax in C# that would allow me to carry out the last part in one step instead of two? i.e. conceptually something like this:
this = command.Materialize(x =>
new Complaint
{
Id = x.Field<int>("Id"),
Transcript = x.Field<string>("Transcript");
}
Well, you could use a static method instead of a constructor:
public static Complaint FromId(int id)
{
var command = dataContext.CreateStoreCommand(
"dbo.stp_Complaint_Get",
CommandType.StoredProcedure,
new SqlParameter("Id", id));
return command.Materialize(x =>
new Complaint
{
Id = x.Field<int>("Id"),
Transcript = x.Field<string>("Transcript");
//... etc. ... Lots more fields from db
});
}
Not inherently. You could store the complaint object inside the class, and have all the properties point off that rather than setting them all from the constructor.
eg
public class Complaint
{
private readonly {type of complaint} m_Complaint;
public int Id
{
get { return m_Complaint.Id; }
}
// ...etc
}
You could get more complicated if you don't have a setter on m_Complaint - keeping nullable backing fields, and check that before you access the m_Complaint properties
I believe you may try something like this:
var currentInstance = this;
var complaint = command.Materialize(x =>
new Complaint
{
Id = currentInstance.Id = x.Field("Id"),
Transcript = currentInstance.Transcript = x.Field("Transcript");
//... etc. ... Lots more fields from db
});
I don't think you can capture this in the closure, so using that currentInstance trick should help, however it may as well turn out to be redundant.
Besides, code such as that one is sort of obfuscated compared to your current solution. I believe that your code is pretty fine as it is.
I gather that you're trying to avoid setting all of those properties twice, because any change to the structure requires you to update the properties in two places, is that correct?. You could try to use some reflection to copy the properties from one instance to another.
var complaint = command.Materialize(x =>
new Complaint
{
Id = x.Field<int>("Id"),
Transcript = x.Field<string>("Transcript");
//... etc. ... Lots more fields from db
}
foreach (var prop in typeof(Complaint).GetProperties())
...
Have the complaint object as a member variable and the get/set properties accesses the underlying complaint's properties?