How To Get list Of Users - c#

I am using Entity Framework 6
public class ALLEvent
{
[Key]
public long Eventid { get; set; }
public string eventname{ get; set; }
public string AttendingUsers{ get; set; }
public virtual List<User> Users{ get; set; }
}
public class Users
{
[Key]
public long id { get; set; }
public string Name{ get; set; }
}
I have an AllEvent Class, in AttendingEvent property I have a list of attending UserIds separated by a comma. Is there any way I can get a list of attending users in the Users property of AllEvents?

You could do it with Select:
allEvent.Users = allEvent.AttendingUsers.Split(',').Select(
u => new User
{
Id = Convert.ToInt64(u)
}).ToList();
Or with a foreach loop:
var users = new List<Users>();
foreach (var u in allEvent.AttendingUsers.Split(',').ToList())
{
users.Add(new Users
{
Id = Convert.ToInt64(u)
});
}
allEvent.Users = users;
The Convert.ToInt64() method is used if you need a long type. But be aware, this could throw an exception.
https://msdn.microsoft.com/en-us/library/0zahhahw(v=vs.110).aspx
FormatException
or
OverflowException

You will have to do a query for every event to get the users:
using(var ctx = new MyContext())
{
ctx.Configuration.LazyLoadingEnabled = false;
var events = ctx.AllEvent.ToList();
foreach(var event in events)
{
event.Users = ctx.Users.Where(u =>
event.AttendingUsers.Split(',').Cast<long>().ToList().Contains(u.id)).ToList();
}
}
A much better option would be to make the Users have a foreignkey to an event (except if it's many to many, then you'll need an intermediate table). Then you can use lazy/eager loading. At the moment the virtual keyword doesn't do anything in your Event model because there's no relation between the entities.
Edited answer because cast from string -> long is needed.

Related

Trying Extract List of Categories with Corresponding Tickets using Linq

I am trying to extract list of Categories with the corresponding Tickets for a specific userId using Linq Lambda expression.
Category:
public class Category
{
public int Id { get; set; }
public string CategoryName { get; set; }
public ICollection<Ticket> Tickets { get; set; }
}
Ticket:
public class Ticket
{
public int Id { get; set; }
public string Title { get; set; }
public User User { get; set; }
public Category Category { get; set; }
}
User
public class User
{
public string Id { get; set; }
public string UserName { get; set; }
public ICollection<Ticket> Tickets { get; set; }
}
This is what I tried so far. But it is not even close.
public async Task<IEnumerable<Category>> GetCategories(string userid)
{
var categories = _context.Categories
.Include(c => c.Tickets)
.AsQueryable();
categories = categories
.Where(c => c.Tickets.Any(t =>t.User.Id.Equals(id)));
return await categories.ToListAsync();
}
How can I have a list of categories with corresponding tickets for a specific userId?
The issue is this line of code:
categories = categories.Where(c => c.Tickets.Any(t =>t.User.Id.Equals(id)));
This will return all categories that contain at least 1 ticket with the specified user id, but all tickets in the category will be included. My understanding is, you want to keep only the tickets that belong to the specified user id.
The fact that your Tickets are represented by an ICollection<Ticket> instead of IEnumerable<Ticket>, make this a bit more difficult - since LINQ works on IEnumerables. That's the reason for the ToList() call, which causes enumeration - something to be aware of here - Tickets collection is no longer lazy past that point.
This code will do what you want:
IEnumerable<Category> selection = (from c in categories
select new Category
{
Id = c.Id,
CategoryName = c.CategoryName,
Tickets = (from t in c.Tickets
where t.User.Id.Equals(id)
select t).ToList()
}).Where(c => c.Tickets.Count() > 0);
but, I think, both performance-wise and readability-wise, you might want a loop instead:
List<Category> selection = new List<Category>();
foreach (var category in categories)
{
category.Tickets = category.Tickets.Where(t => t.User.Id.Equals(id)).ToList();
if (category.Tickets.Count > 0)
{
selection.Add(category);
}
}
Finally, since your Ticket class carries the Category info anyway, it may be worth to flatten the list using SelectMany:
var selection = categories.SelectMany(c => c.Tickets.Where(t => t.User.Id.Equals(id)));
This returns a flattened list of Tickets - but only the ones that match the specified user id, from all categories. This has the advantage of staying lazy (it's still an IEnumerable), being simple, and very likely of higher performance than the other options; but, you no longer have the nested list.
Take your pick!

Query separate collection in RavenDB Index (WHERE IN)

Using RavenDB v4.2 or higher, I want to setup an index that queries another collection. Basically, reproduce a WHERE IN clause in the mapping part of the index.
The models below represent two collections. Here each User has a collection of Device ID's:
class Device {
public string Id { get; set; }
public string Name { get; set; }
}
class User {
public string Id { get; set; }
public string BlogPostId { get; set; }
public List<string> DeviceIds { get; set; }
}
Now consider the following index as an example on what I'm trying to achieve:
public class DeviceIndex : AbstractIndexCreationTask<Device, DeviceIndex.Result>
{
public class Result
{
public string Id { get; set; }
public string DeviceName { get; set; }
public bool HasUser { get; set; }
public int UserCount { get; set; }
}
public DeviceIndex()
{
Map = devices => from d in devices
select new Result
{
Id = d.Id,
DeviceName = d.Name,
HasUser = ... ?, // How to get this from Users collection?
UserCount = ... ? // same...
};
}
How do I fill the HasUser true/false and UserCount properties in this index? E.g. how can I query the 'User' collection here?
Please note that this example is seriously simplified for brevity. I'm not so much interested in workarounds, or changing the logic behind it.
As #Danielle mentioned you need to use a mutli-map-index and reduce the result.
Here is a working example
public class DeviceIndex : AbstractMultiMapIndexCreationTask<DeviceIndex.Result>
{
public class Result
{
public string Id { get; set; }
public string DeviceName { get; set; }
public bool HasUser { get; set; }
public int UserCount { get; set; }
}
public DeviceIndex()
{
AddMap<User>(users => from u in users
from deviceId in u.DeviceIds
let d = LoadDocument<Device>(deviceId)
select new Result
{
Id = d.Id,
HasUser = true,
UserCount = 1,
DeviceName = d.Name,
});
AddMap<Device>(devices => from d in devices
select new Result
{
Id = d.Id,
HasUser = false,
UserCount = 0,
DeviceName = d.Name,
});
Reduce = results => from result in results
group result by new { result.Id } into g
select new Result
{
Id = g.First().Id,
DeviceName = g.First().DeviceName,
HasUser = g.Any(e => e.HasUser),
UserCount = g.Sum(e => e.UserCount),
};
}
}
and you can call it like this
var result = await _session.Query<DeviceIndex.Result, DeviceIndex>().ToListAsync();
If you would have a Users List in the Device class List<string> Users
a list that contains the document ids from the Users collection then you could Index these Related documents.
See:
https://demo.ravendb.net/demos/csharp/related-documents/index-related-documents
Or do the opposite,
Create an index on the Users collection, and index the related Device info
Without changing current models,
You can create a Multi-Map Index to index data from different collections.
https://ravendb.net/docs/article-page/4.2/csharp/indexes/multi-map-indexes
https://ravendb.net/docs/article-page/4.2/csharp/studio/database/indexes/create-multi-map-index
https://ravendb.net/learn/inside-ravendb-book/reader/4.0/10-static-indexes-and-other-advanced-options#querying-many-sources-at-once-with-multimap-indexes

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.

Joins with Dapper return null

I have the following classes:
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public string Department { get; set; }
public List<Event> Events { get; set; }
}
public class Event
{
public int Id { get; set; }
public int UserId { get; set; }
public string EventText { get; set; }
public string StartTime { get; set; }
public string EndTime { get; set; }
public string Day { get; set; }
public string ColorIdentifier { get; set; }
public int Week { get; set; }
}
I'm trying to get all the users and their events with Dapper like this:
var sql = "SELECT u.Id, e.UserId, e.EventText FROM cpc.PLANNING_TOOL_USERS u LEFT JOIN cpc.PLANNING_TOOL_EVENTS e ON u.Id=e.UserId";
var result = SqlMapper.Query<User, Event, User>(connection, sql, (u, e) =>
{
if (u.Events == null)
u.Events = new List<Event>();
u.Events.Add(e);
return u;
}, splitOn: "Id, UserId");
The Id for the user is returned back, but the list of events is not populated. I have looked at many examples here on Stack Overflow regarding this, but I can't see what I'm doing wrong.
To omit the situation that SQL returns no data I have just mocked two user rows with SQL union.
User with Id=1 and one Event, and User with Id=2 and two Events.
SqlMapper.Query returns flat results that are best for 1 to 1 relation. You have one user to many events relation, so some helper storage needed to maintain that relation as a mapping thru the results. I have used .NET dictionary for that.
My code sample below:
// introducing temporary storage
var usersDictionary = new Dictionary<int, User>();
var sql = #"SELECT 1 Id, 1 UserId, 'EventText1' EventText
union SELECT 2 Id, 2 UserId, 'EventText2' EventText
union SELECT 2 Id, 2 UserId, 'Another EventText2' EventText";
var result = SqlMapper.Query<User, Event, User>(connection, sql, (u, e) =>
{
if (!usersDictionary.ContainsKey(u.Id))
usersDictionary.Add(u.Id, u);
var cachedUser = usersDictionary[u.Id];
if (cachedUser.Events == null)
cachedUser.Events = new List<Event>();
cachedUser.Events.Add(e);
return cachedUser;
}, splitOn: "UserId");
// we are not really interested in `result` here
// we are more interested in the `usersDictionary`
var users = usersDictionary.Values.AsList();
Assert.AreEqual(2, users.Count);
Assert.AreEqual(1, users[0].Id);
CollectionAssert.IsNotEmpty(users[0].Events);
Assert.AreEqual(1, users[0].Events.Count);
Assert.AreEqual("EventText1", users[0].Events[0].EventText);
Assert.AreEqual(2, users[1].Events.Count);
I hope that helped you solving your mapping issue and events being null.

Navigation Property null when I do context.Users.Create(). Is that correct?

I'm developing my first class library that uses Entity Framework Code First as Data access layer.
I have this class:
public class User
{
public int UserId { get; set; }
public String Name { get; set; }
public int Age { get; set; }
public String City { get; set; }
public String Country { get; set; }
public String Email { get; set; }
public String InterestIn { get; set; }
public virtual ICollection<User> Friends { get; set; }
public virtual ICollection<User> FromWhomIsFriend { get; set; }
}
And now I testing my code with a Console application:
static void Main(string[] args)
{
Database.SetInitializer(
new DropCreateDatabaseAlways<AdnLineContext>());
insertUsersAndFriends();
}
private static void insertUsersAndFriends()
{
using (var context = new AdnLineContext())
{
var user1 = context.Users.Create();
user1.Name = "User1";
user1.Age = 25;
user1.City = "City1";
user1.Country = "Country1";
user1.Email = "email_1#email.com";
user1.InterestIn = "User1's interests";
var user2 = context.Users.Create();
user2.Name = "User2";
user2.Age = 26;
user2.City = "City2";
user2.Country = "Country2";
user2.Email = "email_2#email.com";
user2.InterestIn = "User2's interests";
var user3 = context.Users.Create();
user3.Name = "User3";
user3.Age = 27;
user3.City = "City3";
user3.Country = "Country3";
user3.Email = "email_3#email.com";
user3.InterestIn = "User3's interests";
context.Users.Add(user1);
context.Users.Add(user2);
context.Users.Add(user3);
user1.Friends.Add(user2);
user3.Friends.Add(user1);
context.SaveChanges();
}
}
I'm testing, so the database is empty.
This is my UserConfiguration class:
public UserConfiguration()
{
Property(d => d.Name).IsRequired();
Property(d => d.Age).IsRequired();
Property(d => d.City).IsRequired();
Property(d => d.Country).IsRequired();
Property(d => d.Email).IsRequired();
Property(d => d.InterestIn).IsRequired();
HasMany(d => d.MessagesSent).WithRequired(l => l.SentByUser).WillCascadeOnDelete(false);
HasMany(d => d.MessagesReceived).WithRequired(l => l.SentToUser).WillCascadeOnDelete(false);
HasMany(d => d.Friends).
WithMany(d => d.FromWhomIsFriend).
Map(c =>
{
c.ToTable("UserFriends");
c.MapLeftKey("UserId");
c.MapRightKey("FriendId");
});
HasMany(d => d.WantsToDo).
WithMany(a => a.Users).
Map(t =>
{
t.ToTable("UserActivities");
t.MapLeftKey("UserId");
t.MapRightKey("ActivityId");
});
}
But I get a null pointer exception here user1.Friends.Add(user2); because Friends is null.
What am I doing wrong? How can I fix this problem?
Entity Framework seems to be smart in this case. You are adding a new User to the context:
var user1 = context.Users.Create();
//...
context.Users.Add(user1);
//...
user1.Friends.Add(user2);
The entity is in Added state after that. Why should EF run a query with lazy loading to initialize the Friends collection? user1 is the principal in the relationships and because the state is Added it cannot exist yet in the database, hence there can't be any dependent in the database refering to it that could be loaded. So, EF does not try to load the collection at all (which is good in order to avoid unnecessary database roundtrips).
You could apply tricks to make it work - by attaching the new users before you add them to the context:
var user1 = context.Users.Create();
//...
var user2 = context.Users.Create();
//...
var user3 = context.Users.Create();
//...
user1.UserId = 1;
context.Users.Attach(user1);
user2.UserId = 2;
context.Users.Attach(user2);
user3.UserId = 3;
context.Users.Attach(user3);
// ...because we cannot attach three users with the same key
user1.Friends.Add(user2);
user3.Friends.Add(user1);
// Lazy loading will run twice here based on the `UserId` which is 1,2,3
// and returns nothing, but the Friends collection will be initialized
// as empty collection
// This MUST be AFTER accessing the Friends collection
context.Users.Add(user1);
context.Users.Add(user2);
context.Users.Add(user3);
// the three "dummy UserIds" are ignored because state is Added now
context.SaveChanges();
Now, just forget this solution again. It's nonsense to force lazy loading (= expensive database query) to create an empty collection. C# has the new operator for this:
var user1 = context.Users.Create();
//...
var user2 = context.Users.Create();
//...
var user3 = context.Users.Create();
//...
user1.Friends = new List<User>();
user1.Friends.Add(user2);
user3.Friends = new List<User>();
user3.Friends.Add(user1);
context.Users.Add(user1);
context.Users.Add(user2);
context.Users.Add(user3);
context.SaveChanges();
You can also just use var user1 = new User() in this scenario where you only add new entities to the context. Creating dynamic proxies has no benefit here (unless you would set any foreign key properties to other existing entities and want to access their corresponding navigation properties after calling SaveChanges - which doesn't seem to be the case in your example).
You must declare your "List<>" Properties like this:
public virtual ICollection<User> Friends { get; set; }
If you don't use virtual keyword EF will not initialize collection for you.
In your case, you are creating new object, use private properties to initialize it for new objects:
private ICollection<User> _friends;
public ICollection<User> Friends {
get { return _friends ?? (_friends = new List<User>()); }
set { _friends = value; }
}
You have to initialize the member Friends like this:
using (var context = new AdnLineContext())
{
context.Users.Add(user1);
context.Users.Add(user2);
context.Users.Add(user3);
user1.Friends = new List<User>();
user1.Friends.Add(user2);
user3.FromWhomIsFriend.Add(user1);
context.SaveChanges();
}
I think that you might want to consider how the database should model the relationship between users. It seems like you want to have a 1:N relationship between users and itself (IE, one user can have multiple other users associated with it). To be honest the only way I know how to achieve this is a lookup table that associates two UserIds together. You could do this in Entity Code First like this:
public class FriendDefinition
{
[ForeignKey("User")]
public int UserId { get; set; }
[ForeignKey("Friend")]
public int FriendUserId { get; set; }
public virtual User User { get; set; }
public virtual User Friend { get; set; }
}
Then you could update your User class like so:
public class User
{
public int UserId { get; set; }
public String Name { get; set; }
public int Age { get; set; }
public String City { get; set; }
public String Country { get; set; }
public String Email { get; set; }
public String InterestIn { get; set; }
public virtual ICollection<FriendDefinition> Friends { get; set; }
}
Finally, you would now use this as follows:
var user = db.context.Users.First();
var firstFriend = user.Friends.First().Friend;
It's a little clunky, but I think it would serve your purpose. Code first is an excellent tool but you still have to conceptualize how the data is actually being stored in the database to model what you need.
You should use the Create method of DbSet - it will give you a fully initialised proxy.
var user1 = context.Users.Create();
user1.Name = "User1";
user1.Age = 25;
user1.City = "City1";
user1.Country = "Country1";
user1.Email = "email_1#email.com";
user1.InterestIn = "User1's interests";
//....
context.Users.Add(user1);
Hard coding things like user1.Friends = new List<User>(); is hack not a solution.

Categories