Constructing linq query - c#

I am struggling to figure out a linq statement that I would think is a very common scenario. Assuming the structure below, I have many users, each users has many diaries, each diary has many widgets.
I would like to say, get me the user where UserName = 'bob' and Password = 'password' and that users diaries where DisabledByAdmin == true and the list widgets where DisabledByAdmin == true only the diaries the previous statement returned.
Assuming the data is 1 user that is linked to 2 diaries (one has DisabledByAdmin == true), and those 2 diaries each have 2 widgets (1 has DisabledByAdmin == true), I would like to get my user object back that would have 1 diary object in my list and 1 widget in EACH diary.
So the return data would be user object, with one diary and one widget in that diary collection. For the life of me, I can't figure that out.
Anyone?
Thanks very much
Ralph
public class Widget
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string Name { get; set; }
public bool DisabledByAdmin { get; set; }
public bool DisabledByUser { get; set; }
}
public class DigtalDiary
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public bool DisabledByAdmin { get; set; }
public bool DisabledByUser { get; set; }
public virtual List<Widget> Widgets { get; set; }
}
public class User
{
[Key]
public int Id { get; set; }
public string Password { get; set; }
public string UserName { get; set; }
public virtual List<DigtalDiary> Diaries { get; set; }
}

Something like
var users from u in UserList
where u.UserName = "Bob" and u.Password = "123";
var lookup = new Dictionary<int, List<DigitalDiary>>();
foreach(var u in users)
{
var digitalDiaries = from dd in u.Diaries
where dd.DisabledByAdmin != true; //Or whatever your criteria
lookup.add(u.Id, digitalDiaries.toList());
}
This will give you a dictionary of UserId's that match your criteria, and a list of Ditital Diaries for that user that aren't disabled and whatnot.
Then you can repeat that for Widgets
(Also this was just coded out in the window here, so there might by typos)
Edit:
Also you could use a
new Dictionary<User, List<DigitalDiary>>();
if you'll be wanting the full User Fields as soon as you're done. I'm just used to storing keys

If I understand you correctly, assuming that you have a List collection of User of name myUserList, you may iterate through this list only getting the values you are looking for. Then, if the values you are looking for were found, you may add the User responsible with these values to a new List collection of User.
Example
List<User> myUserList = new List<User>(); //Initialize a new List of Users
//Add users to the list
myUserList.Add(new User() { UserName = "bob", Password = "password" });
myUserList.Add(new User() { UserName = "Joe", Password = "password" });
myUserList.Add(new User() { UserName = "Miranda", Password = "password" });
myUserList.Add(new User() { UserName = "Kevin", Password = "password" });
//
List<User> myGatheredList = new List<User>(); //Initialize a new List of Users of name myGatheredList (not required)
foreach (User _user in myUserList.Where(x => x.UserName == "bob" && x.Password == "password")) //Only get values which match a UserName of value 'bob' and a password of value 'password' as _user for every User
{
Debug.WriteLine(_user.UserName); //Writes 'bob'.
foreach (DigtalDiary _diary in _user.Diaries) //Get every DigitalDiary in _user.Diaries as _diary for every DigitalDiary
{
if (_diary.DisabledByAdmin /*&& _diary.Widgets.Count == x */ /* More conditions can be inserted here */) //Continue if _diary.DisabledByAdmin is true
{
foreach (Widget _widget in _diary.Widgets) //Get every Widget in _diary.Widgets as _widget for every Widget
{
if (_widget.DisabledByAdmin) //Continue if _widget.DisabledByAdmin is true
{
if (!myGatheredList.Contains(_user)) //Continue if _user does not exist in myGatheredList
{
myGatheredList.Add(_user); //Add _user to myGatheredList (not required)
//Do something with _user
}
}
}
}
}
}
NOTICE: This will only get the Users which have got at least ONE DigitalDiary disabled by admin and ONE Widget disabled by admin.
Thanks,
I hope you find this helpful :)

If I understood the question AND you are using EntityFramework
You may need to play with the Select Projection to meet your requirements.
This example is an AND, use || for or condition as appropriate
var mylist = Context.Set<User>.Where(u=>u.Id==userId && u.Password==password
&& u.Diaries.DiabledByAdmin == false
&& u.Diaries.Widgets.DisabledByAdmin == false)
.Select(new {u.Id, u.Diaries.Id,u.Diaries.Widgets.Id}); //remove select if user Object desired

Related

How to write Linq query to check whether List have that id

I have a List inside that List one more List is there. How to check the id is present in any one of the List.
public class Users
{
public virtual Userid { get; set; }
public virtual List<UserCity> UserCities { get; set; }
}
And
public class UserCity
{
public virtual UserCityId { get; set; }
public virtual UserCityName { get; set; }
}
How to get the users which are having Userid =usercityid.
Linq query i tried like this
List<Users> lstusers = new List<Users>();
lstusers = lstusers
.Where(x=>x.UserCity.Any(w => w.UserCityId == w.Userid ))
.ToList();
If the users have usercityid with same userid i need to get those users.
There are some errors in your query. Try this:
// create a dummy list for testing
var lstusers = new List<Users>
{
new Users {Userid = 1, UserCities = new List<UserCity>{ new UserCity { UserCityId = 1, UserCityName="xxx"} } }
};
// execute the linq-query
lstusers = lstusers.Where(x => x.UserCities.Any(w => w.UserCityId == x.Userid)).ToList();
// and print the number of users found
Console.WriteLine($"Number of users found: {lstusers.Count}");

Entity Framework is trying to insert into wrong table?

I've built a test application with Entity Framework to simulate a database that contains friends lists.
I want the database to store the user's ID's and when I retrieve them (the "AcceptedFriends") I want Entity Framework to also return the friends "usermodel".
But every time I try to add 2 users as friends to the "AcceptedFriends" table it
throws an exception:
" Violation of PRIMARY KEY constraint 'PK_Users'. Cannot insert duplicate key in object 'dbo.Users'. The duplicate key value is (GUID value of a user's ID) "
Some attempted solutions:
Solution 1
Attempting to create 2 lists of the same friend list (received, sent) but that defeats the purpose of what I am trying to achieve.
Solution 2
Here are the code files:
"Users Model"
public class Users
{
#region Private fields
#endregion
#region Public properties
public string Username { get; set; }
public string ID { get; set; }
public string PasswordHash { get; set; }
public virtual List<AcceptedFriends> AcceptedFriendsList { get; set; }
// public virtual List<PendingFriends> PendingFriendsList { get; set; }
// public virtual List<RemovedFriends> RemovedFriendsList { get; set; }
#endregion
}
"Accepted Friends model"
public class AcceptedFriends
{
#region Public properties
public string RelationKey { get; set; }
public string RequestSenderID { get; set; }
public string RequestReceiverID { get; set; }
public virtual List<Messages> ChatList { get; set; }
public Users RequestSender { get; set; }
public Users RequestReceiver { get; set; }
#endregion
}
"Database model creation"
#region Users table
// Create primary key in Users table
modelBuilder.Entity<Users>().HasKey(property => property.ID);
// Map Username to be unique
modelBuilder.Entity<Users>().HasIndex(property => property.Username).IsUnique();
// Create a one to many relation with AcceptedFriends table
modelBuilder.Entity<Users>()
.HasMany(property => property.AcceptedFriendsList)
.WithOne(property => property.RequestReceiver)
.HasForeignKey(property => property.RequestReceiverID);
#endregion
#region Accepted friends table
// Create key for AcceptedFriends
modelBuilder.Entity<AcceptedFriends>().HasKey(property => property.RelationKey);
#endregion
Edit
Here is how I am inserting the friends
public static void AddFriends(AcceptedFriends friends)
{
using(Context context = ConnectToDatabase())
{
context.AcceptedFriends.Add(friends);
context.SaveChanges();
};
}
Edit 2
Here is where I add the friends/users
Plus I've noticed another odd behaviour When I add new users to the friends table
without adding them to the users table first it adds them both to the friends table and users table.
Console.WriteLine("Connecting to database");
DB.ConnectToDatabase();
Console.WriteLine("Connected to database successfully");
List<Users> userList = new List<Users>(DB.GetUsersList());
List<AcceptedFriends> friendsCount = new List<AcceptedFriends>(DB.GetAcceptedFriends());
if(userList.Count != 2)
{
DB.AddUser(new Users()
{
Username = "User1",
PasswordHash = "PasswordHash",
});
DB.AddUser(new Users()
{
Username = "User2",
PasswordHash = "PasswordHash",
});
userList = new List<Users>(DB.GetUsersList());
};
if(friendsCount.Count < 1)
{
Users user1 = userList[0];
Users user2 = userList[1];
DB.AddFriends(new AcceptedFriends()
{
RequestReceiver = user2,
RequestSender = user1,
});
};
Console.WriteLine("Server is great success!");
Console.ReadLine();
Edit 3
I might have found a solution.
It does return the models both for the user and friends,
But I can't accept this as a solution yet because it feels to hackey(?) for me
(Thanks to #wertzui, You helped me to get to this solution)
Basically everytime a new context is created it sets up the the friends and users to return thier usermodels
/// <summary>
/// Gets the friends user models
/// </summary>
/// <param name="context"> The database context that was created </param>
private static void SetupFriends(Context context)
{
// For every "AcceptedFriend"
foreach(AcceptedFriends friend in context.AcceptedFriends)
{
// Get sender and receiver usermodels
// by matching ID's
Users sender = context.Users.FirstOrDefault(user => user.ID == friend.RequestSenderID);
Users receiver = context.Users.FirstOrDefault(user => user.ID == friend.RequestReceiverID);
sender.AcceptedFriendsList.Add(friend);
receiver.AcceptedFriendsList.Add(friend);
friend.RequestSender = sender;
friend.RequestReceiver = receiver;
};
}
When you create new User Instances in your new AcceptFriends {...} Code, you are not setting their Id, so they keep their default which is 0. Now Entity Framework thinks, that you want to create a new Friendship with 2 new Users. Instead you should populate them with the Users, you created earlier.
if(friendsCount.Count < 1)
{
Users user1 = userList[0];
Users user2 = userList[1];
DB.AddFriends(new AcceptedFriends()
{
RequestReceiver = user1,
RequestSender = user2,
});
}

Dapper mapping multiple joins to single object returning no records

Im trying to use Dapper in an ASP.Net Core application to map multiple tables to one object with other objects as its properties.
My tables are as follows (just basic summary):
user table
address table (no user ids stored in this table, this table is just address records)
address_type table (lookup table)
phone_number table (no user ids stored in this table, this table is
just phone records)
phone_number _type table (lookup table)
user_has_address table - this table has a user_id, address_id and address_type_id
user_has_phone_number table - this table has a
user_id, phone_number _id and phone_number _type_id
Basically whats happening is results get returned fine if all the users have no address or phone records or if only the first user in the list has address/phone records. What I want is if a user has an address/phone number then that dictionary is populated, otherwise I still want all the user info but that address/phone number dictionary will be empty.
My objects look like the following:
public class User
{
public uint id { get; set; }
public DateTime modified_date { get; set; }
public uint modified_by { get; set; }
public string user_name { get; set; }
public uint company_code { get; set; }
public string email { get; set; }
public bool active { get; set; }
public string first_name { get; set; }
public string last_name { get; set; }
public Dictionary<uint, Address.Address> addresses { get; set; }
public Dictionary<uint, Address.PhoneNumber> phone_numbers { get; set; }
}
public class Address
{
public uint address_id { get; set; }
public AddressType address_type { get; set; }
public string address_line1 { get; set; }
public string address_line2 { get; set; }
public string address_line3 { get; set; }
public string city { get; set; }
public string state { get; set; }
public string country_code { get; set; }
public string postal_code { get; set; }
public sbyte is_po_box { get; set; }
}
public class AddressType
{
public uint id { get; set; }
public string name { get; set; }
}
public class PhoneNumber
{
public uint id { get; set; }
public PhoneNumberType phone_number_type { get; set; }
public string phone_number { get; set; }
public string phone_ext { get; set; }
}
public class PhoneNumberType
{
public uint id { get; set; }
public string name { get; set; }
}
Here is my function where I try to use Dapper to map to the User class:
public List<User> GetUsersByStatus(uint companyCode, string status)
{
if (companyCode == 0)
throw new ArgumentOutOfRangeException("companyID", "The Company ID cannot be 0.");
List<User> Users = new List<User>();
try
{
string sql = #"SELECT u.*, ad.*, adt.*, p.*, pt.*
FROM master.user u
LEFT JOIN master.user_has_address AS uha ON uha.user_id = u.id
LEFT JOIN master.address AS ad ON ad.id = uha.address_id
LEFT JOIN master.lookup_address_type adt ON adt.id = uha.address_type_id
LEFT JOIN master.user_has_phone_number AS uhp ON uhp.user_id = u.id
LEFT JOIN master.phone_number AS p ON p.id = uhp.phone_number_id
LEFT JOIN master.lookup_phone_number_type pt ON pt.id = uhp.phone_number_type_id
WHERE u.company_code = " + companyCode;
switch (status)
{
case "1":
// Active Status.
sql = sql + " AND (u.active = TRUE)";
break;
case "2":
// Retired Status.
sql = sql + " AND (u.active = FALSE)";
break;
}
sql = sql + " ORDER BY u.user_name";
using (var conn = new MySqlConnection(connectionString))
{
conn.Open();
var userDictionary = new Dictionary<uint, User>();
conn.Query<User, Address, AddressType, PhoneNumber, PhoneNumberType, User>(sql, (u, ad, adt, p, pt) =>
{
User user;
if (!userDictionary.TryGetValue(u.id, out user))
userDictionary.Add(u.id, user = u);
if (ad != null && adt != null)
{
Address address = ad;
address.address_type = new AddressType() { id = adt.id, name = adt.name };
if (user.addresses == null)
user.addresses = new Dictionary<uint, Address>();
if (!user.addresses.ContainsKey(adt.id))
user.addresses.Add(adt.id, address);
}
if (p != null && pt != null)
{
PhoneNumber phone = p;
phone.phone_number_type = new PhoneNumberType() { id = pt.id, name = pt.name };
if (user.phone_numbers == null)
user.phone_numbers = new Dictionary<uint, PhoneNumber>();
if (!user.phone_numbers.ContainsKey(pt.id))
user.phone_numbers.Add(pt.id, phone);
}
return user;
},
splitOn: "id,id,id,id").AsQueryable();
Users = userDictionary.Values.ToList();
}
}
catch(Exception ex)
{
//TO DO: log exception
}
return Users;
}
I've tried tracing through it and in cases where the 3rd user (for example) has address/phone records, it seems to grab the first 2 users fine but then jump right out of the query portion when it gets to the record with address/phone numbers and then it returns an empty Users list.
Does anyone have any idea what Im doing wrong?
I'm not sure this is the solution, but I have a problem with this code:
User user;
if (!userDictionary.TryGetValue(u.id, out user))
userDictionary.Add(u.id, user = u);
Remember that u is parameter of the lambda, and will be changed during execution. I would do this:
User user;
if (!userDictionary.TryGetValue(u.id, out user))
{
user = new User(u); // Make a new instance of user
userDictionary.Add(u.id, user);
}
Also you should definitely use parameters for your query:
WHERE u.company_code = #CompanyCode";
And finally I don't think it should be the responsibility of this code to construct dictionaries for holding addresses and phone numbers. The User constructor should take care of that.
Unfortunately, I must have 50 rep to add a comment, so I'll make this an answer.
Are all the fields in the DB concerning phone and address non-nullable? If no, then the mistake could be that it can't match them with your C# classes. In this case, declare immutable types nullable so that they match DB types. Like this:
int? val;
Turns out the main issue was the fact that I left the address_id parameter in the Address class named the way it was, it should ahve been just 'id'. I updated that and it works now, I did also update my code according to Palle Due's suggestion so that may have contributed as well.

List of values as a user attribute

I'm using identity 2.1.0 in ASP.NET MVC 5 application, and I have pages for admin to work (create/edit/delete user) with some custom user properties I defined. However I need to store a list of values in one field. How can I achieve this by using identity, and how to show this on a web page ?
To save such values you can extend your ApplicationUser, in case of multiple values you can do this via an n:m relationship:
First create a table to store the country values in (also add to your DbContext as e.g. public DbSet<Country> Countries { get; set; }):
public class Country
{
[Key]
public int Id { get; set; } // or e.g. "string Code" to save e.g. "us"
public string Name { get; set; }
public List<ApplicationUsers> Users { get; set; }
}
then you can also add a list of Country to your ApplicationUser:
public class ApplicationUser : IdentityUser<KEY>
{
// ...
public List<Country> Countries { get; set; }
}
and finally to update the countries of a user something like the following:
var user = // get user
var countryToAdd = db.Countries.FirstOrDefault(c => c.Name == countryName) ??
new Country() { Name = countryName };
if (user.Countries == null)
user.Countries = new List<Country>() { countryToAdd };
else if (!user.Countries.Contains(countryToAdd))
user.Countries.Add(countryToAdd);
db.SaveChanges();
And to get all users from one country:
var country = db.Countries.Include(c => c.Users)
.FirstOrDefault(c => c.Name == countryName);
if (country != null)
{
var users = country.Users;
}

WCF REST service doesn't return anything because I have a circular reference (I think)

I'm developing a WCF REST service using Entity Framework Code First data layer and I have a navigation property.
User class:
[DataContract]
public class User
{
[DataMember]
public int UserId { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public int Age { get; set; }
[DataMember]
public string City { get; set; }
[DataMember]
public string Country { get; set; }
[DataMember]
public string Email { get; set; }
[DataMember]
public string InterestIn { get; set; }
[DataMember]
public virtual ICollection<User> Friends { get; set; }
[DataMember]
public virtual ICollection<User> FromWhomIsFriend { get; set; }
}
ServiceContract method:
public List<User> GetUserFriends(string user_id)
{
int userId;
OutgoingWebResponseContext ctx =
WebOperationContext.Current.OutgoingResponse;
if ((user_id == null) ||
(!Int32.TryParse(user_id, out userId)) ||
(userId < 1))
{
ctx.StatusCode = System.Net.HttpStatusCode.BadRequest;
ctx.StatusDescription = "user_id parameter is not valid";
throw new ArgumentException("GetUserFriends: user_id parameter is not valid", "user_id");
}
List<User> friends = null;
try
{
using (var context = new AdnLineContext())
{
context.Configuration.ProxyCreationEnabled = false;
context.Configuration.LazyLoadingEnabled = false;
var users = from u in context.Users.Include("Friends")
where u.UserId == userId
select u;
if ((users != null) &&
(users.Count() > 0))
{
User user = users.First();
//friends = user.Friends.ToList();
friends = new List<User>();
foreach (User f in user.Friends)
{
User us = new User()
{
UserId = f.UserId,
Name = f.Name,
Age = f.Age,
City = f.City,
Country = f.Country,
Email = f.Email,
InterestIn = f.InterestIn,
Friends = f.Friends,
FromWhomIsFriend = f.FromWhomIsFriend
};
friends.Add(us);
}
}
ctx.StatusCode = System.Net.HttpStatusCode.OK;
}
}
catch (Exception ex)
{
ctx.StatusCode = System.Net.HttpStatusCode.InternalServerError;
ctx.StatusDescription = ex.Message;
ctx.SuppressEntityBody = true;
}
return friends;
}
This method doesn't return anything. If I comment this line FromWhomIsFriend = f.FromWhomIsFriend it works.
FromWhomIsFriend is a navigation property to the user from whom I am his friend. To represent user relationship I have this table:
UserID | FriendID
---------+----------
3 | 1
---------+----------
1 | 2
If I ask about friends from user 1, I get user 2, and its FromWhomIsFriend pointing to user 1. And user 1 Friends navigation property pointing to user 2, and continues.
Do you know how why I don't return anything?
You have to enable proxy creation in order to support the lazy loading. What you can do is to use Include in your query to load the navigation property .
var users = from u in context.Users.Include(u=>u.Friends)
where u.UserId == userId
select u;
Or else the other solution is using separate object model as the WCF contracts(DTO). Then you can enable lazy loading and then copy all the required values form the EF entity object(proxy) to your new Data Transfer Object. You can use something like Automaper to map the object easily.

Categories