ObjectId.ToString not working in projection (LINQ) - mongodb-.net-driver

I get an exception when executing this code:
List<TenantSelectorDto> foundSelectors = null;
//.....
foundSelectors = tenants
.Where(filter)
.Select(ts => new TenantSelectorDto
{
Id = ts.Id.ToString(),
Name = ts.Name,
ShortName = ts.ShortName,
TenantCode = ts.TenantCode,
})
.ToList();
The exception is:
NotSupportedException, Message: ToString of type System.Object is not supported in the expression tree {document}{_id}.ToString().
Id in the DTO is defined as string while the Id of the Tenant object is a MongoDB ObjectId. The DTO is part of a REST API and I do not want to force consumers to link with the entire MongoDB driver libraries just because of the data type ObjectId.
Is this a bug or why does .ToString not work when using in a projection?

Seems to be a LINQ problem! Instead of using LINQ I tried to use the Aggregation Framework and here it works as expected!
var filterDefinition = Builders<Tenant>.Filter.Where(<Build whatever filter you need here>);
var foundSelectors = tenantCollection
.Find(filterDefinition)
.Project(ts => new TenantSelectorDto
{
Id = ts.Id.ToString(),
Name = ts.Name,
ShortName = ts.ShortName,
TenantCode = ts.TenantCode,
})
.ToList();
Does the trick without exception!
Don't know if this is a general LINQ problem or a problem of the MongoDB LINQ implementation though.

Related

projection wont work in mongodb c# driver

I have class that executes mongo queries
its works but when I send projection in query, projection won't work
and mongo return hole document
whats the matter?
query = new QueryDocument( BsonSerializer.Deserialize<BsonDocument>(queryStr));
queryStr="{family:'james'},{}" => its ok
queryStr="{},{family:0}" => not ok. return all columns, but don't want to get family column
Remember: just want this method. not another methods. because want send any query to mongo. I've read mapped mongo objects like ORMs.
just want this method. thanks
Did you look into the new C# 2.0 driver documentation? Looks like projection is a part of the options argument you can give FindAsync
private static void Find(IMongoCollection<Person> mongoCollection)
{
var query = Builders<Person>.Filter.Eq(p => p.Name, "bob");
var options = new FindOptions<Person>()
{
Projection = Builders<Person>.Projection
.Include(p => p.Name)
.Exclude(p => p.Id)
};
var result = await mongoCollection.FindAsync(query, options);
...
The BsonDocument object created from JSON {.A.}{.B.} in the question has 2 braces-pairs, and only the first one will matter (A). This is OK, since projection and query are 2 separate items.
Personally

LINQ to Entities does not recognize the method , method cannot be translated into a store expression

private void BindGrid()
{
AdvContextEF db = new AdvContextEF();
var query = from r in db.mytable
orderby r.CreateDate descending
select new
{
r.id,
r.code,
r.mytable.relatedtables[0].TheCenter.Name
};
RadGrid1.DataSource = query.ToList();
RadGrid1.DataBind();
}
I got the following error when running the code above.
LINQ to Entities does not recognize the method 'AdvContextEF.mymethod get_Item(Int32)' method, and this method cannot be translated into a store expression.
thank you
Instead of trying to index into r.mytable.relatedtables[0], try using .FirstOrDefault().
r.mytable.relatedtables.FirstOrDefault().TheCenter.Name
or
Name = r.mytable.relatedtables.Select(rt => rt.TheCenter.Name).FirstOrDefault()

Returning an odata IQueryable object that differs to the query options

I need to get the following code to work
public IQueryable<BankingDTO> Get(ODataQueryOptions<TillSummaryDTO> options)
{
return((IQueryable<BankingDTO>)options.ApplyTo(this._bankingService.GetBanking()));
}
I would like to query on TillSummaryDTO because it has the field "TillOpID" on it. However I would like to return BankingDTO as this is the end result which contains the group by and sum. When I run the query I receive the error "Cannot apply ODataQueryOptions of 'Bepoz.Presentation.ViewModels.TillSummaryDTO' to IQueryable of 'Bepoz.Presentation.ViewModels.BankingDTO" what is the best practice for this?
The bankingservice.GetBanking method looks like this
var query = from t in _tillSummaryRepository.Table
join w in _workStationRepository.Table on t.TillOpID equals w.WorkstationID
join s in _storeRepository.Table on w.StoreID equals s.StoreID
join v in _venueRepository.Table on s.VenueID equals v.VenueID
select new TillSummaryDTO
{
TillOpID = t.TillOpID,
Cash = t.Cash,
Workstation = new WorkstationDTO()
{
WorkstationID = w.WorkstationID,
Name = w.Name,
Store = new StoreDTO()
{
StoreID = s.StoreID,
StoreGroup = s.StoreGroup,
Name = s.Name,
Venue = new VenueDTO()
{
VenueID = v.VenueID,
VenueGroup = v.VenueGroup,
Name = v.Name,
}
}
}
};
return query.GroupBy(x => x.Workstation.Name)
.Select(x => new BankingDTO()
{
TotalCash = x.Sum(y => y.Cash),
WorkstationName = x.Key
});
The scenario you want to achieve is that you have an entity set of TillSummaryDTO that you want to query, and you would like the return type to be a collection of BankingDTO. The query for the BankingDTO is carried out by applying the query options in the URL onto TillSummaryDTO . But the fact that the BankingDTO and TillSummaryDTO are different kind of types makes it impossible achieve that in a simple Get action method, right?
This scenario can be better resolved by the function feature of the OData protocol that the function takes the TillSummaryDTO collection as input parameter, has some internal complicated logic to query for the right BankingDTO, and returns the BankingDTO instead of TillSummaryDTO.
For the concept of function in OData protocol, you can refer to this link for V4 and section "10.4.2. Functions" of this page for V3.
For implementation, this sample can be referred to for Web API OData V4 and this tutorial can be referred to for Web API OData V3.

Returning Linq query results into a List object (based on condition)

I need to return a number of Linq query results into a List object based on a foreign key value. What is the syntax for doing this? I am new to using Linq, so below is my best guess so far. I receive an error in the .Where() "clause" stating "The name 'pt' does not exist in the current context. Any help would be greatly appreciated!
List<AgentProductTraining> productTraining = new List<AgentProductTraining>();
var prodCodes = productTraining.Select(pt => new[]
{
pt.ProductCode,
pt.NoteId,
pt.ControlId
})
.Where(pt.CourseCode == course.CourseCode);
You would need to switch the locations of where and select if you're using extension methods:
var prodCodes = productTraining.Where(pt => pt.CourseCode == course.CourseCode)
.Select(pt => new SomeRandomType
{
ProductCode = pt.ProductCode,
NoteId = pt.NoteId,
ControlId = pt.ControlId
});
I also recommend, as you can see above, that you create a type for that select statement so that you're not relying on anonymous types. You should put in into an object type that you know everything about.
Also, if CourseCode is a string, that should be pt.CourseCode.Equals(course.CourseCode).

LINQ to Entities does not recognize the method 'Boolean CheckMeetingSettings(Int64, Int64)' method

I am working with code first approach in EDM and facing an error for which I can't the solution.Pls help me
LINQ to Entities does not recognize the method 'Boolean
CheckMeetingSettings(Int64, Int64)' method, and this method cannot be
translated into a store expression.
My code is following(this is the query which I have written
from per in obj.tempPersonConferenceDbSet
where per.Conference.Id == 2
select new PersonDetials
{
Id = per.Person.Id,
JobTitle = per.Person.JobTitle,
CanSendMeetingRequest = CheckMeetingSettings(6327,per.Person.Id)
}
public bool CheckMeetingSettings(int,int)
{
///code I have written.
}
Please help me out of this.
EF can not convert custom code to SQL. Try iterating the result set and assigning the property outside the LINQ query.
var people = (from per in obj.tempPersonConferenceDbSet
where per.Conference.Id == 2
order by /**/
select new PersonDetials
{
Id = per.Person.Id,
JobTitle = per.Person.JobTitle,
}).Skip(/*records count to skip*/)
.Take(/*records count to retrieve*/)
.ToList();
people.ForEach(p => p.CanSendMeetingRequest = CheckMeetingSettings(6327, p.Id));
With Entity Framework, you cannot mix code that runs on the database server with code that runs inside the application. The only way you could write a query like this, is if you defined a function inside SQL Server to implement the code that you've written.
More information on how to expose that function to LINQ to Entities can be found here.
Alternatively, you would have to call CheckMeetingSettings outside the initial query, as Eranga demonstrated.
Try:
var personDetails = obj.tempPersonConferenceDbSet.Where(p=>p.ConferenceId == 2).AsEnumerable().Select(p=> new PersonDetials
{
Id = per.Person.Id,
JobTitle = per.Person.JobTitle,
CanSendMeetingRequest = CheckMeetingSettings(6327,per.Person.Id)
});
public bool CheckMeetingSettings(int,int)
{
///code I have written.
}
You must use AsEnumerable() so you can preform CheckMeetingSettings.
Linq to Entities can't translate your custom code into a SQL query.
You might consider first selecting only the database columns, then add a .ToList() to force the query to resolve. After you have those results you van do another select where you add the information from your CheckMeetingSettings method.
I'm more comfortable with the fluid syntax so I've used that in the following example.
var query = obj.tempPersonConferenceDbSet
.Where(per => per.Conference.Id == 2).Select(per => new { Id = per.Person.Id, JobTitle = per.Person.JobTitle })
.ToList()
.Select(per => new PersonDetails { Id = per.Id,
JobTitle = per.JobTitle,
CanSendMeetingRequest = CheckMeetingSettings(6327, per.Person.Id) })
If your CheckMeetingSettings method also accesses the database you might want to consider not using a seperate method to prevent a SELECT N+1 scenario and try to express the logic as part of the query in terms that the database can understand.

Resources