Clarification of interaction of Linq, EntityQuery, and RIA Services - c#

I have a general question about the interaction of linq with an entityquery and ria services. I know that within my domainconext I can define queries for my application. For example, I can use linq within my domaincontext to define the following query:
public IQueryable<User> GetUsersFriends(Guid usersID)
{
return (from f in this.ObjectContext.Friends join u in this.ObjectContext.Users on f.FriendUsersID equals u.ID where f.UsersID.Equals(usersID) select u);
}
This is all good. But my question is, can I perform the same type of query from the client side (silverlight app) ? So can I do build a linq statement against an EntityQuery object and return data from the database in this fashion?
I know that I can perform linq operations on the collections of entities that already have been loaded? Can I use linq on the client side to load the collections?
I am thinking that all queries that ultimately hit the database need to be defined in my domain context? Out of force of habit I keep trying to use linq to define new queries and pass those to the domaincontext.load() operation....which fails miserably. The domainconext probably doesn't have any way to marshal the queries across the line...right?
Is my understanding correct? If someone could help verify this for me, I would appreciate it.

One of the main advantages of IQueryable (and it's also a pretty good security risk), is that you CAN create a query on the client side, and serialize it back to the server for processing on the serve. The requirement here is that it must return the type that is defined (in your case IQueryable where User is the type that must returned in a collection that supports IEnumerable<>.
I took my example from Shawn Wildermuth (wildermuth.com), and tweaked it a little. This is what I use in my client side "model".
publicvoid PerformQuery<T>(EntityQuery<T> qry, EventHandler<EntityResultsArgs<T>> evt, object pUserState = null, bool NoRecordsThrow = False, LoadBehavior pLoadBehavior = LoadBehavior.MergeIntoCurrent ) where T : Entity
{
ModelDataContext.Load<T>(
qry,
pLoadBehavior,
r =>
{
if (evt != null)
{
try
{
if (r.HasError)
{
#if DEBUG
System.Diagnostics.Debugger.Break();
#endif
//internal class to record error messages
AppMessages.ErrorMessage.Display(string.Concat(r.Error.Message, Environment.NewLine, "------------------------", "------- Stack Trace ------", Environment.NewLine, r.Error.StackTrace));
}
else if (r.Entities.Count() > 0 || NoRecordsThrow)
evt(this, new EntityResultsArgs<T>(r.Entities, r.UserState));
}
catch (Exception ex)
{
#if DEBUG
System.Diagnostics.Debugger.Break();
#endif
evt(this, new EntityResultsArgs<T>(ex));
}
}
},
pUserState);
}
When I would then use it like this:
var UserQuery = <DomainServiceName>.Users.Where(pUser => pUsers.City == "Calgary");
PerformQuery<User>(UserQuery, UserQueryComplete)
The downside is that via this one endpoint, User data can be extracted with any kind of filter that is issued on the client side....
HTH,
Richard

Related

How could I get a Relative Entity's count using EF6

I have a Entity called "Client", and another Entity called "Card".
A Client may have many Cards.
My Client Entity looks like this:
public class Client{
public virtual ICollection<Card> Cards {get; set;}
}
Now I want to show the Client data in a DataGrid in WPF, and I want to get Cards Count data,so I add a property to Client Entity, which like this:
public class Client{
public virtual ICollection<Card> Cards {get; set;}
public int CardCount
{
return Cards.Count;
}
}
And then I query the data with Linq and Bind to view
var query = from n in db.Clients select n;
When I run the Application, I got a Exception just right on the return Cards.Count; line;
System.ObjectDisposedException
The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.
So how could I correctly get the cards count?
There is a way simpler method than the other answers here show. Please also realize that solutions such as
var client = db.Clients.FirstOrDefault(c=> c.Id = someid); //get a client
if (client != null)
{
cardCount = client.Cards.Count;
}
will cause an issue called Select N+1 problem. Read up on it if interested, but in a nutshell, it means the following:
Because you are not only interested in one exact client, but you want to display N clients, you need to do one (1) query to get just the clients. Then, by doing the FirstOrDefault stuff, you are actually doing one (1) extra db roundtrip to the database per Client record, which results in an additional N * 1 = N roundtrips. What this means that, if you were to just query the Clients without any related data, you could get however many client records you like, in just one query. But by fetching related data to each of them one-by-one, you are doing way too many db roundtrips.
Here is a way to solve this issue, by using joins and projections. You can get all the data you need in a single DB access:
using (var context = GetDbContext())
{
return context.Clients.Select(cli => new YourViewModel
{
Name = cli.FullName,
// Other prop setters go here
CardCount = cli.Cards.Count
}).Skip((page - 1) * pageSize).Take(pageSize).ToList();
}
You might be wondering, what's the difference afterall? Well, here, you are not working with materialized objects, as others call them here, but with a DbContext. By applying the proper LINQ operators to it (note, that this works not just with DbContext, but also with any IQueryable (well obviously not if you call AsQueryable() on an already in-memory collection but whatever)), LINQ to Entities can construct a proper SQL to join the tables and project the results and therefore you fetch all required data in one go. Note that LINQ to Entities IS ABLE to translate the cli.Cards.Count into a proper SQL Count statement.
You can get the count without loading the entities like this:
using (var context = new MyContext())
{
var client = context.Client.Find(clientId);
// Count how many cards the client has
var cardsCount = context.Entry(client)
.Collection(b => b.Cards)
.Query()
.Count();
}
More information on MSDN page.
You get an ObjectDisposedException if you do not materialize the retreived query. In the following case, the query gets executed only when you Access the first time the list from GetNonMaterialized and not before leaving the method. Fact of this the db is disposed because of lost of scope.
public IEnumerable<Client> GetNonMaterialized()
{
return from n in db.Clients select n;
}
In the following case the query is executed before leaving the method.
public IEnumerable<Client> GetMaterialized()
{
return (from n in db.Clients select n).ToList();
}
Always be sure that the query is executed before exiting the scope of a ObjectContext.
If you want to know whether the query is executed and when enalbe Logging of EF.
How can I turn off Entity Framework 6.1 logging?

How to stop EntityFramework from getting all rows

I tried to create a generic CreateOrUpdate function for EntityFramework where my classes/tables always have an ID field. Since that data is sent to a webpage and posted back to the server I am totally disconnected from the context and have to use the standard (set.Any / set.FirstOrDefault) way of checking to see if my object already exists.
The method works perfectly in terms of doing its job and creating/updating as required. But what I have found is that the db.Set<T>().FirstOrDefault(whereFunction); brings back all data from the DB and then performs the FirstOrDefault in memory. I would much prefer this to happen in SQL, but no matter what I have tried I can't get it to work.
Do you have any suggestions on how I might make the FirstOrDefault function translate to proper SQL so that I don't retrieve too much from the DB?
Also, I have tried First, Any & Count, all of which get all of the rows from the DB.
public void CreateOrUpdateEntity<T>(T entity) where T : class
{
using (var db = new ProjectContext())
{
Func<T, bool> whereFunction = m => m.As<dynamic>().ID == entity.As<dynamic>().ID;
var firstValue = db.Set<T>().FirstOrDefault(whereFunction);
if (firstValue == null)
{
db.Set<T>().Attach(entity);
db.ChangeTracker.Entries<T>().First(e => e.Entity == entity).State = EntityState.Added;
}
else
{
db.ChangeTracker.Entries<T>().First(e => e.Entity == entity).State = EntityState.Modified;
}
}
}
You should use expression instead of Func<T, bool> delegate. Usage of delegate invokes Enumerable.FirstOrDefault(Func<T,TResult>) method which is executed in memory (and requires all data loaded to client), instead of Queryable.FirstOrDefault(Expression<Func<T, TResult>>) which is translated into SQL and executed on server side.
NOTE: I have doubts about whether your delegate can be translated into SQL.
BTW: In order to get entity by id you can use method Find:
var firstValue = db.Set<T>().Find(entity.As<dynamic>().ID);

Multiple requirements of a query

not certain how to word this but here goes. I'm using linq to write the service layer for a server. I've already got my database setup and I've inherited classes from that using an Entity layer (which will be replaced with a WCF layer to correctly make use of JSON - a software requirement for this project). I am currently writing a method called "JoinCalendar" ( a major part of the application) where a I want to certify that a user is valid to join the calendar.
Here's the code for startes
public bool JoinCalendar(int famID, string email)
{
using (FamilySchedulerEntities db = new FamilySchedulerEntities())
{
var checkUser = (from u in db.Users
where u.familyID.Equals(famID) && !u.emailAddress.Equals(email, StringComparison.InvariantCultureIgnoreCase)
select u).FirstOrDefault();
if( checkuser != null){ return false}
else
{
//stuff
return true;
}
The aim of this method is take the familyID (the Identity field of a group of users) of the calendar the user is trying join and that users emailaddress (the unique ID for each user). I want to perform two different checks from that query.
Firstly: I want to check that the familyID is a valid one (ie. make sure the familyID is an actual value stored on the database)
Secondly: Check that email address of the user does not match a user who is registered to any family
Thirdly: Can I use the "!" operator in a linq query oris it bad practise/etiquette
kind regards,
Simon Johnson
PS. Only been using linq (and in fact writing a database application) for less than a week so please keep this newbie friendly. Ideally I need a super elegenat solution because the client to this server may be connected using a cellular (ie. not wifi) connection and I need to keep data transmission to an absolute minimum. If the best solution is hard then please explain it sloooow
Your two requirements could be expressed as:
bool familyExists = db.Users.Any(u => u.familyID == famID);
bool emailTakenAlready = db.Users.Any(u => u.emailAddress == email);
These seem to be independent requirements, so you can do separate queries for them.
You can certainly use a logical not (!) in Linq queries where it makes sense, it's just a matter of personal style.
You can simplify it like this:
public bool JoinCalendar(int famID, string email)
{
using (var db = new DataContext())
{
var exists = db.Users.Any(u => u.familyID == famID && u.emailAddress != email);
if (exists)
{
return false;
}
//stuff
return true;
}
}
It should also be most efficient as that query can be optimized on by the database.

How can I implement Query Interception in a LINQ to Entities query? (c#)

I'm trying to implement encrypted columns in EF4 and using the CTP5 features to allow simple use of POCO's to query the database. Sorry that this is a lot of words, but I hope the below gives enough to explain the need and the problem!
So, bit of background, and my progress so far:
The intention is that if you query the tables without using our DAL then the data is rubbish, but I don't want the developers to worry about if/when/how the data is encrypted.
For simplicity, at this stage I'm working on the assumption any string column will be encrypted.
Now, I have successfully implemented this for returning the data using the Objectmaterialized event, and for data commits using the SavingChanges event.
So given the following class:
public class Thing
{
public int ID { get; set; }
[Required]
public string Name { get; set; }
public DateTime Date { get; set; }
public string OtherString { get; set; }
}
The below query returns all the required values and the POCO materialized has clear data in it.
var things = from t in myDbContext.Things
select t;
where myDbContext.Things is a DbSet<Thing>
Likewise, passing an instance of Thing to Things.Add()
(with clear string data in the Name and/or OtherString values)
and then calling myDbContext.SaveChanges() encrypts the strings before it gets to the data store.
Now, the problem I have is in this query:
var things = from t in myDbContext.Things
where t.Name == "Hairbrush"
select t;
This results in the unencrypted value being compared to the encrypted value in the DB. Obviously I don't want to get all the records from the database, materialize them, and then filter the results based on any supplied Where clause... so what I need to do is: intercept that query and rewrite it by encrypting any strings in the Where clause.
So I've looked at:
writing a query provider, but that doesn't seem like the right solution... (is it?)
writing my own IQueryable wrapper for the DbSet which will capture the expression, run over it using an expression tree visitor and then forward the new expression to the DbSet...
Attempts at both have left me somewhat lost! I prefer the second solution i think since it feels a bit neater, and is probably clearer to other developers in future. But I'm happy to go with either or another better option!!
The main thing I am struggling with is when/how the LINQ expression is applied to the object... I think i've got myself a bit confused as to where the expression executes in the IQueryable object thus I'm not sure which method I need to implement in my wrapper to then grab and manipulate the expression being passed in...
I'm sure I'm missing something fairly obvious here and I'm waiting for that light bulb moment... but its not coming!!
Any help will be very gratefully received!
Thought I'd let you know what my final solution was.
In the end I have gone a wrapper class which implements a Where method, but without going to the extent of implementing IQueryable entirely. LINQ will still execute against the class (at least to the extent that I want/need it to) and will call the Where method with the expression from the LINQ.
I then traverse this ExpressionTree and replace my strings with encrypted values before forwarding the new expressiontree to the internal DbSet. and then returning the result.
Its pretty crude, and has its limitation, but works for our particular circumstance without problem.
Thanks,
Ben
you should use the QueryInterceptor attribute, search here in SO or in google and you find examples on how to use it.
a snippet:
[QueryInterceptor("Orders")]
public Expression<Func<Order, bool>> FilterOrders()
{
return o => o.Customer.Name == /* Current principal name. */;
}
// Insures that the user accessing the customer(s) has the appropriate
// rights as defined in the QueryRules object to access the customer
// resource(s).
[QueryInterceptor ("Customers")]
public Expression<Func<Customer, bool>> FilterCustomers()
{
return c => c.Name == /* Current principal name. */ &&
this.CurrentDataSource.QueryRules.Contains(
rule => rule.Name == c.Name &&
rule.CustomerAllowedToQuery == true
);
}
You can use David Fowler's Query Interceptor:
https://github.com/davidfowl/QueryInterceptor
One example of its use:
IQueryable q = ...;
IQueryable modifed = q.InterceptWith(new MyInterceptor());
And on class MyInterceptor:
protected override Expression VisitBinary(BinaryExpression node) {
if (node.NodeType == ExpressionType.Equal) {
// Change == to !=
return Expression.NotEqual(node.Left, node.Right);
}
return base.VisitBinary(node);
}

C# - Entity Framework - Understanding some basics

Model #1 - This model sits in a database on our Dev Server.
Model #1 http://content.screencast.com/users/Keith.Barrows/folders/Jing/media/bdb2b000-6e60-4af0-a7a1-2bb6b05d8bc1/Model1.png
Model #2 - This model sits in a database on our Prod Server and is updated each day by automatic feeds. alt text http://content.screencast.com/users/Keith.Barrows/folders/Jing/media/4260259f-bce6-43d5-9d2a-017bd9a980d4/Model2.png
I have written what should be some simple code to sync my feed (Model #2) into my working DB (Model #1). Please note this is prototype code and the models may not be as pretty as they should. Also, the entry into Model #1 for the feed link data (mainly ClientID) is a manual process at this point which is why I am writing this simple sync method.
private void SyncFeeds()
{
var sourceList = from a in _dbFeed.Auto where a.Active == true select a;
foreach (RivWorks.Model.NegotiationAutos.Auto source in sourceList)
{
var targetList = from a in _dbRiv.Product where a.alternateProductID == source.AutoID select a;
if (targetList.Count() > 0)
{
// UPDATE...
try
{
var product = targetList.First();
product.alternateProductID = source.AutoID;
product.isFromFeed = true;
product.isDeleted = false;
product.SKU = source.StockNumber;
_dbRiv.SaveChanges();
}
catch (Exception ex)
{
string m = ex.Message;
}
}
else
{
// INSERT...
try
{
long clientID = source.Client.ClientID;
var companyDetail = (from a in _dbRiv.AutoNegotiationDetails where a.ClientID == clientID select a).First();
var company = companyDetail.Company;
switch (companyDetail.FeedSourceTable.ToUpper())
{
case "AUTO":
var product = new RivWorks.Model.Negotiation.Product();
product.alternateProductID = source.AutoID;
product.isFromFeed = true;
product.isDeleted = false;
product.SKU = source.StockNumber;
company.Product.Add(product);
break;
}
_dbRiv.SaveChanges();
}
catch (Exception ex)
{
string m = ex.Message;
}
}
}
}
Now for the questions:
In Model #2, the class structure for Auto is missing ClientID (see red circled area). Now, everything I have learned, EF creates a child class of Client and I should be able to find the ClientID in the child class. Yet, when I run my code, source.Client is a NULL object. Am I expecting something that EF does not do? Is there a way to populate the child class correctly?
Why does EF hide the child entity ID (ClientID in this case) in the parent table? Is there any way to expose it?
What else sticks out like the proverbial sore thumb?
TIA
1) The reason you are seeing a null for source.Client is because related objects are not loaded until you request them, or they are otherwise loaded into the object context. The following will load them explicitly:
if (!source.ClientReference.IsLoaded)
{
source.ClientReference.Load();
}
However, this is sub-optimal when you have a list of more than one record, as it sends one database query per Load() call. A better alternative is to the Include() method in your initial query, to instruct the ORM to load the related entities you are interested in, so:
var sourceList = from a in _dbFeed.Auto .Include("Client") where a.Active == true select a;
An alternative third method is to use something call relationship fix-up, where if, in your example for instance, the related clients had been queried previously, they would still be in your object context. For example:
var clients = (from a in _dbFeed.Client select a).ToList();
The EF will then 'fix-up' the relationships so source.Client would not be null. Obviously this is only something you would do if you required a list of all clients for synching, so is not relevant for your specific example.
Always remember that objects are never loaded into the EF unless you request them!
2) The first version of the EF deliberately does not map foreign key fields to observable fields or properties. This is a good rundown on the matter. In EF4.0, I understand foreign keys will be exposed due to popular demand.
3) One issue you may run into is the number of database queries requesting Products or AutoNegotiationContacts may generate. As an alternative, consider loading them in bulk or with a join on your initial query.
It's also seen as good practice to use an object context for one 'operation', then dispose of it, rather than persisting them across requests. There is very little overhead in initialising one, so one object context per SychFeeds() is more appropriate. ObjectContext implements IDisposable, so you can instantiate it in a using block and wrap the method's contents in that, to ensure everything is cleaned up correctly once your changes are submitted.

Categories

Resources