How can I use a lambda function in the FindAsync() method? - c#

I want to find objects that match a certain requirement. In this case, I want the className attribute of the Course class to be equal to "course". The statement I wanted to use was:
Course matchingCourse = collection.FindAsync(x => x.className == "course");
The className attribute is not the primary key, so I cannot simply put the key in.
From this I get the error "Cannot convert lambda expression to type 'object' because it is not a delegate type". I have looked around and I cannot figure out how to solve the issue in this context.
How can I use a lambda to do what I'm trying to do in FindAsync(), or is there another way?

You can use a Where followed by a FirstOrDefault or SingleOrDefault or just pass the predicate to those, unless you need the cache that Find gives you. This will call the database each time. Find only works on primary keys.
For example:
Course matchingCourse = collection.FirstOrDefault(x => x.className == "course");
For Async:
Course matchingCourse = await collection.FirstOrDefaultAsync(x => x.className == "course");

The main difference between Find and Select is that Find will first check whether the object that you want has been fetched before since you created the DbContext. Select will always fetch the data from the database.
In fact, Find is used, if you fetched an object, lost the reference to it, and want the fetched object again, not the original.
Usually it is not a good idea to keep the DbContext alive for a long time, so chances are very small that you loose the reference to the fetched object.
Therefore my advice would be to use Select to fetch the item:
(leaving out the async, not part of your problem)
var result = collection
.Where(collectionElement => collectionElement.ClassName == "course")
.FirstOrDefault();
If you really need to get the earlier fetched item, you can use DbContext.ChangeTracker.Entries<...>() and do a Where(...) on the already fetched objects

Related

Perform includes on key object, using LINQ, in a GroupBy situation

I have a relatively simple, yet somehow weirdly complicated case whereby I need to perform includes on a lengthy object graph, when I'm doing a group-by.
Here is roughly what my LINQ looks like:
var result = DbContext.ParentTable
.Where(p => [...some criteria...])
.GroupBy(p => p.Child)
.Select(p => new
{
ChildObject = p.Key,
AllTheThings = p.Sum(p => p.SomeNumericColumn),
LatestAndGreatest = p.Max(p => p.SomeDateColumn)
})
.OrderByDescending(o => o.SomeTotal)
.Take(100)
.ToHashSet();
That gives me a listing of anonymous objects, just the way I want it, with child objects neatly associated with some aggregate stats about said object. Fine. But I also need a fair share of the object graph associated with child object.
This ask gets even a bit messier than it might otherwise be, when I want to use existing code, I already have to perform the includes. I.e., I have a static method which will take an IQueryable of my child object and, based upon parameters, give me back another IQueryable, with all the proper includes that I need (there are rather a lot of them).
I can't seem to figure the correct way to take my child object as a queryable, and give that to my include method, such that I get it back, for expansion at the point I want to express it to the new anonymous object (where I'm saying ChildObject = n.Key).
Sorry if this is something of a duplicate -- I did search around and found solutions that were close to what I'm wanting, here but not quite.

Getting weird behavior when retrieving data from Microsoft CRM using LINQ

I'm having this problem accessing the Contact entity using LINQ.
I have the 2 functions below.
If I ran the 1st function and then call the 2nd one, I seemed to be missing a lot of fields in the 2nd query. Like firstname and lastname are not showing up. They just shows up as null values. If I ran the 2nd function on its own, I am getting the right data. The only fields that shows up correctly in both runs are Id, ContactId and new_username.
If I ran the 2nd function on its own, I am getting the right data.
Any ideas what am I doing wrong?
Thanks a lot
Here are the 2 functions
public List<String> GetContactsUsernameOnly()
{
IQueryable<String> _records = from _contactSet in _flinsafeContext.ContactSet
where
_contactSet.new_FAN == "username"
orderby _contactSet.new_username
select _contactSet.new_username;
return _records.ToList();
}
public List<Contact> GetContacts()
{
IQueryable<Contact> _records = from _contactSet in _flinsafeContext.ContactSet
where
_contactSet.new_FAN == "my-username-here"
orderby _contactSet.new_username
select _contactSet;
return _records.ToList();
}
It is because you are reusing the same CRM context when you call both methods (in your case _flinsafeContext)
What the context does is cache records, so the first method is returning your contact but only bringing back the new_username field.
The second method wants to return the whole record, but when it is called after the first one the record already exists in the context so it just returns that, despite only having the one field populated. It is not clever enough to lazy load the fields that have not been populated. If this method was called first, it doesn't exist in the context so will return the whole record.
There are 2 ways to get around this:
1) Don't reuse CRMContexts. Instead create a new one in each method based on a singleton IOrganizationService.
2) There is a ClearChanges() method on your context that will mean the next time you do a query it will go back to CRM and get the fields you have selected. This will also clear any unsaved Created/Updates/Deletes etc so you have to be careful around what state the context is in.
As an aside, creating a new CRM Context isn't an intensive operation so it's not often worthwhile passing contexts around and reusing them. It is creating the underlying OrganisationService that is the slowest bit.
This behaviour can be so painful, because it is horribly inefficient and slow to return the entire record so you WANT to be selecting only the fields you want for each query.
And here's how you return just the fields you want:
IEnumerable<ptl_billpayerapportionment> bpas = context.ptl_billpayerapportionmentSet
.Where(bm => bm.ptl_bill.Id == billId)
.Select(bm => new ptl_billpayerapportionment()
{
Id = bm.Id,
ptl_contact = bm.ptl_contact
})
This will ensure a much smaller sql statement will be executed against the context as the Id and ptl_contact are the only two fields being returned. But as Ben says above, further retrievals against the same entity in the same context will return nulls for fields not included in the initial select (as per the OP's question).
For bonus points, using IEnumerable and creating a new, lightweight, entity gives you access to the usual LINQ methods, e.g. .Any(), .Sum() etc. The CRM SDK doesn't like using them against var datasets, apparently.

Of what type is the result of a LINQ query?

Examples on LINQ gives this
var query = context.Contacts
.Where(q => q.FirstName == "Tom");
I'm wondering what object is "query"? And also is it possible (advisable) to pass it to a method (within the same class)?
The query object is most likely of type IQueryable<Contact>. You can of course pass it to a method, whether that is in the same class or in another class does not matter.
But keep in mind that LINQ does use a mechanism named "deferred execution". That means that query does not get enumerated immediately, but rather when it is needed. All the stuff you put in your query (the Where-clause for example) gets executed then. For more information about deferred execution have a look at MSDN: Query Execution.
NB: You can find out the exact type of the query variable if you hover you mouse over it or the var keyword in Visual Studio.

Performing SQL "in" equivalent in EF

I have two rateable objects E and S, S is contained within E so you say E.S and they both use the same object R to be rated; R will always have ONLY ONE rated object at a time so if R.E.HasValue == true then R.S.HasValue will never be true.
I have all the R objects from the database and I have E. What would be the lambda expression to get all Rs Where R.S in E.S??
UPDATE
I found this MSDN Documentation where it says:
Referencing a non-scalar variables,
such as an entity, in a query is not
supported. When such a query executes,
a NotSupportedException exception is
thrown with a message that states
"Unable to create a constant value of
type EntityType. Only primitive types
('such as Int32, String, and Guid')
are supported in this context."
So... I can't use A-Dubb's answer since the query reads
R.Where(r => r.SID.HasValue).Where(r => E.S.Contains(r.S))
Which results in a NotSupportedException... I still have the property R.SID which is of type int? but how could I then query all E.S to get their IDs?
P.S. E.S. is of type EntityCollection.
I will say that IN clauses are normally represented with call to Contains. For example
public class R {
public int Id {get; set;}
}
public IEnumerable<R> InClause(IEnumerable<int> ids) {
var subset = ids.ToList();
return dbContext.Query<R>.Where(r => subset.Contains(r.Id));
}
I know that's not the exact API call in EF, but it should help set the basis for your solution. Maybe you already know how to generate IN clauses but I figure if you did then you presumably wouldn't be stuck. If the IN clause isn't the difficult part of the query then what is? Maybe the piece of business logic you mentioned? Again, a C# object model would help.
The problem with using contains though, is that its closer to sql "like" than "in". So if you have user entry on say 8 digit ID's and someone
enters 0 then contains will bring you back ALL id's with zero somewhere in it.

load an object twice and change one of them, the other one doesnt changed

Father father = BL.GetFatherById(1);
Product product = BL.GetByID(123);
(father.Products[553] == product)
product.delete = true;
father.Products[553].delete == false !!??
why is that ?
aren't they connected ?? its the same object.
As you can read in section 10.3 of the NHibernate reference manual database identity and CLR object identity are equivalent per session.
Therefore Object.ReferenceEquals(foo, bar) will yield true if and only if foo and bar are attached to the same session and map to the same database row. Be careful when using == for comparing object identity - the operator may have been overloaded (but you should usually know that).
In consequence you should always get the same object no matter what query you use for the object as long as you stay within the same session. Are you using multiple sessions? Maybe a Unit of Work pattern and you are comparing objects returned from to different units of work?
First, let me tell you that what you are doing is HORRIBLE. What does this actually mean
father.Products[553] == product;
Unless you have coded a custom collection, which I doubt you did there is no way that would work.
Are you removing product that is
already at index 553 from collection?
No
Are you breaking the relationship
between father and product on index
553? No
Are you establishing relatinship to a
father with a new product? No
Are you establishing relationship
from new product to father? No
So
Expose products a IEnumerable rather than list
Add Add/Remove methods that will handle relationship syncronozation.
Take a look here how to do it (disregard the actual question)
How to map it? HasOne x References

Categories

Resources