Where clause with Linq in C# MVC 4 - c#

I'm trying to select a field that is from my UserProfile table, RoleID. The parameter passed into this Post method is Username and it is a string from a textbox which is working correctly.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult GetRoles(string UserName)
{
if (!string.IsNullOrWhiteSpace(UserName))
{
string applyfor = db.Profiles
.Select(s => s.RoleID)
.Where(a=>Profile.UserName.Contains(UserName))
.First();
ViewBag.ApplyingFor = applyfor;
However this gives me Sequence contains no elements.
I've tried several other methods, such as .Equals(). I'm pretty sure it is my where clause.
What am I doing wrong here?
Note: RoleID is not part of the Websecurity, also there is data in the database.

If you break down your code and highlight what each Lambda statement returns you'd see the issue:
string applyfor = db.Profiles
^^^^^^^^
This most likely returns something like DbSet<Profile>.
.Select(s => s.RoleID)
^^^^^^
This most likely returns IQueryable<int>. At this point you've lost the context of the profile and now only have zero or more RoleIDs.
So your a in the where statement is an int Value, you have no way to find a username now, and this where statement literally makes no sense.
.Where(a=>Profile.UserName.Contains(UserName))
When you rearrange the Lambda expressions as Grant Winney's Answer shows you can see why most of the time a Select() is the last thing that normally happens (in simple queries).
I would wager there is no UserName on Profile. and you want to
string applyfor = db.Profiles
.Where(p => p.User.Any(u.UserName == UserName))
.Select(p => p.RoleID)
.First();
As a side note, Microsoft Best practice is to Camel-Case method parameters. So I would recommend your method look like:
public ActionResult GetRoles(string userName) // or username
{
}

Your Where statement should probably look more like this:
... .Where(a => a.UserName == Profile.UserName).FirstOrDefault();

Try this instead:
string applyfor = db.Profiles
.Where(x => x.UserName == UserName)
.Select(x => x.RoleID)
.First();
Also, if there's a chance you won't find a matching record, use FirstOrDefault() instead of .First() and then test for null.

Related

LINQ to Entities does not recognize the method IsUserInCc

My Code:
elections = elections.Where(e => e.Creator == Username || e.Approver == Username || IsUserInCc(e.Cc,Username))
.OrderBy(e => e.Status)
.ThenByDescending(e => e.Group);
var test = elections.FirstOrDefault();
private bool IsUserInCc(string cc, string username)
{
var ccList = cc.Split(';');
if (ccList.Contains(username))
return true;
return LDAPUtility.Instance.IsUserInGroup(ccList.ToList(), username);
}
Error:
LINQ to Entities does not recognize method IsUserInCc.
From many posts, I can understand why error was thrown. Basically IsUserInCc is not available in SQL execution. I need somehow convert it back to C# to handle it.
LINQ to Entities does not recognize my method
LINQ to Entities does not recognize the method in query
LINQ to Entities does not recognize the method 'System.String ToString(Int32)'
However, in my specific case, what is the best approach?
You need to convert to list first. Also note that elections must be able to hold a list for this to run.
elections = elections.ToList().Where(e => e.Creator == Username || e.Approver == Username || IsUserInCc(e.Cc,Username))
.OrderBy(e => e.Status)
.ThenByDescending(e => e.Group);
For your function written in code, you cannot use that on Queryables. You need to convert to in-memory list and then apply the filter required using your function.
The root cause of your issue is that your underlying data isn't normalised properly. You need to put your CC's in a collection, not have them as a single deliniated string.
In SQL you'd need to add a new table called CC or something and put each user name in there and link it back to an election. Or if it's an in-memory collection, add a new property that in its Getter will do the split for you.
Either way, then you won't run into this kind of problem. If your data isn't properly structured, you will create problems for yourself further up the stack.
When you want to send request to databaseusing Linq like:
var query = listData.Where(x=>x.Id == 123);
Type of this query is IQueryable that means your query not Executed yet!
Now you are sending data as IQueryable to method and can not process on your data, you have to Execute that with methods like: Tolist(), ToListAsync() or something like these.
The best way for these is that you get data from database without that method, after that you execute your query, you can Run this method.
GoodLuck.
Can you try like this :
elections = elections.Where(e => e.Creator == Username || e.Approver == Username).Tolist().Where(e => IsUserInCc(e.Cc,Username))
.OrderBy(e => e.Status)
.ThenByDescending(e => e.Group);
var test = elections.FirstOrDefault();
private bool IsUserInCc(string cc, string username)
{
var ccList = cc.Split(';');
if (ccList.Contains(username))
return true;
return LDAPUtility.Instance.IsUserInGroup(ccList.ToList(), username);
}

Determine if record exists where two fields must match in MVC

I have a table (and a corresponding class) for this solution that has only two fields a UserId and a TicketId. Now I need to test if a record exists in this table where the UserId matches the logged in individual and TicketId matches the passed value. This is the code I tried:
bool subscriptionExists = db.TicketSubscriptions.Any(ts => ts.TicketId.Equals(db.Tickets.FirstOrDefault(t => t.TicketNumber == ticketNumber).TicketId))
.Where(ts => ts.UserId == db.Users.FirstOrDefault(u => u.NTUserName.Equals(User.Identity.Name)).UserId);
However, I am getting the following error:
'bool' does not contain an extension method for 'where' and no
extension method 'where' accepting a first argument of type 'bool'
could be found (are you missing a using directive or an assembly
reference?
Am I on the right track? or is there a much better way to test this?
You are using Where on a bool value instead of a List. Any() returns bool. You can try like following. Hope this will help you.
var ticketId=db.Tickets.FirstOrDefault(t =>t.TicketNumber==ticketNumber).TicketId;
var userId=db.Users.FirstOrDefault(u=>u.NTUserName.Equals(User.Identity.Name)).UserId;
bool subscriptionExists = db.TicketSubscriptions.
Any(ts => ts.TicketId.Equals(ticketId) && ts.UserId == userId);
You're calling the .Where on the result of the .Any, which is a boolean value. Try combining your clauses and then calling .Any on the whole thing:
bool subscriptionExists = db.TicketSubscriptions.Any(
ts => ts.TicketId.Equals(db.Tickets.FirstOrDefault(t => t.TicketNumber == ticketNumber).TicketId) &&
ts.UserId == db.Users.FirstOrDefault(u => u.NTUserName.Equals(User.Identity.Name)).UserId);
TicketSubscriptions.Any() returns a Bool.
It is result is a True or False. So you cannot continue to query with a where. Write the code more readable. You can see mistakes easier. In any() function you need a boolean statement like if statements. You can use || or && signs simply.
var userId = User.Identity.Name;
var passedValue = ... // I dont know if it is from a parameter.
bool subscriptionExists = TicketSubscriptions.Any(ts => ts.NTUserName == userId && ts.TicketId == passedValue );
your problem is that Any() returns a bool, so you can't call Where() on it.
Where, returns another IEnumerable, so if you put Where before Any(), you won't have that problem.
You can also put both conditions inside the Any() I.E. someEnumerable.Any(/*condition1*/ && /*condition1*/);
on another note, I'd make use of Navigation Properties if I were you. You can google search for "navigation properties" and the name of whatever database framework you're using(like entity framework or linq to sql) to find more information.
That will let you do things like
subscriptionExists = db.TicketSubscriptions
.Any(ts => ts.Ticket.TicketNumber == ticketNumber
&& ts.User.NTUserName == User.Identity.Name);
on yet another note, I believe that User.Identity is going to have the user id, so you don't even have to bother accessing the users table like that. Intellisence can help you find out how to access that.
Complementing the #Azim answer
When you use .FirstOrDefault must verify returned values. Otherwise if you have sure that the return value never is null must use .First
var ticketId=db.Tickets.FirstOrDefault(t =>t.TicketNumber==ticketNumber).TicketId;
varuserId=db.Users.FirstOrDefault(u=>u.NTUserName.Equals(User.Identity.Name)).UserId;
if(ticketId != null && userId != null)
{
bool subscriptionExists = db.TicketSubscriptions.
Any(ts => ts.TicketId.Equals(ticketId) && ts.UserId == userId)
}

Input string was not in a correct format error, but still able to get parsed value from LINQ to XML query

I have an xml file from which I go get IDs from certain nodes. The nodes have the following structure:
<Bistot ID="1223"/>
<Compressed_Bistot ID="28388"/>
<Compressed_Monoclinic_Bistot ID="28389"/>
...
They are also at different levels in my xml file (it is too big to list it all here).
In order to do this, I use a function that executes a LINQ query:
public String GetItemName(uint id)
{
IEnumerable<String> names = _xmlFile.Descendants()
.Where(x => x.Attributes().Any(a => a.Name.LocalName == "ID") && uint.Parse(x.Attributes().First(a => a.Name.LocalName == "ID").Value) == id)
.Select(t => t.Name.LocalName);
return names.ElementAt(0);
}
In this form, it works well if the id I provide exists and the names variable actually has an entry. If it doesn't though, it raises an exception at return names.ElementAt(0); because there is no elements at index 0. At this point, in debug mode, if I go check the variable names, I do see it mentioned that Input string was not in a correct format, but no error is raised and the function returns the good name associated to id.
I have also tried the following form in order to remove the exception that occurs if the LINQ request doesn't find any associated names in the xml file:
public String GetItemName(uint id)
{
IEnumerable<String> names = _xmlFile.Descendants()
.Where(x => x.Attributes().Any(a => a.Name.LocalName == "ID") && uint.Parse(x.Attributes().First(a => a.Name.LocalName == "ID").Value) == id)
.Select(t => t.Name.LocalName);
if (names.Count() != 0) // Error raised here
{
return names.ElementAt(0);
}
else
{
return "";
}
}
This is where my problem occurs, since in this case, when I call names.Count(), the Input string was not in a correct format(of type FormatException) error is raised and my application then does not continue/complete.
I have also tried to call .ToList() at the end of my LINQ query as I had a warning telling me to do so. But in this case, the Input string was not in a correct format error is raised inside my LINQ query directly, and not at the names.Count() call.
I do understand the nature of this error, something is not getting parsed right when I call uint.Parse(x.Attributes().First(a => a.Name.LocalName == "ID").Value) inside my LINQ query, but I don't understand why for two reasons:
I have manually checked and each values in the ID attribute in my xml file are real uint (just numbers, no spaces, etc).
When not using names.Count() or .ToList(), while it does mention the error in debug mode when I look at the variable, the name associated to the id is still found and the function returns it properly.
Why is my uint.Parse(...) giving me troubles here?
The issue is that when you use ElementAt(0), it only needs to find the first element - so if there's an ID element later with an invalid value (which I'm sure there is), you're not going to hit it. When you call Count() or ToList(), it iterates over the whole of the query, causing the problem.
As it is, your code is very inefficient though, evaluating multiple times - it would be much better written as:
public String GetItemName(uint id)
{
IEnumerable<String> names = ...; // Query as before
return names.FirstOrDefault() ?? "";
}
I'd also recommend using the explicit conversions provided by LINQ to XML instead of manually calling uint.Parse, too:
IEnumerable<String> names = _xmlFile.Descendants()
.Where(x => (uint?) x.Attributes()
.Where(a => a.Name.LocalName == "ID")
.FirstOrDefault() == id)
.Select(t => t.Name.LocalName);
Here we're finding the first ID attribute, converting it to uint? (giving null if there's no such attribute) and then comparing that with id.

Get ICollection property of generic type

I'm new in ASP.NET MVC 5, I need get the Icollection property and search into other property, if printed the result of the first search is fine, but when i go to search in the result is null.
what is the problem?
var userId = User.Identity.GetUserId();
var user = db.Users.Include( u=> u.Sucursales)
.Where(u => u.Id == userId)
.SingleOrDefault();
if( user != null )
{
var sucursal = user.Sucursales.Include(s => s.Emisor)
.Where(s => s.ID == suc)
.SingleOrDefault();
if (sucursal != null)
{
var tipoCfe = sucursal.Emisor.TiposCfe
.Where(t => t.ID == factura)
.SingleOrDefault();
Your query will take place right away since you are using SingleOrDefault(), see this StackOverflow question pertaining to SingleOrDefault() Your Include(s => s.Emisor) sticks out to me though. Since Emisor wasn't included when fetching the user, you will not be able to request that since your query is no longer of type IQueryable. Your query has already been executed.
In order to retrieve the data you require, you will have to obtain the data during your first query. I would do something similar to: db.Users.Include("Sucursales.Emisor") when you retrieve the user.
More on include method... MSDN Explanation of Include Method
I changed
var user = db.Users.Include( u=> u.Sucursales)
for
var user = db.Users.Include("Sucursales.Emisor")

Why does Select return a boolean?

I'm working with Entity Framework 6 in MVC 5.
I have the following method:
[HttpPost]
public ActionResult UpdateDetails(ApplicationUser applicationUser)
{
var context = new ApplicationDbContext();
var user = context.Users.Select(x => x.UserName == applicationUser.UserName).FirstOrDefault();
//etc etc
}
Users is an IDbSet<ApplicationUser>.
Why am I getting a bool back from the Select method?
My expectation would be to get an ApplicationUser object back. Why isn't this the case?
Thanks
Select() projects an element of a sequence. Since x.UserName == applicationUser.UserName returns a bool, the result of the method will be a boolean.
What you want requires the Where method. This filters the sequence based on the specified predicate:
var user = context.Users.Where(x => x.UserName == applicationUser.UserName).FirstOrDefault();
Which can be shortened to:
var user = context.Users.FirstOrDefault(x => x.UserName == applicationUser.UserName);
This is possible, since this overload of FirstOrDefault() takes a filter predicate as second parameter.
Your select is returning an object that is the result of the comparison.
Change to:
var user = context.Users.FirstOrDefault(x => x.UserName == applicationUser.UserName);
It is because you are SELECTing a boolean.
Since there is no requirement specified, I am guessing your requirement from your attempted code.
use the below line.
var user=context.Users.Where(user=>user.UserName==applicationUser.UserName).FirstOrDefault();
Select literally selects something inside the arguments. So, if you have an expression that returns a bool, Select will return bool.

Categories