Mongodb best way to query - c#

Hi started using mongodb with c#, finding it really cool, just wondering the best way to query
Seem to have an option of the followings
var query = Query<Entity>.EQ(e => e.Id, id);
var entity = collection.FindOne(query);
vs
var entity = collection.Entity.AsQueryable().Single(x => x.Id == id)
Now the second looks more attractive to me as its what Im used to, but in terms of performance and best practice, whats the difference and whats recommended?

Your first example is using the drivers native query - Essentially creating a QueryDocument via the Query<T> helpers etc..
Your second is using Linq.
Under the hood, these both boil down to generating the same query:
db.entity.find({_id: 'abc123'});
The resultant QueryDocument is serialized as the query, in this case
{_id: 'abc123'}
According to the linq docs:
Only LINQ queries that can be translated to an equivalent MongoDB query are supported. If you write a LINQ query that can’t be translated you will get a runtime exception and the error message will indicate which part of the query wasn’t supported.
To me, this would suggest there's some overhead converting LINQ queries into MongoDB queries...
I haven't tested this, but I suspect using Query is slightly more performant

The first approach, in which you build a query and then use it to find a result, is effectively the Specification Pattern.
The second approach, being a direct LINQ query, is typically more familiar to .NET developers.
The specification pattern provides you with a reusable piece of filtering logic. It's very useful if you frequently need to filter collections by the same criteria--and especially if the filter needs to be applied to collections from various repositories.
Here's a quick example of a possible useage of it (pardon any rough code, I've been out of C# for a while now):
static class WidgetSpecification {
function Query<Widget> AvailableWidgets()
{
return new Query<Widget>.EQ(e =>
e.StartDate <= Date.Today
&& e.EndDate >= Date.Today
&& e.Active == true
&& e.InStock == true);
}
}
Then, anywhere in your app that you need "available widgets" you'd call something like:
{
...
var products = getProductsFromSomewhere();
var query = WidgetSpecifiation.AvailableWidgets();
var availableProducts = Products.Find(query);
...
}
If you ever redefine what an "available widget" is you can update the specification and have all consumers behave the same.

Related

How can I determine if a LINQ query is going to be LINQ to SQL vs. LINQ to Objects?

Usually the distinction between LINQ to SQL and LINQ to Objects isn't much of an issue, but how can I determine which is happening?
It would be useful to know when writing the code, but I fear one can only be sure at run time sometimes.
It's not micro optimization to make the distinction between Linq-To-Sql and Linq-To-Objects. The latter requires all data to be loaded into memory before you start filtering it. Of course, that can be a major issue.
Most LINQ methods are using deferred execution, which means that it's just building the query but it's not yet executed (like Select or Where). Few others are executing the query and materialize the result into an in-memory collection (like ToLIst or ToArray). If you use AsEnumerable you are also using Linq-To-Objects and no SQL is generated for the parts after it, which means that the data must be loaded into memory (yet still using deferred execution).
So consider the following two queries. The first selects and filters in the database:
var queryLondonCustomers = from cust in db.customers
where cust.City == "London"
select cust;
whereas the second selects all and filters via Linq-To-Objects:
var queryLondonCustomers = from cust in db.customers.AsEnumerable()
where cust.City == "London"
select cust;
The latter has one advantage: you can use any .NET method since it doesn't need to be translated to SQL (e.g. !String.IsNullOrWhiteSpace(cust.City)).
If you just get something that is an IEnumerable<T>, you can't be sure if it's actually a query or already an in-memory object. Even the try-cast to IQueryable<T> will not tell you for sure what it actually is because of the AsQueryable-method. Maybe you could try-cast it to a collection type. If the cast succeeds you can be sure that it's already materialized but otherwise it doesn't tell you if it's using Linq-To-Sql or Linq-To-Objects:
bool isMaterialized = queryLondonCustomers as ICollection<Customer> != null;
Related: EF ICollection Vs List Vs IEnumerable Vs IQueryable
The first solution comes into my mind is checking the query provider.
If the query is materialized, which means the data is loaded into memory, EnumerableQuery(T) is used. Otherwise, a special query provider is used, for example, System.Data.Entity.Internal.Linq.DbQueryProvider for entityframework.
var materialized = query
.AsQueryable()
.Provider
.GetType()
.GetGenericTypeDefinition() == typeof(EnumerableQuery<>);
However the above are ideal cases because someone can implement a custom query provider behaves like EnumerableQuery.
I had the same question, for different reasons.
Judging purely on your title & initial description (which is why google search brought me here).
Pre compilation, given an instance that implements IQueryable, there's no way to know the implementation behind the interface.
At runtime, you need to check the instance's Provider property like #Danny Chen mentioned.
public enum LinqProvider
{
Linq2SQL, Linq2Objects
}
public static class LinqProviderExtensions
{
public static LinqProvider LinqProvider(this IQueryable query)
{
if (query.Provider.GetType().IsGenericType && query.Provider.GetType().GetGenericTypeDefinition() == typeof(EnumerableQuery<>))
return LinqProvider.Linq2Objects;
if (typeof(ICollection<>).MakeGenericType(query.ElementType).IsAssignableFrom(query.GetType()))
return LinqProvider.Linq2Objects;
return LinqProvider.Linq2SQL;
}
}
In our case, we are adding additional filters dynamically, but ran into issues with different handling of case-sensitivity/nullreference handling on different providers.
Hence, at runtime we had to tweak the filters that we add based on the type of provider, and ended up adding this extension method:
Using EF core in net core 6
To see if the provider is an EF provider, use the following code:
if (queryable.Provider is Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider)
{
// Queryable is backed by EF and is not an in-memory/client-side queryable.
}
One could get the opposite by testing the provider against System.Linq.EnumerableQuery (base type of EnumerableQuery<T> - so you don't have to test generics).
This is useful if you have methods like EF.Functions.Like(...) which can only be executed in the database - and you want to branch to something else in case of client-side execution.

Passing a GetWhere query (Func<entityDTO,bool>) to a data layer method which needs a (Func<entity,bool>) parameter to work

I have the following method in a data access class which uses entity framework:
public static IEnumerable<entityType> GetWhere(Func<entityType, bool> wherePredicate)
{
using (DataEntities db = new DataEntities())
{
var query = (wherePredicate != null)
? db.Set<entityType>().Where(wherePredicate).ToList()
: db.Set<entityType>().ToList();
return query;
}
}
This works fine when I use the entities across all layers... however I am trying to move to using a DTO class and I would like to do something like the following:
public static IEnumerable<EntityTypeDTO> GetWhere(Func<EntityTypeDTO, bool> wherePredicate)
{
//call a method here which will convert Func<EntityTypeDTO,bool> to
// Func<EntityType,bool>
using (DataEntities db = new DataEntities())
{
var query = new List<EntityType>();
if (wherePredicate == null)
{
query = db.Set<EntityType>().ToList();
}
else
{
query = (wherePredicate != null)
? db.Set<EntityType>().Where(wherePredicate).AsQueryable<EntityType>().ToList()
: db.Set<EntityType>().ToList();
}
List<EntityTypeDTO> result = new List<EntityTypeDTO>();
foreach(EntityType item in query)
{
result.Add(item.ToDTO());
}
return result;
}
}
Essentially I want a method which will convert Func to Func.
I think I have to break down the Func into an expression tree and then rebuild it somehow in the entityType?
I want to do this to allow the Presentation Layer to just pass the Expression queries?
Am I missing something basic or is there an easier design pattern that can pass a query from a DTO to a data access class without knowing the details of the query?
I have tried making the DTO inherit from the entity which doesn't seem to work either?
If there is a better design pattern that I am missing I would love a pointer and I can investigate from there...
Firstly I would suggest that you put a querying layer of your own in front of Entity Framework rather than allowing any arbitrary Func to be passed in because it will be very easy in the future to pass a Func that Entity Framework can not translate into a SQL statement (it can only translate some expressions - the basics are fine but if your expression calls a C# method, for example, then Entity Framework will probably fail).
So your search layer could have classes that you build up as criteria (eg. a "ContainsName" search class or a "ProductHasId" class) that are then translated into expressions in your search layer. This separates your app entirely from the ORM, which means that ORM details (like the entities or like the limitations of what Funcs can and can't be translated) don't leak out. There's lots out there that's been written about this some of arrangement.
One final note, though, if you are working close to the ORM layer, Entity Framework is very clever and you could probably get a long way without trying to translate your Func<dto, bool> to a Func<entity, bool>. For example, in the below code, accessing "context.Products" returns a "DbSet" and calling Select on it returns an IQueryable and calling Where on that also returns an IQueryable. Entity Framework will translate all of that into a single SQL statement so it won't pull all other Products into memory and then filter the ID on that memory set, it will actually perform the filtering in SQL even though the filter is operating on a projected type (which is equivalent to the DTO in your case) and not the Entity Framework entity -
var results = context.Products
.Select(p => new { ID = p.ProductID, Name = p.ProductName })
.Where(p => p.ID < 10)
.ToList();
The SQL executed is:
SELECT
[Extent1].[ProductID] AS [ProductID],
[Extent1].[ProductName] AS [ProductName]
FROM [dbo].[Products] AS [Extent1]
WHERE [Extent1].[ProductID] < 10
So, if you changed your code to get something like..
return context.Products
.Map<Product, ProductDTO()>()
.Where(productDtoWherePredicate)
.ToList();
.. then you might be just fine with the Funcs that you already have. I presume that you already have some sort of mapping functions to get from EF entities to DTOs (but if not then you might want to look into AutoMapper to help you out - which has support for "projections", which are basically IQueryable maps).
I am going to put this up as an answer.Thanks to Dan for the quick answer. Looking at what you are saying I can write a query/filter set of classes. for example, take the following code:
GetProducts().GetProductsInCategory().GetProductsWithinPriceRange(minPrice, maxPrice);
This code would run like so: Get Products would get all products in the table and the remaining functions would filter the results. if all queries run like this it may put a significant load on the Data Access Layer/ DB Server Connections... not sure.
or
An Alternate I will work on also is:
If each function creates a Linq expression, I could combine them like this: How do I combine multiple linq queries into one results set?
this may allow me to do this in a manner where I can return the filtered results set from the database.
Either way I am marking this as answered. I will update when I have more details.

Why does this LINQ expression break my loop & conversion logic?

Background
ArticleService is a class that provides methods for front-end layers to facilitate business with the back-end.
Two of its base responsibilities are to convert ViewModels (ArticleViewModel) to the appropriate Models (Article) when persisting data, and the reverse, convert Models to ViewModels when fetching data... so often that I created a private method building ViewModel objects:
private ArticleViewModel BuildViewModel(Article a)
{
return new ArticleViewModel { Title = a.Title /* all properties */ }
}
Moving along, the ArticleService provides a method to fetch all articles from the data store, returning them as ViewModels: public IEnumerable<ArticleViewModel> All()
A calling class uses it like this: var articleViewModels = _articleService.All();
Simple, right?
Problem:
I originally wrote All() lazily with a classic foreach loop:
private IEnumerable<ArticleViewModel> All()
{
var viewModels = new List<ArticleViewModel>();
foreach (var article in _db.Articles)
viewModels.Add(BuildViewModel(article));
return viewModels;
}
It worked fine - articleViewModels was an instantiate list of all view models.
Next, I used ReSharper to convert this loop to a LINQ statement for performance and prettiness, and then combined the assignment statement with the return statement. Result:
private IEnumerable<ArticleViewModel> All()
{
return _db.Articles.Select(article => BuildViewModel(article)).ToList();
}
I debugged the LINQ statement and the beast awakened:
LINQ to Entities does not recognize the method 'ArticleViewModel
BuildViewModel(Article)' and this method cannot be translated into a store expression.
Question - Why does this LINQ statement break my code?
Note: Stepping back to an explicit declaration, assignment, return worked with the LINQ statement, so I'm almost certain it's something to do with the lambda logic.
Question - Why does this LINQ statement break my code?
Because LINQ to Entities is trying to translate BuildViewModel into SQL. It doesn't know how, so it dies miserably.
In your original version, you were streaming the entity from the database to your local box, and then doing the projection using BuildViewModel client-side. That's fine.
so I'm almost certain it's something to do with the lambda logic.
Nope. It's because LINQ to Entities can't translate BuildViewModel into SQL. It doesn't matter if you use a lambda expression to express the projection or not.
You can rewrite the code like this:
return _db.Articles.
.AsEnumerable()
.Select(article => BuildViewModel(article)).ToList();
This causes _db.Articles to be treated as a plain old enumerable, and then does the projection client side. Now LINQ to Entities doesn't have to figure out what to do with BuildViewModel.
The answer by Jason explains the error, but doesn't [didn't] list the simple fix (if you still want to use LINQ). Just add a call to .AsEnumerable() right after "_db.Articles". This will force any future LINQ statements to be performed using LINQ to Objects (in memory) rather than trying to use the IQueryable to perform the statements against the database.
Simply your function BuildViewModel is not traslable to raw SQL. That is.

Linq to SQL Expression Properties That are Translatable to SQL

I have a LINQ to SQL class, we'll call it Test, and I want to be able to access properties with LINQ queries but I get the famed "No Supported Translation to SQL" runtime error. I'm interested in the conceptual problem. Here is my simplified class:
public class Test
{
public int ID {get; set;} // Stored in Database
public int NonForeignKeyValue {get; set;} // Stored in Database
}
Here is sort of an example of what I'm trying to accomplish, but I don't want the overhead of always explicitly writing the join in LINQ:
var db = (new DataContext()).GetTable<Test>();
var q = (from t in db.GetTable<Test>()
join o in db.GetTable<OtherTable>() on o.ID equals t.ID
where t.OtherStuff
select t)
I'd like to be able to add a property to Test that tells me if there are any rows in OtherTable that could be joined with Test:
public bool IsInOtherTable
{
get
{
return (new DataContext())
.GetTable<OtherTabke>()
.Any(x => x.NonForeignKeyValue == this.NonForeignKeyValue));
}
}
Ultimately this is what I want my code to look like, but it errors. I basically want to return all entries that contain some database computed value:
using (DataContext db = new DataContext())
{
var q = db.GetTable<Test>()
.Where(x => x.IsInOtherTable && x.OtherStuff); //Error
}
I'm basically trying to save myself from writing this code every single time I want to check if Test has certain information in another table. I'm not that interested in the exact problem I described, I'm more interested in how to conceptually add the join part to the SQL and still use LINQ. I'm guessing I use Linq.Expression, but I really don't know and I'm not aware of how to do it.
As an aside, I could just write the actual SQL, as its not that complicated, but I'd like to know how to get around this and still use LINQ.
Edit: I tried this property, but I get the same error. Its more complicated that just changing the return type to Expression...
public System.Linq.Expressions.Expression<Func<Article3, bool>> Exists
{
get
{
using (DataContext db = new DataContext())
{
return i => db.GetTable<OtherTable>()
.Any(x => x.NonForeignKeyValue == i.NonForeignKeyValue));
}
}
}
Each time the linq generator is to translate a code into a query, it has to process an expression tree.
In your examples, you are not passing around expression but rather - properties, delegates, i.e. stuff which the expression visitor is unable to "step into".
In general, try to rethink your conditions so that instead of bool you have Expression<T, bool> etc.
http://netpl.blogspot.com/2008/02/linq-to-object-vs-linq-to-sql-and.html
Firstly, I think you may be overestimating "the overhead of always explicitly writing the join in LINQ". It's an extra line of code which has the advantage of being relatively self-documenting as to just what you are doing (always a nice thing), and any other approach is going to be turned first into SQL and then into a query plan that will be at least as expensive to execute, possibly more expensive (SQLServer is good a joins!)
Still, there are two alternatives I can thinkof.
One is to have an EntityRef property on the class that defines this relationship with the other table. You can then test if it is null in your query (or EntitySet if it's on the other side of a one-to-many relationship).
The other is to define a SQL function that returns a bit result indicating whether an id refers to a row that does or doesn't relate to the other table.
Then define a protected method on your DataContext-derived class that matches the signature in C# terms, and has a Function attribute mapping it to that SQL function. (Since this isn't something that you can give a sensible non-db-using version of in the C# version, you can implement the C# function by calling ExecuteMethodCall).
Then you can use that method instead of the join.
Still, this is likely less self-explanatory in the code and at risk of being less efficient than just keeping the join.

how to do a Join in Entity Framework

Instead of writing a linq query, is there a way I can just do a join by simply doing something like this:
using (var db = new MatchGamingEntities())
{
db.Accounts.Join() //I am unsure of the syntax
db.Accounts.Include...
...
return View(Account.SingleOrDefault());
}
I want to use these predefined Entity functions instead of writing linq, is it practical? Also how do you use these predefined functions? I have a table called "Accounts" and "BankTransactions" they both have AccountId in common, How would I query that using these functions and what type of result would it return its one to many relationship.
LINQ is really your better bet, things start to get hectic in Lambda's really fast and linq just looks a lot better given the structured way compared to lambda's
See this SO Post:
C# Joins/Where with Linq and Lambda
var query = db.Accounts.Join(db.BankTransactions,
acc => acc.AccountID,
bank => bank.AccountID,
(acc,bank) => new { Account = acc, BankTransaction = bank });
Edit:
This should(or something similar) return a query that will return a collection of Accounts and inside each account it's relevant BankTransaction.
This should do it but again, rather use LINQ if possible.
Edit: Just as an afterthought, you can add additional lamba extensions like a where clause to the above one.

Categories

Resources