Querying Complex Object using Lambda - c#

I am querying a complex c# object that describes a Shift Roster. This object is populated from a web service and is an object from a third party software supplier.
My task is to build a gridview from data from this third party product. The object is an that contains other objects nested inside most of which are an array of objects.
I am using Lambda Expressions to navigate down the object, but I am struggling to get values out, and I am also wondering whether the expression I am writing could be better. Here is one example where I am trying to get an employee name out:
var employeeDetails = employeeRoster.PathsRosters.SelectMany(a => a.EmployeesRosters);
var empName = employeeDetails.Select(b => b.Resource.Name).ToList();
foreach (string nam in empName)
{
string var = nam;
}
employeeRoster is the object that I get back from the web service and PathRosters is an array. Then I am querying the result again and casting to a List just to get back the employee name.
Is there a better way of doing this? Unfortunately, I cannot serialize to XML as I have been told it has to be done in memory on the object.

Instead of making two calls, you can do the same with one:
var employeeNames = employeeRoster.PathsRosters.SelectMany(a => a.EmployeesRosters)
.Select(b => b.Resource.Name).ToList();
Also, your foreach loop doesn't do anything. I don't see the point in having this.

var empNames = employeeRoster.PathsRosters.SelectMany(a => a.EmployeesRosters)
.Select(b => b.Resource.Name).ToList();

Related

How to query for specific type and then get underlying document

What I'm trying to do is query by a specific document type
MyType foo = client.CreateDocumentQuery<MyType>(documentCollectionUri)
.ToList()
.FirstOrDefault(d => d.SomeProperty == someProperty);
then replace the document with another. It looks like the only methods available are
public Task<ResourceResponse<Document>> ReplaceDocumentAsync(Uri documentUri, object document, RequestOptions options = null);
public Task<ResourceResponse<Document>> ReplaceDocumentAsync(Document document, RequestOptions options = null);
public Task<ResourceResponse<Document>> ReplaceDocumentAsync(string documentLink, object document, RequestOptions options = null);
all of which require the Document or its id.
So, how do I get those values from foo? Not possible?
First and foremost something is not quite clear by the way you express your question.
MyType foo = client.CreateDocumentQuery<MyType>(documentCollectionUri)
.ToList()
.FirstOrDefault(d => d.SomeProperty == someProperty);
If you are storing multiple different types of object in the same question then using the T parameter MyType does not guarantee that the results you are gonna get will only be MyType results but rather anything that matches the expression will be mapped and returned as MyType. There is not other filtering in the CosmosDB SDK that guarantees that the querying will only occur on objects of that type because CosmosDB has no context of that.
The only reliable way you can do this using the ReplaceDocumentAsync method is to (as Alex AIT said) add the Id property with a [JsonProperty("id")] attribute on your object. Considering that it is a DTO and not a Domain object, this property should be there and it will really help with other functionality such us delete, etc.
I would highly recommend looking at Cosmonaut which is a CosmosDB ORM which also handles Type based collection sharing and the same querying and updating you are trying to achieve without having to mess with any such logic.
Disclaimer: I am the creator of Cosmonaut
On a side note, you should not use ToList() on an IQueryable created using CreateDocumentQuery but instead use the .AsDocumentQuery() method on it and then use the combination of while(query.HasMoreResults) then query.ExecuteNextAsync() method to make proper use of the paginated CosmosDB functionality.
First, you should do your query like this:
List<MyType> foo = client.CreateDocumentQuery<MyType>(documentCollectionUri)
.Where(d => d.SomeProperty == someProperty);
.ToList();
Otherwise you will fetch all documents from db before you even filter for your property.
For your actual question, you have two options:
1) Make the id part of your document
CosmosDB will automatically set the Id when saving, and deserialize it to the field when retrieving the document. You can then use the id for the replace operation.
class MyType{
[Newtonsoft.Json.JsonProperty(PropertyName="id")]
public string Id {get; set;}
}
// Get documentUri by:
UriFactory.CreateDocumentUri(string databaseId, string collectionId, string documentId)
public Task<ResourceResponse<Document>> ReplaceDocumentAsync(Uri documentUri, object document, RequestOptions options = null);
2) Separate the query definition from the query execution
You can use different types for query definition and response deserialization. So you can query against MyType and deserialize the response as Document.
var query = client.CreateDocumentQuery<MyType>(documentCollectionUri)
.Where(d => d.SomeProperty == someProperty)
.AsDocumentQuery();
var response = await query.ExecuteNextAsync<Document>(cancellationToken).ConfigureAwait(false);
var document = response.FirstOrDefault();

Accessing the properties of an IEnumerable

I'm using TweetInvi to grab a bunch of tweets that match a specified hashtag. I do this with the following:
var matchingTweets = Search.SearchTweets(hashtag);
This returns an IEnumerable (named ITweet, interface of Tweet), however I cannot create a List<> of Tweets, because Tweet is a static type.
I made, instead, a list of objects, using:
List<object> matches = matchingTweets.Cast<object>().ToList();
However, although each member of the matchingTweets IEnumerable has a number of properties, I cannot access them using:
long tweetID = matches[i].<property>;
Using matches[i].ToString() returns the tweet content, so how can I effectively cast the results in matchingTweets to a list, and subsequently access the properties of those list members? I would ideally like to avoid using dynamic.
In your example above you were trying to grab the ID from the tweet. ITweet implements ITweetIdentifier which contains the Id property. You can literally just access it by:
var matchingTweets = Search.SearchTweets(hashtag);
//Grab the first 5 tweets from the results.
var firstFiveTweets = matchingTweets.Take(5).ToList();
//if you only want the ids and not the entire object
var firstFiveTweetIds = matchingTweets.Take(5).Select(t => t.Id).ToList();
//Iterate through and do stuff
foreach (var tweet in matchingTweets)
{
//These are just examples of the properties accessible to you...
if(tweet.Favorited)
{
var text = tweet.FullText;
}
if(tweet.RetweetCount > 100)
{
//TODO: Handle popular tweets...
}
}
//Get item at specific index
matchingTweets.ElementAt(index);
I don't know exactly what you want to do with all the info, but since the SearchTweets returns a IEnumerable of ITweets you have access to anything an ITweet has defined.
I highly recommend looking through their wiki. It's pretty well organized and gives you clear examples of some basic tasks.
It makes sense you cannot access the properties. You cast it into object so you can only access the objects properties and methods (that like you said might have been overridden).
It should be fine to just access it like this:
List<ITweet> tweets = matchingTweets.Take(5).ToList();
What you can do is project it to a new object of yours:
var tweets = matchingTweets.Select(item => new {
property1 = item.property1,
property2 = item.property2
})
.Take(5).ToList();
Then you will be able to access what you need. Now, if you need to share this data outside the scope of that function create a DTO object and initialize it instead of the anonymous type.
Depending on the size of the project and amount of effort usually it is in any case a good practice to create a layer of DTO objects when you interact with an external service like this. Then if their models changed you can contain your changes only to the DTOs.
If all you want are the ids of the first 5 then:
var ids = matchingTweets.Take(5).Select(item => item.id).ToList();

Convert IEnumerable<T> to collection of dynamically generated objects

Recently I asked a more general question about getting properties of model through foreign key.
Now I moved a bit further but still have no idea how transform objects on the fly.
What I've got is an IEnumerable collection which I get through repository
regionRaw = unitOfWork.RegionRepository.Get(
keyOrder: q => q.OrderBy(d => d.RegionID),
filter: p => p.FullName.Contains(lastname) || p.ShortName.Contains(lastname),
orderBy: jtSorting,
includeProperties: "District, ISO31662, GOST767Region");
Further I am going to export data from this collection to Excel. So I need a select statement that gets all the fields I need.
dt = regionRaw
.Select(x => new
{
ISO = x.ISO31662.GOSTName,
DistrictName = x.District.ShortName
})
I do not want to enumerate all the fields I need like on the top.
I am able to make a method that recognizes which of the fields have simple values and which have objects referenced through foreign key. And then that method will return a list of properties.
Now I need some way to write something like a foreach inside select. I see something like this:
dt = regionRaw
.Select(x => new
{
foreach (prop in propList)
{
prop.PropertyName = x.GetType()
.GetProperty(prop.TableName)
.GetValue(x, null).GetType()
.GetProperty(prop.PropertyName)
.GetValue(
x.GetType().GetProperty(prop.TableName).GetValue(x, null),
null);
}
}
Where propList is a collection of properties that I get before.
I do totally realize that upper code is more a pseudo-code but I have no idea how to realize this in .NET.
So if you can suggest some solution for this task I will be very grateful. Or maybe you could explain that all this is a bad idea and shouldn't be realized.
You wont be able to create an anonymous type with dynamic properties as anon types are created during compile and your properties are created during execution.
But why do you need strongly typed properties if you're not going to code against them, as you wont know them until someone executes the query?
Expando object may be of use to you?http://msdn.microsoft.com/en-us/library/system.dynamic.expandoobject.aspx

Is there any way of knowing the content of an IQueryable object without using Reflection?

I have to work with a given class "QueryGenerator" that generates dynamic queries from selected Tables and Columns by the user using a StringConnection and a Provider. Anyways, i don't have to know the implementation of the class but i have to use it and i'm stock.
At the end, the "QueryGenerator" returns the result query as an object, the only thing that i know (because i use Reflector on the class) is that i can do an IQueryable cast on that query result. Here is an example:
var result = (IQueryable)myQueryGenerator.Result;
And for knowing the content of result, i have to use Reflection.
So, is there any better way of finding out the content of result , and, for example, fill a DataSet with it?
No, there isn't.
IQueryable could return objects of different types (e.g. if you query against an array of objects). In this extreme case you would need to determine the type for each individual item in the enumerated query as you access them.
Example code to demonstrate this scenario:
object[] objs = new object[3]{ "string", 78, DateTime.Now };
var q = objs.AsQueryable().Skip(1).Take(2);
foreach( var o in q )
{
var t = o.GetType();
}

Filter Generic Collection Down to Records Containing Any Item in Collection of String

string rep = "Joe Shmoe"
ObjectSet<StoreData> storeData = edmContext.StoreData;
ObjectSet<CallData> callData = edmContext.CallData;
IEnumerable<string> repStoreData = storeData.Where(r => r.RepName == rep).Select(s => s.Location);
IEnumerable<CallData> repCallData = Here is where I want to filter down the callData collection down to just the records that have a location that is contained in the repStoreData collection
I've tried using some form of Join and Any but don't really understand the arguments those are asking for.
This was my best attempt and it is a no go.
... = callData.Join(d => d.LOCATION.Any(repStoreData));
Well you don't have to use a join. You could just use:
callData.Where(d => repStoreData.Contains(d.LOCATION))
That's assuming d.LOCATION is a single string.
However, you probably don't want to do that with your current declaration of repStoreData as IEnumerable<string> - LINQ won't be able to turn that into a query to be executed at the database.
If you're able to declare repStoreData as IQueryable<string>, however, that would be more likely to work well. I don't know whether that will work with ObjectSet<T>, but I'd hope so.

Categories

Resources