LINQ Returning a set of results - c#

i have some code that sets user's properties like so:
us = new UserSession();
us.EmailAddr = emailAddr;
us.FullName = fullName;
us.UserROB = GetUserROB(uprUserName);
us.UserID = GetUserID(uprUserName);
us.UserActive = GetUserActive(uprUserName);
where GetUserROB, GetUserID and GetUserActive all look similar like so:
private static string GetUserActive(string userName)
{
using (Entities ctx = CommonSERT.GetContext())
{
var result = (from ur in ctx.datUserRoles
where ur.AccountName.Equals(userName, StringComparison.CurrentCultureIgnoreCase)
select new
{
Active = ur.active
}).FirstOrDefault();
if (result != null)
return result.Active;
else
return "N";
}
}
it works, but i dont think it's the right way here. how can i assign userROB, ID and Active properties all in one LINQ call? without having to have 3 separate functions to do this?

If I understand correctly I believe you can do something like:
private static void GetUserData(string userName, UserSession userSession)
{
using (Entities ctx = CommonSERT.GetContext())
{
var result = (from ur in ctx.datUserRoles
where ur.AccountName.Equals(userName, StringComparison.CurrentCultureIgnoreCase)
select new
{
UserActive = ur.active,
UserROB = ur.ROB,
UserID = ur.ID
}).FirstOrDefault();
}
if (result != null) {
userSession.UserActive = result.UserActive;
userSession.UserROB = result.UserROB;
userSession.UserID = result.UserID;
}
}
In the select new you can place as many properties as you want, this way you can get from the database several properties in a single roundtrip, and handling it later.
In the example I gave, I pass the UserSession as a parameter, in any case you already have other properites alrealdy filled from other methods.

You can create a method that accepts a UserSession object as parameter, then set all three properties in it. I changed your GetUserActive a bit here:
private static void GetUserData(string userName, UserSession user)
{
using (Entities ctx = CommonSERT.GetContext())
{
var result = (from ur in ctx.datUserRoles
where ur.AccountName.Equals(userName, StringComparison.CurrentCultureIgnoreCase)
select new
{
Active = ur.active,
ID = ur.ID,
//...select all properties from the DB
}).FirstOrDefault();
if (result != null)
user.UserActive = result.Active;
user.UserID = result.ID;
//..set all properties of "user" object
}
}

Well you might consider normalising your domain model and having a User property of type User, then your method would return all of the related user data.

Related

EF not retrieving entity out of context

What I'd like to happen is that if I've already created the record (GuildName) in the session and attached it to the context,then for EF to retrieve it from the context instead of creating a duplicate, however it cant seam to make it not create a duplicate.
public static async Task ProcessNames()
{
var db = new SQLDbContext();
db.Configuration.ProxyCreationEnabled = false;
var existingNames = db.CharacterNames.Include("GuildName").Include("RealmName").Take(10).ToList();
var gatherGuildNameTaskList = (
from name in existingNames
select GetGuildFromName(name, db)).ToList();
await Task.WhenAll(gatherGuildNameTaskList);
db.SaveChanges();
}
public static async Task GetGuildFromName(CharacterName characterName, SQLDbContext db)
{
var character = await ApiHelper.GetJsonFromUrlAsync<Character>(new Uri( URL ));
if (character.Guild == null) return;
//This is the call that appears to not be working as intended
var guildName = db.GuildName.
SingleOrDefault(x => x.RealmName.Name == character.Guild.Name) ?? new GuildName
{
Name = character.Guild.Name,
CharacterNames = new List<CharacterName>(),
RealmName = characterName.RealmName
};
if (!guildName.Equals(characterName.GuildName))
{
characterName.GuildName = guildName;
}
}
I only having it take 10 names for testing, but normally there's a few 100k's
The only thing I can think is to call save changes on search method of GetGuildFromName, but I have a feeling, that it might not work as intended, but could be detrimental to performance, not that performance is a huge factor, but I like trying to practice making things perform reasonably.
I think you never add your newGuildName to the context ( db.GuildName.Add(guildName) like this:
var guildName = db.GuildName.
SingleOrDefault(x => x.RealmName.Name == character.Guild.Name);
if (guildName ==null)
{
guildName = new GuildName
{
Name = character.Guild.Name,
CharacterNames = new List<CharacterName>(),
RealmName = characterName.RealmName
};
db.GuildName.Add(guildName);
}

Generic methods with entity framework

I have a question about using a generic method with the entity framework.
I am using this example model
Data Model
And this is the code in my webform this code to populate the object.
var user = new User();
var selectedTitles = new List<UserTitle>();
var selectedDisabilities = new List<UserDisability>();
var t = titleRepository.SearchFor(d => d.Id==1 || d.Id ==2);
foreach (var temp in t)
{
selectedTitles.Add(new UserTitle { IsPublic = true, Title = temp, User = user });
}
var ds = disabilityRepository.SearchFor(d => d.Id==1 || d.Id ==2);
foreach (var temp in ds)
{
selectedDisabilities.Add(new UserDisability { IsPublic = true, Disability = temp, User = user });
}
user.FirstName = "Johnathan";
user.LastName = "Rifkin";
user.UserTitles = selectedTitles;
user.UserDisabilities = selectedDisabilities;
userRepository.Insert(user);
As you can see when populating the “UserTitles” and “UserDisabilities” properties the code is very similar, so rather than duplicate the code I would like to create a generic method that I can use to populate both the “UserTitles” and “UserDisabilities” and any other properties that I'll need in future.
Thanks in advance

C# Create employee. Save to SQL Database using EF

I'm saving an employee to a SQL database. I'm saving Firstname, Lastname, Username and Password. How should I do this to prevent saving more than one identical username?
I've tried this:
private void CreateEmployee()
{
using (var db = new TidrapportDBEntities())
{
var user = (from p
in db.Login
where p.username != null
select p).ToList();
foreach (var vUser in user)
{
if (vUser.username == textBoxUsername.Text)
{
labelSuccessFail.Visible = true;
labelSuccessFail.Text = "Accountname already exist.";
break;
}
else
{
var userInfo = new Login();
var persInfo = new PersonalInformation();
persInfo.firstname = textBoxFirstname.Text;
persInfo.lastname = textBoxLastname.Text;
userInfo.username = textBoxUsername.Text;
userInfo.password = textBoxPassword.Text;
userInfo.employeeId = persInfo.employeeId;
db.Login.Add(userInfo);
db.PersonalInformation.Add(persInfo);
db.SaveChanges();
textBoxFirstname.Text = string.Empty;
textBoxLastname.Text = string.Empty;
textBoxUsername.Text = string.Empty;
textBoxPassword.Text = string.Empty;
labelSuccessFail.Visible = true;
labelSuccessFail.Text = "Successfully created account.";
}
}
}
}
Any tips what I can try?
Kind regards,
Kristian
You should have a unique constraint on the username field. Not sure if you're doing code first, model first or DB first in your EF, but you should be able to google how to get it set on your database using the right method. That will throw an exception if you try to save one, so that makes sure you can't have more than one.
You could also use LINQ statement to restrict the list of users to the user name you wish to create and then you're just down to checking a bool to see if a row is returned or not. That way you're not having to read the entire database table (which your "toList" is doing).
In your code example, you're getting all the users where they have a user name, you're then looping round them, but your conditional code only really works if the first one matches the user name you're trying to save, otherwise you are going to try and recreate a duplicate the second time around. So just to get your code working you could try:
private void CreateEmployee()
{
using (var db = new TidrapportDBEntities())
{
var user = (from p
in db.Login
where p.username != null
select p).ToList();
bool found = false;
foreach (var vUser in user)
{
if (vUser.username == textBoxUsername.Text)
{
found = true;
labelSuccessFail.Visible = true;
labelSuccessFail.Text = "Accountname already exist.";
break;
}
}
if(!found)
{
var userInfo = new Login();
var persInfo = new PersonalInformation();
persInfo.firstname = textBoxFirstname.Text;
persInfo.lastname = textBoxLastname.Text;
userInfo.username = textBoxUsername.Text;
userInfo.password = textBoxPassword.Text;
userInfo.employeeId = persInfo.employeeId;
db.Login.Add(userInfo);
db.PersonalInformation.Add(persInfo);
db.SaveChanges();

What causes the Linq error: This method cannot be translated into a store expression?

I have a bunch of Linq to Entity methods that had the same select statement, so I thought I would be clever and separate that out into it's own method to reduce redundancy... but when i attempted to run the code, i got the following error...
this method cannot be translated into
a store expression
Here is the method i created...
public User GetUser(DbUser user, long uid)
{
return new User
{
Uid = user.uid,
FirstName = user.first_name,
LastName = user.last_name
};
}
And am calling in a method like this...
public User GetUser(long uid)
{
using (var entities = new myEntities()) {
return
entities.DbUsers.Where( x => x.uid == uid && x.account_status == ( short )AccountStatus.Active ).
Select( x => GetUser( x, uid ) ).FirstOrDefault( );
}
}
UPDATE: here is the code that works inline
public User GetUser(long uid, long uid_user)
{
using (var entities = new myEntities())
{
var q = from u in entities.DbUsers
where u.uid == uid_user
select new User
{
Uid = u.uid,
FirstName = u.first_name,
LastName = u.last_name,
BigPicUrl = u.pic_big,
Birthday = u.birthday,
SmallPicUrl = u.pic_small,
SquarePicUrl = u.pic_square,
Locale = u.locale.Trim(),
IsFavorite = u.FavoriteFriends1.Any(x => x.uid == uid),
FavoriteFriendCount = u.FavoriteFriends.Count,
LastWishlistUpdate = u.WishListItems.OrderByDescending(x => x.added).FirstOrDefault().added,
Sex = (UserSex)u.sex
};
var user = q.FirstOrDefault();
user.DaysUntilBirthday = user.Birthday.DaysUntilBirthday();
return user;
}
}
The error is spot on, you can't translate that into a T-SQL (or P-SQL) query.
You need to make sure you've executed the query before you attempt to hydrate it into some other type.
Keep it simple, use an extension method. That's what they are there for.
public static User ToUserEntity(this DbUser user)
{
return new User
{
Uid = user.uid,
FirstName = user.first_name,
LastName = user.last_name
};
}
Then in your DAL:
public User GetUser(long uid)
{
User dbUser;
using (var entities = new myEntities())
{
dbUser = entities.DbUsers
.Where( x => x.uid == uid && x.account_status == (short)AccountStatus.Active )
.FirstOrDefault(); // query executed against DB
}
return dbUser.ToUserEntity();
}
See how i hydrate the POCO into an object after the context has been disposed? This way, you ensure EF has finished it's expression work before you attempt to hydrate into a custom object.
Also i dont know why you're passing uid to that method, it's not even being used.
On a further note, you shouldn't need to do this kind of thing (project EF POCO's into your own objects).
If you do, it's a good case for custom POCO's (map the tables straight into your custom POCO's, don't use the Code Generation).
This expression will work to give the desired result (somewhat) I still havent figured out how to pass in additional variables in teh select statements...
..... .Select(GetUser).FirstOrDefault()
static readonly Expression<Func<DbUser, User>> GetUser = (g) => new User {
Uid = g.uid,
FirstName = g.first_name,
LastName = g.last_name,
BigPicUrl = g.pic_big,
Birthday = g.birthday,
SmallPicUrl = g.pic_small,
SquarePicUrl = g.pic_square,
Locale = g.locale.Trim(),
//IsFavorite = g.FavoriteFriends1.Any(x=>x.uid==uid),
FavoriteFriendCount = g.FavoriteFriends.Count,
LastWishlistUpdate = g.WishListItems.OrderByDescending( x=>x.added ).FirstOrDefault().added
};
You can't do this because the getUser method cannot be converted to any TSQL statement.
if you return your DBUser first and then use it as the first parameter of the GetUser method then you are forcing it to execute and once you have you DBUser you can pass it to GetUser
Maybe you can try this:
public User GetUser(long uid)
{
using (var entities = new myEntities())
{
return GetUser(
entities.DbUsers
.Where( x => x.uid == uid && x.account_status == (short)AccountStatus.Active )
.FirstOrDefault(),
uid);
}
}
EDIT
Since you are saying it still fails could it be beacuse of the enum??
public User GetUser(long uid)
{
using (var entities = new myEntities())
{
short status = (short)AccountStatus.Active;
return GetUser(
entities.DbUsers
.Where( x => x.uid == uid && x.account_status == status )
.FirstOrDefault(),
uid);
}
}

Flatten One-to-Many Relationship Using Dynamic LINQ

Is it possible to flatten a one-to-many relationship using dynamic LINQ?
For example, I might have a list of Users and the User class contains a list of many UserPreferences. The UserPreference class is essentially a name/value pair.
A user will define what types of user preferences are available for a group of users.
public class User
{
public string FirstName
{
get;
set;
}
public string LastName
{
get;
set;
}
public IList<UserPreference> UserPreferences
{
get;
set;
}
}
public class UserPreference
{
public UserPreference(string name, object userValue)
{
this.Name = name;
this.UserValue = userValue;
}
public string Name
{
get;
set;
}
public object UserValue
{
get;
set;
}
}
Therefore one user group might be defined in the following way:
List<User> users = new List<User>();
User user1 = new User();
user1.FirstName = "John";
user1.LastName = "Doe";
user1.UserPreferences.Add(new UserPreference("Favorite color", "Red"));
User user2 = new User();
user2.FirstName = "Jane";
user2.LastName = "Doe";
user2.UserPreferences.Add(new UserPreference("Favorite mammal", "Dolphin"));
user2.UserPreferences.Add(new UserPreference("Favorite color", "Blue"));
users.Add(user1);
users.Add(user2);
return users;
The desired output would be:
First Name Last Name Favorite Color Favorite Mammal
John Doe Red NULL
Jane Doe Blue Dolphin
Is there a way to create an anonymous type so that UserPreferences would get rolled up into the User?
For example,
var u = UserScopedSettingAttribute.Select("new (FirstName as FirstName, UserValue as FavoriteColor)", null);
string name = u.FirstName;
string color = u.FavoriteColor;
Ultimately this list of Users will get bound to an ASP.NET GridView web control. There will be a large volume of data involved in this operation and performance will be critical.
Any suggestions are appreciated!
I know it doesn't exactly answer your question, but compiling strings into new classes at runtime like dlinq does has always had kind of a bad smell to it. Consider just simply using a DataTable like this,
DataTable prefs = new DataTable();
IEnumerable<DataColumn> cols = (from u in users
from p in u.UserPreferences
select p.Name)
.Distinct()
.Select(n => new DataColumn(n));
prefs.Columns.Add("FirstName");
prefs.Columns.Add("LastName");
prefs.Columns.AddRange(cols.ToArray());
foreach (User user in users)
{
DataRow row = prefs.NewRow();
row["FirstName"] = user.FirstName;
row["LastName"] = user.LastName;
foreach (UserPreference pref in user.UserPreferences)
{
row[pref.Name] = pref.UserValue;
}
prefs.Rows.Add(row);
}
This should do it. Flattening is generally done with SelectMany extension method, but in this case I am using a let expression. The code to remove the null preferences is a bit ugly and could prob be improved but it works:
var flattenedUsers = from user in GetUsers()
let favColor = user.UserPreferences.FirstOrDefault(pref => pref.Name == "Favorite color")
let favMammal = user.UserPreferences.FirstOrDefault(pref => pref.Name == "Favorite mammal")
select new
{
user.FirstName,
user.LastName,
FavoriteColor = favColor == null ? "" : favColor.UserValue,
FavoriteMammal = favMammal == null ? "" : favMammal.UserValue,
};
My best suggestion would be to not use dynamic LINQ, but add a flatuser class and then loop through the users. The code for this is simple, and if you were able to get a linq query with similar results it would generate the same code, although you can't really tell how optimized it would be as it might involve some joins that would incur a performance penalty instead of just looping. If you were pulling this from a database using LINQ to SQL then you could use an entity relation to to get the data using linq instead of this loop.
Loop:
List<FlatUser> flatusers = new List<FlatUser>();
foreach (User u in users)
{
foreach (UserPreference up in u.UserPreferences)
{
flatusers.Add(new FlatUser
{
FirstName = u.FirstName,
LastName = u.LastName,
Name = up.Name,
UserValue = up.UserValue
});
}
}
Flat User Class:
public class FlatUser
{
public string FirstName
{
get;
set;
}
public string LastName
{
get;
set;
}
public string Name
{
get;
set;
}
public object UserValue
{
get;
set;
}
}
Unfortunately
var u = UserScopedSettingAttribute.Select("new {FirstName as FirstName, UserValue as FavoriteColor}", null);
string name = u.FirstName;
string color = u.FavoriteColor;
won't work. When you use DLINQ Select(string) the strongest compile time class information you have is Object, so u.FirstName will throw a compile error. The only way to pull the properties of the runtime generated anonymous class is to use reflection. Although, if you can wait, this will be possible with C# 4.0 like this,
dynamic u = UserScopedSettingAttribute.Select("new {FirstName as FirstName, UserValue as FavoriteColor}", null);
string name = u.FirstName;
string color = u.FavoriteColor;
I think the pragmatic answer here is to say your attempting to force C# to become a dynamic language and any solution is going to be really pushing C# to its limits. Sounds like your trying to transform a database query of columns that are only determined at query time into a collection that is based on those columns and determined at run time.
Linq and Gridview binding is really pretty and succinct and all but you have to start thinking about weighing the benefit of getting this compiler bending solution to work just so you don't have to dynamically generate gridview rows and columns by yourself.
Also if your concerned about performance I'd consider generating the raw HTML. Relying on the collection based WebForms controls to efficiently display large sets of data can get dicey.
You add in a couple of OnItemDataBound events and boxing and unboxing is going to really gum up the works. I'm assuming too your going to want to add interactive buttons and textboxes to the rows as well and doing 1000 FindControls has never been fast.
There are probably more efficient ways to do this, but to actually answer your question, I came up with the following code. (Note that I've never worked with DynamicLinq before, so there may be a better way to use it to accomplish your goal.)
I created a console application, pasted in the classes from your post, then used the following code.
static void Main(string[] args)
{
var users = GetUserGroup();
var rows = users.SelectMany(x => x.UserPreferences.Select(y => new { x.FirstName, x.LastName, y.Name, y.UserValue }));
var userProperties = rows.Select(x => x.Name).Distinct();
foreach (var property in userProperties)
{
Console.WriteLine(property);
}
Console.WriteLine();
// The hard-coded variety.
var results = users.Select(x => new
{
x.FirstName,
x.LastName,
FavoriteColor = x.UserPreferences.Where(y => y.Name == "Favorite color").Select(y => y.UserValue).FirstOrDefault(),
FavoriteAnimal = x.UserPreferences.Where(y => y.Name == "Favorite mammal").Select(y => y.UserValue).FirstOrDefault(),
});
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
// The dynamic variety.
DynamicProperty[] dynamicProperties = new DynamicProperty[2 + userProperties.Count()];
dynamicProperties[0] = new DynamicProperty("FirstName", typeof(string));
dynamicProperties[1] = new DynamicProperty("LastName", typeof(string));
int propIndex = 2;
foreach (var property in userProperties)
{
dynamicProperties[propIndex++] = new DynamicProperty(property, typeof(string));
}
Type resultType = ClassFactory.Instance.GetDynamicClass(dynamicProperties);
ConstructorInfo constructor = resultType.GetConstructor(new Type[] {});
object[] constructorParams = new object[] { };
PropertyInfo[] propInfoList = resultType.GetProperties();
PropertyInfo[] constantProps = propInfoList.Where(x => x.Name == "FirstName" || x.Name == "LastName").OrderBy(x => x.Name).ToArray();
IEnumerable<PropertyInfo> dynamicProps = propInfoList.Where(x => !constantProps.Contains(x));
// The actual dynamic results creation.
var dynamicResults = users.Select(user =>
{
object resultObject = constructor.Invoke(constructorParams);
constantProps[0].SetValue(resultObject, user.FirstName, null);
constantProps[1].SetValue(resultObject, user.LastName, null);
foreach (PropertyInfo propInfo in dynamicProps)
{
var val = user.UserPreferences.FirstOrDefault(x => x.Name == propInfo.Name);
if (val != null)
{
propInfo.SetValue(resultObject, val.UserValue, null);
}
}
return resultObject;
});
//////////////////////////////////////////////////////////////////////////////////////////////////////////
// Display the results.
var displayResults = dynamicResults;
//var displayResults = results;
if (displayResults.FirstOrDefault() != null)
{
PropertyInfo[] properties = displayResults.First().GetType().GetProperties();
int columnWidth = Console.WindowWidth / properties.Length;
int index = 0;
foreach (PropertyInfo property in properties)
{
Console.SetCursorPosition(index++ * columnWidth, Console.CursorTop);
Console.Write(property.Name);
}
Console.WriteLine();
foreach (var result in displayResults)
{
index = 0;
foreach (PropertyInfo property in properties)
{
Console.SetCursorPosition(index++ * columnWidth, Console.CursorTop);
Console.Write(property.GetValue(result, null) ?? "(null)");
}
Console.WriteLine();
}
}
Console.WriteLine("\r\nPress any key to continue...");
Console.ReadKey();
}
static List<User> GetUserGroup()
{
List<User> users = new List<User>();
User user1 = new User();
user1.FirstName = "John";
user1.LastName = "Doe";
user1.UserPreferences = new List<UserPreference>();
user1.UserPreferences.Add(new UserPreference("Favorite color", "Red"));
user1.UserPreferences.Add(new UserPreference("Birthday", "Friday"));
User user2 = new User();
user2.FirstName = "Jane";
user2.LastName = "Doe";
user2.UserPreferences = new List<UserPreference>();
user2.UserPreferences.Add(new UserPreference("Favorite mammal", "Dolphin"));
user2.UserPreferences.Add(new UserPreference("Favorite color", "Blue"));
users.Add(user1);
users.Add(user2);
return users;
}

Categories