.Where to exclude properties in reflection - linq

I have the following code as well as a string[] called excluded. I want to grab all of the properties of any class, except the ones specifically called out in excluded.
Is there a .Where that will allow my Propertyinfo[] to not contain the excluded properties?
PropertyInfo[] names = typeof(S).GetProperties();

Something like this might do the job:
PropertyInfo[] names = typeof(S).GetProperties().Where(c => !excluded.Contains(c.Name)).ToArray();

Simply:
typeof(S).GetProperties().Where(p => !excluded.Contains(p.Name)).ToArray()

Related

Duplicate parameter in web api

I have an issue regarding duplicate parameter in WebApi.
http://localhost:xxxxx/api/getbook?UserId=7 in this API Controller I have one parameter string UserId and works fine but if i do something like that
http://localhost:xxxxx/api/getbook?UserId=7?UserId=7 gets the result
So how to prevent duplicate parameter in API ?
A couple of things to note:
There would be an & character between the parameters i.e. http://localhost:xxxxx/api/getbook?UserId=7&UserId=7
The first parameter of the same name will be used by default if you are taking a model in to a controller method
If you are keen to throw some error when you discover duplicate parameter names then you could do the following:
var queryParameters = Request.GetQueryNameValuePairs()
.GroupBy(k => k.Key)
.Where(g => g.Count() > 1)
.Select(q => q.Key)
.ToList();
This will give you a List<string> of parameter names that appear more than once.
If you are interested in whether the parameter name and value combination is repeated (as in your example), then just GroupBy(k => k) instead and you will get a list of KeyValuePair to work with.

Tricky LINQ and nested collection

I am trying to figure out an easier way I can take the following code and condense it into as few lines as possible. Ideally, I would like to get an IDictionary> out of this.
var report = db.Reports.Select(rid => rid.ID == reportId) as Report;
Dictionary<string, List<string>> notes = new Dictionary<string, List<string>>();
foreach (var subreport in report.SubReports)
{
foreach (var subreportitem in subreport.SubReportItems)
{
notes[subreportitem.Title] = new List<string>();
foreach (var note in subreportitem.SubReportItemNotes)
{
notes[subreportitem.Title].Add(note.NoteDetails);
}
}
}
Ideally, I had wanted to do something like so:
from report in db.Reports
where report.ID == reportId
from subreports in report.SubReports
from subreportitems in subreports.SubReportItems
from notes in subreportitems.SubReportItemNotes
//Unsure how to select into the desired dictionary...
This should be equivalent:
db.Reports
.Where(rpt => rpt.ID == reportId)
.Cast<Report>()
.SelectMany(rpt => rpt.SubReports)
.SelectMany(subRpt => subRpt.SubReportItems)
.ToDictionary(
sri => sri.Title,
sri => sri.SubReportItemNotes.SelectMany(note => note.NoteDetails);
Notes:
The first line of your code uses .Select(rid => rid.Id == reportId), but I assumed this should have been Where instead of Select, otherwise you would end up with a collection of null because the Select results would be of type bool and the as Report would output null for each.
This only works if the Titles of all of your SubReportItems are unique. It is conceivable that one Report could have 10 SubReports and that among those SubReports there are two or more SubReportItems with the same Title. If that is the case, then you may need to rethink this a bit, otherwise you will get a DuplicateKeyException when you try to add a Title that is already in the dictionary.
Explanation:
Basically, we are taking the set of reports, and applying the condition that we only want reports where the ID is the desired one. Personally, I would put this in a separate line, and use SingleOrDefault instead of Where because I expect only one result.
Next, we call .Cast<Report> just because you are using as Report, so I guess you had a need for it. This may be redundant and unnecessary in practice.
The first .SelectMany call will get all SubReports of all Reports. Again, we probably will only ever have one Report object at this point.
Now we have a bunch of SubReports, but we really want to get all the SubReportItems, so we use another SelectMany to get those.
Now that we have all the SubReportItems from all the SubReports from all the (1) Reports, we create the dictionary. For each SubReportItem we create a key from the Title property, and then for the value we use one final SelectMany to get all the NoteDetails objects associated with all the current SubReportItemNotes.

linq problem with distinct function

I am trying to bind distinct records to a dropdownlist. After I added distinct function of the linq query, it said "DataBinding: 'System.String' does not contain a property with the name 'Source'. " I can guarantee that that column name is 'Source'. Is that name lost when doing distinct search?
My backend code:
public IQueryable<string> GetAllSource()
{
PromotionDataContext dc = new PromotionDataContext(_connString);
var query = (from p in dc.Promotions
select p.Source).Distinct();
return query;
}
Frontend code:
PromotionDAL dal = new PromotionDAL();
ddl_Source.DataSource = dal.GetAllSource();
ddl_Source.DataTextField = "Source";
ddl_Source.DataValueField = "Source";
ddl_Source.DataBind();
Any one has a solution? Thank you in advance.
You're already selecting Source in the LINQ query, which is how the result is an IQueryable<string>. You're then also specifying Source as the property to find in each string in the databinding. Just take out the statements changing the DataTextField and DataValueField properties in databinding.
Alterantively you could remove the projection to p.Source from your query and return an IQueryable<Promotion> - but then you would get distinct promotions rather than distinct sources.
One other quick note - using query syntax isn't really helping you in your GetAllSources query. I'd just write this as:
public IQueryable<string> GetAllSource()
{
PromotionDataContext dc = new PromotionDataContext(_connString);
return dc.Promotions
.Select(p => p.Source)
.Distinct();
}
Query expressions are great for complicated queries, but when you've just got a single select or a where clause and a trivial projection, using the dot notation is simpler IMO.
You're trying to bind strings, not Promotion objects... and strings do not have Source property/field
Your method returns a set of strings, not a set of objects with properties.
If you really want to bind to a property name, you need a set of objects with properties (eg, by writing select new { Source = Source })

Selecting single items from MVC enumerable models

when using a MVC 3 collection that uses IEnumerable, is there a way to make small queries work to find single values? I've been experimenting with little success.
I have a collection of settings that have descriptions and settings. The problem is exposing one of them, as each is unique. I've tried something like this:
var appl = _service.GetSettings(id, app); //Call to service layer & repository
appl.Select(a => a.setting_value.Where(a.setting_name.StartsWith("Login")));
With little success (unless I'm missing something). Is it possible to select one item from an enumerable collection and either pass it to a ViewBag or ViewData object? What I would like to do is something like the following:
var appl = _service.GetSettings(id, app); //Call to service layer & repository
ViewBag.Login = appl.Select(a => a.setting_value.Where(a.setting_name.StartsWith("Login")));
And pass this to the view, since I have a view that now combines a collection with single values.
The following seems to work within the view:
Application Name #Html.TextBox("application_name", #Model.FirstOrDefault().app_name)
But I'm not sure if this violates separation of concerns. Am I on the wrong path here? Thank you!
EDIT: Here is what I needed. The answers below got me very very close +1 +1
ViewBag.Login = appl.Where(a => a.setting_name.StartsWith("Login")).FirstOrDefault().setting_value
ViewBag.Login = appl.Select(a => a.setting_value.Where(a.setting_name.StartsWith("Login"))).FirstOrDefault();
This will select the first object that matches your criteria and return a single result or null
var appl = _service.GetSettings(id, app);
ViewBag.Login = appl
.Where(a => a.setting_name.StartsWith("Login"))
.FirstOrDefault();

LINQ Query to find all tags?

I have an application that manages documents called Notes. Like a blog, Notes can be searched for matches against one or more Tags, which are contained in a Note.Tags collection property. A Tag has Name and ID properties, and matches are made against the ID. A user can specify multiple tags to match against, in which case a Note must contain all Tags specified to match.
I have a very complex LINQ query to perform a Note search, with extension methods and looping. Quite frankly, it has a real code smell to it. I want to rewrite the query with something much simpler. I know that if I made the Tag a simple string, I could use something like this:
var matchingNotes = from n in myNotes
where n.Tags.All(tag => searchTags.Contains(tag))
Can I do something that simple if my model uses a Tag object with an ID? What would the query look like. Could it be written in fluent syntax? what would that look like?
I believe you can find notes that have the relevant tags in a single LINQ expression:
IQueryable<Note> query = ... // top part of query
query = query.Where(note => searchTags.All(st =>
note.Tags.Any(notetag => notetag.Id == st.Id)));
Unfortunately there is no “fluent syntax” equivalent for All and Any, so the best you can do there is
query = from note in query
where searchTags.All(st =>
note.Tags.Any(notetag => notetag.Id == st.Id))
select note;
which is not that much better either.
For starters see my comment; I suspect the query is wrong anyway! I would simplifiy it, by simply enforcing separately that each tag exists:
IQueryable<Note> query = ... // top part of query
foreach(var tagId in searchTagIds) {
var tmpId = tagId; // modified closures...
query = query.Where(note => note.Tags.Any(t => t.Id == tmpId));
}
This should have the net effect of enforcing all the tags specified are present and accounted for.
Timwi's solution works in most dialects of LINQ, but not in Linq to Entities. I did find a single-statement LINQ query that works, courtesy of ReSharper. Basically, I wrote a foreach block to do the search, and ReSharper offered to convert the block to a LINQ statement--I had no idea it could do this.
I let ReSharper perform the conversion, and here is what it gave me:
return searchTags.Aggregate<Tag, IQueryable<Note>>(DataStore.ObjectContext.Notes, (current, tag) => current.Where(n => n.Tags.Any(t => t.Id == tag.Id)).OrderBy(n => n.Title));
I read my Notes collection from a database, using Entity Framework 4. DataStore is the custom class I use to manage my EF4 connection; it holds the EF4 ObjectContext as a property.

Resources