I am creating an ASP website with a possibility to register.
The nickname that visitors choose to register has to be unique.
Everytime when an user registers, I select all users from the database, and then I am using a foreach loop to check or username already exists:
private List<User> listExistingUsers;
listExistingUsers = Database.GetUsers();
foreach (User u in listExistingUsers)
{
if (u.Nickname == txtNickname.text)
{
Error = "Username already in use.";
}
}
But the code above doesn't work properly. It doesn't check all the items in the list which are read from the database. So it is possible to have users with the same usernames, which I don't want.
What can I do to solve this problem? I read about LINQ, but I think that this is the wrong way of checking usernames with List<> in my opinion. I think this username-check must be done in another way.
Can you experts help me? I could also do this check with a SQL-query, but I would like to do it in c#.
Instead of returning ALL users from DB, pass username to Query/stored procedure and let backend do the check, and then return back just a status flag 1/0 - exists/doesn't.
if (Database.GetUsers().Select(x => x.Nickname).Contains(txtNickname.Text)) should do what you want.
I've condensed everything into a single line so I'll give a quick explanation; First I use your Database.GetUsers() method to retrieve the users, then I use select to project the Nickname since that's what we're comparing. If that were to execute on it's own it would result in an IEnumerable<string> with all of the Nicknames. From there I use contains to see if that list contains the nickname that (I'm assuming) has been entered in the UI.
You can use Contains operator in order tocheck
listExistingUsers.Select(x => x.Nickname).Contains(txtNickname.text);
link : http://msdn.microsoft.com/fr-fr/library/bhkz42b3%28v=vs.80%29.aspx
Remark : You can use Any or count (very expensive last solution)
Use Any operator. It checks whether any element of a sequence satisfies some condition. In your case condition is user nickname equals to text in textBox:
if (Database.GetUsers().Any(u => u.Nickname == txtNickname.Text))
Error = "Username already in use.";
BTW if you change GetUsers to return IQueryable<User> then check will occur on server side.
Do get a list of NickNames once
var nickNames = new List<string>();
for(int i=0;i<listExistingUsers.Count;i++)
{
nickNames.Add(listExistingUsers.NickName);
}
Then u can simply use
if(nickNames.Contains(txtNickname.text))
{
Error = "Username already in use.";
}
1) Have you verified that Database.GetUsers() is actually returning the full list, with no SQL issues?
2) Do you need it to be case-insensitive?
3) You can use the LINQ to do the query like this:
if (listExistingUsers.Any(u => string.Equals(u, txtNickname.Text, StringComparison.CurrentCultureIgnoreCase)))
{
// Process error
}
If Database.GetUsers() return all the users from database, so do not use it! Imagine if you have already 1000 of users, for each new user it will load all the users, and you will have performance issues.
Instead, create a new method that search your database and return only one result, case it exists.
Something like :
private bool Database.UserExists(txtNickname.text) {
//Your query to database with a where statment looking for the nickname. It could be a LINQ query, or any other way you use in your system.
//If it brings 1 result, it has to return true.
}
I think the most tricky part of your task is to fill the database Correctly.
Particularly:
Cut off trailing and ending spaces
Decide if the user names should becase sensitive
Make sure that when creating a new user name you do not have the nick already
About Loading users and checking:
As mentioned above LINQ is the most effective a C# like checking for duplicates
( if (Database.GetUsers().Select(x => x.Nickname).Contains(txtNickname.Text)))
I am more used to writing SQL statements than using LINQ. If you've got lots of users SQL will read only the selected ones but I don't know if the LINQ statement above pulls all users into the memory pool or just the one(s) with the same nickname.
Related
Please tell me how to solve this problem.
Here is an example table:
Filled with data like this:
I have a method to automatically assign a number to the CerNambe column:
public string CerNamber()
{
var cer = DBContext.Certificate
.Where(p => p.CerNambe != null)
.OrderByDescending(p => p.Id)
.FirstOrDefault();
string _cer = cer.CerNambe;
int outCer = Convert.ToInt32(_cer.Substring(0, _cer.IndexOf('-')));
string newCer = Convert.ToString(outCer + 1 + "-P/" + DateTime.Now.Year);
return newCer;
}
But I ran into a problem. If the number is assigned by the user erroneously, it is deleted, and the numbering is violated.
Here is the question: how to find this violation and assign the missing number to the next record. But in this case, automatic numbering will not work? Since the method is looking for the last record!
How to find this violation and assign the missing number to the next record ?
If I were you, I would modify the database schema so that I don't have to do this. But if you have to, the easiest yet ugliest way is to use RegEx to filter and find special patterns.
The solution: If your underlying database provider is SQL Server, you could use SqlMethods.Like to filter the database result set down to a manageable subset of data that can then analyze locally with RegEx.
Linq to Entities doesn't support regex because it can't convert it to SQL. You could load all of the records back into memory and then use Linq to Objects on them (perform a. ToList() to a variable, then a second LINQ query for this regex), but this meant you'll get to load every DB record into memory to run this query.
The same advice applies: modify the database schema so that you don't have to do this.
For understanding RegEx visit :
System.Text.RegularExpressions.Regex Class
So what I am trying to do is write code that will pick an appropriate personnel based on the data that I have to submit a license to a district board. This code is obfuscated from my actual codebase so please bear with the student/class example.
The logic should say, if there was no teacher present, or teacher has no license then use the state school directors license. if we cant find that, use the national.
The fall back works fine when I exclude the state director from the query, it runs without problem. However, when I try to include this, it throws a 'System.InvalidOperationException'. I have verified that all students have a current address and an appropriate state exists in the dictionary.
It looks like my current syntax of using a dictionary in an iqueryable is invalid and EF doesnt know how to work with a dictionary!
var qry = from student in FilteredContext // type is IQueryable<Student>
let studentsAddress = student.Addresses.FirstOrDefault(x=>x.IsCurrent)
let instructor = lastClass == null ? : lastClass.Instructor
let stateSchoolDirector = _personnelContext.Context.FirstOrDefault(x=>x.ID == SchoolDirectors.ByState[currentAddress.State])
let nationalSchoolDirector = _personnelContext.Context.FirstOrDefault(x => x.ID == SchoolDirectors.NationalSchoolDirector)
let personnelForLicense = instructor ?? stateSchoolDirector ?? national
select personnelForLicense.License;
var results = qry.ToList();
So my question is, is there any way to represent a dictionary or something like it within iqueryable? If not, are there any reasonable work arounds? I am totally stuck here and despite seraching could not easily find anything like it on StackOverflow or google results. The closest suggestion I could find was telling me to convert to .AsEnumerable() which I fail to see how it will work with my let statements
Just for going full circle, the only option that I could think of was to convert the dictionary into an actual SQL table/entity on the context
We've been having a problem where the following method which queries a raven db works, but only about 90% of the time
member.UserId = userService.GivenUsernameGetUserId(command.EmailAddress.ToLower());
to counteract this, I made this ugly hack workaround which does seem to have fixed the problem:
member.UserId = userService.GivenUsernameGetUserId(command.EmailAddress.ToLower());
System.Threading.Thread.Sleep(1000);
if (member.UserId.IsNullOrEmpty())
{
logger.Error("the userid was not loaded in time");
for (int i = 0; i < 5; i++)
{
member.UserId = userService.GivenUsernameGetUserId(command.EmailAddress.ToLower());
System.Threading.Thread.Sleep(1000);
if (member.UserId.IsNotNullOrEmpty())
{
logger.Info("The userid was retrieved in a loop after some delay ");
break;
}
}
if (member.UserId.IsNullOrEmpty())
{
logger.Error("a loop of 5 cycles was run trying to retrieve the userId but couldn't get it.");
}
}
Can anyone see why it might only be retrieving the correct data sometimes and whether there's a more elegant solution to making sure it keeps trying until it retrieves the data? I'm thinking whether there's some basic timeout setting that can be set in web.config or something?
The issue is likely stale indexes: the user has been recently created, and the indexes haven't had a chance to update. (Usually this takes milliseconds, but on a large database it can take longer.)
There are 3 things you can do here to fix your problem:
Option 1: Make User Ids based on email address. Then you don't have to mess with indexes at all.
Option 2: You can leave user IDs as-is, but wait for non-stale indexes.
Option 3: When you create a user, wait for indexes to update.
I'll describe each of these options below:
Option 1:
Make your user IDs well-known, so that you don't have to user indexes at all.
Say your object is called User. When you register the User, your code will look like:
public void RegisterUser(string emailAddress)
{
var user = new User
{
UserName = emailAddress,
...
};
// Give the User a well-known ID, so that we don't have to mess with indexes later.
user.Id = "Users/" + emailAddress;
ravenSession.Store(user);
}
If you do that, you won't have to mess with indexes at all. When it comes time to load your user:
public string GivenUsernameGetUserId(string userName)
{
// Look ma, no query needed.
return "Users/" + userName;
// Or, need to return the User itself? You can use .Load, which will never be stale.
// return ravenSession.Load<User>("Users/" + userName);
}
This is really your best option, and you never have to deal with indexes, therefore, you'll never have to deal with stale data.
Option 2
Option 2 is to use .WaitForNonStaleResults. It waits for indexes to become up-to-date before returning results.
public string GivenUsernameGetUserId(string userName)
{
// Use .WaitForNonStaleResultsAsOfNow()
return ravenSession.Query<User>()
.Customize(x => x.WaitForNonStaleResultsAsOfNow())
.Where(u => u.UserName == userName)
.Select(u => u.Id)
.FirstOrDefault();
}
Option 3
Option 3 is to wait for indexes to update when saving your user.
This requires Raven 3.5 or greater.
public void RegisterUser(string userName)
{
ravenSession.Advanced.WaitForIndexesAfterSaveChanges(timeout: TimeSpan.FromSeconds(30));
var user = new User {...};
ravenSession.Store(user);
ravenSession.SaveChanges(); // This won't return until the User is stored *AND* the indexes are updated.
};
Personally, I'd recommend using #1: well-known IDs for your users. Also, I recommend #3 even if you implement other solutions: SaveChanges will wait for the indexes to be updated before returning. This will result in fewer surprises around stale indexes, so I recommend it generally.
I am using Raven DB 3.5 and using mentioned Option 3
This does work but I encountered a problem using this approach:
In a specific use case this operation would take about 60 seconds to complete.
I'd not recommend to make general use of WaitForIndexesAfterSaveChanges() as it obviously can lead to tremendous performance issues.
Instead I'd configure queries using WaitForNonStaleResultsAsOfNow().
I am working on a system that the client decided to use status for the records. One of them is X for excluded. What I want to know is if it is possible to run linq queries that adds something like
where status != 'X'
Automatically to not show "excluded" records. Thanks !
Sort of. Queries in Linq are lazily-evaluated, so you can append conditions to them as you like before actually fetching the first result and it'll still result in "optimal" SQL being used.
For example:
// an extension method on the LINQ context:
public static IQueryable<Story> FilteredStories(this DbContext db)
{
return from story in db.Stories where status != "X" select story;
}
// ...later...
var stories = from story in db.FilteredStories()
where title = "Something"
select story;
foreach(var story in stories)
{
// whatever...
}
You could also "hide" the underlying LINQ context and always go through a wrapper class that appends the status != "X" condition. Of course, then problem with that is then you'd have to jump through hoops if you didn't want a filtered list...
What you might be after is Dynamic LINQ. It's a helper library that you can use to parse LINQ expressions from strings.
LINQ is so easy that writing the question practically gives you the answer!
where status != "X"
My preference would be
.Where(record => record.status != "X")
which does exactly the same thing. The only thing you missed is double quotes because it's a string, not a char.
Follow up question to this:
Linq Combine Left Join Data
Say I have the following db tables:
Users
-------
UserId (PK)
UserName
Roles
-----
RoleId (PK)
RoleName
UserRoles
---------
UserId (PK)
RoleId (PK)
Users 1-M UserRoles M-1 Roles
Using LinqToSQL, I can return the following set (thanks to response from prior question):
[User1], [Role1, Role2, Role3]
[User2], [Role2, Role3]
[User3], [Role3]
The twist is I am trying to sort by Roles. How can I sort the result by Roles?
Clarification
I have a grid, when the user clicks on the Roles column, the rows would be sorted by that column.
So to start the grid would look like this:
[User1], [Role1, Role2, Role3]
[User2], [Role2, Role3]
[User3], [Role3]
Then if they sort on Roles column it would look like this
[User3], [Role3]
[User2], [Role2, Role3]
[User1], [Role1, Role2, Role3]
Just change the original answer very slightly:
from u in dataContext.Users
select new { User = u, Roles = u.UserRoles.Select(ur => ur.Role)
.OrderBy(r => r.RoleName) };
(That's assuming you want to sort each element of the result by the roles it contains. If that's not correct, please explain what you want in more detail.)
Could you not simpy use something like this?
// users is the collection you already got from Linq2Sql
var usersSorted = from u in users order by u.Roles select u;
int ascending = 1; //set to -1 for descending
from u in Users
orderby u.Roles.Count * ascending
select new { u, u.Roles.OrderBy(x => x.RoleName) }
Your query will need to cater for the many to many though (not shown).
Hey #zzz, the answers I've seen so far seem to indicate how to sort the rows for each user, whereas, if I understand your clarification, you do want that, but what you're asking for is how to then sort those statements alphabetically. I'll try to provide an answer to that question.
Though your request is very common, regrettably, SQL does not have a native way to convert a table (the Roles column) to a comma delimited string. This normally isn't a problem because you can simply return the Roles field as
{
UserName = u.UserName,
RolesList = string.Join(", ",
u.UserRoles.Select(ur => ur.Role.RoleName).ToArray())
}
This will work, surprisingly, even though I just mentioned that there is no equivalent function to string.Join in SQL. That's because LINQ is smart enough to just have SQL return the table and to apply the string.Join() at the last minute, in memory.
The problem comes when you then try to sort by the RoleList field as it is created in memory and not in SQL. If you do you'll get the following error message.
NotSupportedException: Method
'System.String Join(System.String,
System.String[])' has no supported
translation to SQL.
This leaves you with two choices:
Write a stored procedure to do this that utilizes a custom function to convert a table to a comma separated list.
OR bring the entire result set back into memory by returning it as .ToList() and then performing the sort ie (/my whole query/).ToList().OrderBy(q => q.Roles);
The second option will be much slower if you have a large dataset. If you user list is never going to grow very large or this query will only get called infrequently when an admin loads the user management screen, then the memory option may not be noticeably slower; however, if this query will be called frequently and/or the user table will get large, then this may not be a viable option.
I would suggest a third option. Reappraise the requirements. In this case, the user may really need a filtering feature where they can look at all users who are in a, b, c roles. If that is the true need, then sorting is not only much harder to implement, but it may also be a worse solution.
Good luck!