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.
Related
So what I was trying was to fetch only those columns from table which has to be updated and I tried it as below:
var user = (from u in context.tbl_user where u.e_Id == model.Id select
new { u.first_name, u.last_name, u.role, u.email, u.account_locked })
.FirstOrDefault();
But when I tried to assign new value to the fetched data as below
user.first_name = model.FirstName;
I saw below error getting displayed
Property or indexer 'anonymous type: string first_name, string
last_name, string role, string email, bool account_locked.first_name'
cannot be assigned to -- it is read only
But when I retrieved all the values from table without filtering as below it worked fine.
var user = (from u in context.tbl_user where u.e_Id == model.Id select u).FirstOrDefault();
Why it doesn't work for first query. I've read in many sites that it is good to retrieve only required properties from database in terms of performance and security. But I am really not able to understand what's wrong with the first approach I opted. Any explanations are much appreciated.
Update
Are there any other ways to fetch only required column and update them and store them back?
Anonymous Types properties are read-only so you can not change them.
Stop doing micro-optimizing or premature-optimization on your code. Try to write code that performs correctly, then if you face a performance problem later then profile your application and see where is the problem. If you have a piece of code which have performance problem due to finding the shortest and longest string then start to optimize this part.
We should forget about small efficiencies, say about 97% of the time:
premature optimization is the root of all evil. Yet we should not pass
up our opportunities in that critical 3% - Donald Knuth
If you want to just fetch specific columns you can create a custom class and fill the properties in your query like others have mentioned.
As others said anonymous type is read only, for achieving what you want, you will have to create a type for it with properties that are required:
public class User
{
public string FirstName {get;set;}
public string LastName {get;set;}
.....................
.....................
}
and then, you have to use it in your linq query:
var user = (from u in context.tbl_user
where u.e_Id == model.Id
select new User
{
FirstName = u.first_name,
LastName = u.last_name,
......................,
.....................
}).FirstOrDefault();
Anonymous types are read-only by design.
In order to retrieve something that you can edit, you have to create a class and select your entities into that class, as done here: How to use LINQ to select into an object?
Or here:
var user = (from u in context.tbl_user where u.e_Id == model.Id select
new User_Mini { u.first_name, u.last_name, u.role, u.email, u.account_locked })
.FirstOrDefault();
Note: you won't be able to call context.SubmitChnages() when editing this new object. You could do something like this though: LINQ to SQL: how to update the only field without retrieving whole entity
This will allow you to update only certain parts of the object.
I've been learning C# / LINQ / ASP.NET / MVC 3 / EF for a few months now comming from Java / Icefaces / Ibatis base (Real world uses .NET D;). I really enjoy LINQ / Entity Framework from the .NET Framework but I'm having a few issues understand what's really happening behind the scenes.
Here's my problem:
I'm using a AJAX / JSON fed jQuery datatable (that I highly recommend to anyone in need of a free web datatable system by the way). I have a method in my MVC3 application that returns a JSON result of the data needed by the table, doing the sorting and all. Everything is working nicely and smoothly. However, I'm having a concern with the "dirty" hack I had to do to make this work.
Here's the complete code:
//inEntities is the Entity Framework Database Context
//It includes the following entities:
// Poincon
// Horaire
// HoraireDetail
//Poincon, Horaire and HoraireDetail are "decorated" using the Metadata technic which
//adds properties methods and such to the Entity (Like getEmploye which you will see in
//the following snippet)
//
//The Entity Employe is not a database data and therefor not handled by the EF.
//Instead, it is a simple object with properties that applies Lazy Loading to get an
//Employe Name based off of his Employe ID in the Active Directory. An employe object
//can be constructed with his Employe ID which will expose the possibility of getting
//the Employe Name from the AD if needed.
[HttpPost]
public JsonResult List(FormCollection form)
{
String sEcho;
int iDisplayStart;
int iDisplayLength;
String sSearch;
int iSortingCols;
Dictionary<String, String> sorting;
try
{
sEcho = form["sEcho"];
iDisplayStart = int.Parse(form["iDisplayStart"]);
iDisplayLength = int.Parse(form["iDisplayLength"]);
sSearch = form["sSearch"];
iSortingCols = int.Parse(form["iSortingCols"]);
sorting = new Dictionary<string,string>();
for (int i = 0; i < iSortingCols; i++)
sorting.Add(form["mDataProp_" + form["iSortCol_" + i]].ToUpper(), form["sSortDir_" + i].ToUpper());
}
catch
{
HttpContext.Response.StatusCode = 500;
return null;
}
var qPoincon = inEntities.Poincons.AsEnumerable();
var lPoincon = qPoincon.Select(o => new
{
o.id,
emp = o.getEmploye(),
o.poinconStart,
o.poinconEnd,
o.commentaire,
o.codeExceptions
}).AsEnumerable();
//Search
lPoincon = lPoincon.Where(p => (p.emp.empNoStr.Contains(sSearch) || p.emp.empNom.Contains(sSearch) || (p.commentaire != null && p.commentaire.Contains(sSearch))));
//Keep count
int iTotalDisplayRecords = lPoincon.Count();
//Sorting
foreach(KeyValuePair<String,String> col in sorting)
{
switch (col.Key)
{
case "EMPNO":
if (col.Value == "ASC")
lPoincon = lPoincon.OrderBy(h => h.emp.empNo);
else
lPoincon = lPoincon.OrderByDescending(h => h.emp.empNo);
break;
case "POINCONSTART":
if (col.Value == "ASC")
lPoincon = lPoincon.OrderBy(h => h.poinconStart);
else
lPoincon = lPoincon.OrderByDescending(h => h.poinconStart);
break;
case "POINCONEND":
if (col.Value == "ASC")
lPoincon = lPoincon.OrderBy(h => h.poinconEnd);
else
lPoincon = lPoincon.OrderByDescending(h => h.poinconEnd);
break;
case "COMMENTAIRE":
if (col.Value == "ASC")
lPoincon = lPoincon.OrderBy(h => h.commentaire);
else
lPoincon = lPoincon.OrderByDescending(h => h.commentaire);
break;
}
}
//Paging
lPoincon = lPoincon.Skip(iDisplayStart).Take(iDisplayLength);
//Building Response
var jdt = new
{
iTotalDisplayRecords = iTotalDisplayRecords,
iTotalRecords = inEntities.Poincons.Count(),
sEcho = sEcho,
aaData = lPoincon
};
return Json(jdt);
}
As you can see, when I'm grabbing the entire list of "Poincons" from the EF and turning it into a Enumerable. From my current understanding, turning the LINQ query into a Enumerable "kills" the link to the EF, or in other words, will generate the SQL required to get that list at that point instead of keeping the LINQ data until the end and execute a percise query that will return only the data you require. After turning this LINQ Query into a Enumerable, I'm heavily filtering the LINQ (since there is paging, sorting, searching in the datatable). This leads me to thinkg that what my code is currently doing is "Grab all the "Poincons" from the database and put it into the web server's memory as a Enumerable, do your work with the Enumerable then serialize the result as a JSON string and send it to the client.
If I'm correct, the performance hit is quite heavy when you hit the couple thousand of entries (which will happen quite fast once in production... everytime an employe comes to work, it will add 1 entry. 100 employes, ~300 work days a year, you get the idea).
The reason for this hack is that the EF does not know what "getEmploye" method of "Poincon" is, therefor throwing an exception at runtime similar to this:
LINQ to Entities ne reconnaît pas la méthode « PortailNorclair.Models.Employe getEmploye() », et cette dernière ne peut pas être traduite en expression de magasin.
Approximated traduction (If anyone can let me know in a comment how to configure IIS / ASP.NET to display errors in english while keeping the globalization in a foreign language, I would be really grateful. French information about error messages is sometimes lacking):
LINQ to Entity does not recognize the method " PortailNorclair.Models.Employe getEmploye()" and the following could not be translated to a SQL expression.
The "getEmploye" method instances and returns a Employe object with the employe id found in the Poincon object. That Employe object has properties that "lazy loads" information like the employe name from the Active Directory.
So the question is: How can I avoid the performance hit from using .AsEnumerable() on the non-filtered list of objects?
Thanks a lot!
The "getEmploye" method instances and returns a Employe object with
the employe id found in the Poincon object. That Employe object has
properties that "lazy loads" information like the employe name from
the Active Directory.
You should be storing the Employee Name in the database, so you can then order, sort, skip and take in your Linq Query without having to load every employee object.
If empNoStr, empNom, and empNo were all in the database, you could retrieve just the records you want, and call getEmploye() (loading whatever else you need from active directory, or wherever) for each of those.
There are some classes on which your program performs its main work.
There are other classes which represent to database rows.
If you keep them separated, you can also separate actions you intend to occur in the database from actions you intend to perform locally. This makes it trivial to avoid loading the full table, when specific rows are required.
I see you're also doing Paging locally, while the database can do that and save your webserver some memory.
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
I have a User entity in my entity model:
Username and Email should be unique but for now EF4 doesn't support this.
So I wrote this code to ensure uniqueness :
public void CreateNewUser(string i_UserName, string i_Email)
{
using (ModelContainer context = new ModelContainer())
{
User usr;
usr = context.UserSet.Where(u => u.Username == i_UserName).SingleOrDefault();
if (usr != null)
throw new Exception("Username not unique");
usr = context.UserSet.Where(u => u.Email == i_Email).SingleOrDefault();
if (usr != null)
throw new Exception("Email not unique");
context.UserSet.AddObject(new User() { Username = i_UserName, Email = i_Email });
context.SaveChanges();
}
}
If this is right approach, do I have way to automatically preform this code whenever context.UserSet.AddObject() is called? Or a more elegant way does exist?
I don't know of a more elegant way. Rather than using SingleOrDefault, I think you want something like:
bool isUniqueUserName = !context.UserSet.Any(u => u.Username == i_UserName);
for performance. Any() stops at the first match and doesn't have to enumerate the entire sequence.
Correct way is defining Unique index in database and catching an exception. These checks in code must be much more complex then simple: say me if the user already exists. First of all this is not only problem of insert but user can also usually modify his email. Another problem is concurrency - in very corner case you can have two users inserting same records in the same time. In both threads test queries can return that user name and email are not registered but during the saving one thread will get an exception (if you have unique index in db) or duplicit record will be created. If you want to avoid it you must lock your database records and lock table for insertion (serializable transaction). Such operation can decrease throughput of your application.
Another fact is that you will do 2 additional queries before each insert and at least one query before each update.
I use the same approach. Just ensure that you have one method that insert it and you should be find. Similar to factory method pattern.
Branching off of #ysrb's answer (having a single method that does the User insert), a more foolproof solution might be to hook into the ObjectContext.SavingChanges event and do your test there if a User entity is being saved. This way you can be sure your logic always fires.
Example:
IEnumerable<ObjectStateEntry> changes = this.ObjectStateManager.GetObjectStateEntries(EntityState.Added);
foreach (ObjectStateEntry stateEntryEntity in changes)
{
if (!stateEntryEntity.IsRelationship && stateEntryEntity.Entity != null)
{
if (stateEntryEntity.Entity is User)
{
//Do you work here
}
}
}
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.