Please look at my code below
var result = (from c in db.vCompanies
where id == c.id
from user in db.Users
select new ViewCompanyModel()
{
Id = c.id,
Descripton = c.Descripton,
Name = c.Name,
ImageLink = c.ImageLink,
AdminEmail = c.Email,
Users = db.eUsers
.Where(o => o.CompanyName.Equals(c.Name))
.Select(o => new UserManageViewModel.UserViewModel
{
Id = o.UserId,
Name = o.LastName,
Email = o.Email,
Company = o.CompanyName,
ListOfRoles = user.AspNetUsers.AspNetRoles.Select(x=>x.Name).ToList()
})
}).FirstOrDefault();
I receive not correct data in ListOfRoles - I receive data only of first user.
I tried to add something like this
Where(x=>x.UserId == o.UserId)
I also tried change for this
ListOfRoles = db.Users.Where(x=>x.UserId == o.UserId).Select(x=>x.AspNetUsers.AspNetRoles)
But in this case I can't select x.Name.
I am doing something wrong.
Please advise me.
if(result != null)
{
foreach (var user in result.Users)
{
var roles = db.Users.Where(x => x.UserId == user.Id).Select(x => x.AspNetUsers.AspNetRoles).FirstOrDefault();
user.ListOfRoles = roles.Select(x => x.Name).ToList();
}
}
This is the answer! It's work!
Related
I want to add a value in IQueryable get from another IQueryable here is my code
var queryReturnRequest = query.Include(x => x.Assignment)
.ThenInclude(x => x.Asset).Select(x => new ReturnRequestDto
{
AssetCode = x.Assignment.Asset.Code,
AssetName = x.Assignment.Asset.Name,
Id = x.Id,
ReturnDate = x.ReturnDate,
AssignedDate = x.Assignment.AssignedDate,
State = x.State,
UserRequestId = x.UserRequestId,
UserAcceptId = x.UserAcceptId,
RequestedBy = userList.FirstOrDefault(u => u.Id == x.UserRequestId) != null ? userList.FirstOrDefault(u => u.Id == x.UserRequestId).UserName : "",
AcceptedBy = userList.FirstOrDefault(u => u.Id == x.UserAcceptId) != null ? userList.FirstOrDefault(u => u.Id == x.UserAcceptId).UserName : "",
});
But it was crashed and return error 500, I found out that was because of userList.FirstOrDefault but when I console.log() it just work fine. So what wrong with it and is there another way to add it in IQueryable ?
I tried to user for loop to add IQueryable but it just return to null after get our of loop.
Try rewrite query in the following way. Also note that Select discards Includes, so avoid them in such case.
var queryReturnRequest = query
.Select(x => new ReturnRequestDto
{
AssetCode = x.Assignment.Asset.Code,
AssetName = x.Assignment.Asset.Name,
Id = x.Id,
ReturnDate = x.ReturnDate,
AssignedDate = x.Assignment.AssignedDate,
State = x.State,
UserRequestId = x.UserRequestId,
UserAcceptId = x.UserAcceptId,
RequestedBy = userList.Where(u => u.Id == x.UserRequestId).Select(x => x.UserName).FirstOrDefault() ?? "",
AcceptedBy = userList.Where(u => u.Id == x.UserAcceptId).Select(x => x.UserName).FirstOrDefault() ?? "",
});
I want to sort the data based on CreatedUtc time. I have tried to use Reverse function, it seems to work out but still looking for some alternate option.
var result = _participantRepo.AsQueryable().Where(x => x.Id == ParticipantId).SelectMany(x =>
x.Relations).ToList().Where(x => x.UserId != AppUserId).Select(r => new RelationVM
{
IsOwner = r.UserId == participant.CreatedByUserId,
FirstName = r.FirstName,
LastName = r.LastName,
Email = r.Email,
UserId = r.UserId,
RelationType = r.RelationType,
Role = r.Role,
IsAccepted = r.IsAccepted,
AvatarUrl = r.AvatarUrl,
CreatedUtc = r.CreatedUtc
}).Reverse().ToList();
There are 2 things you need to concern:
You can sort the elements of a sequence by using OrderBy
You should not .ToList() when you have not done, So you might to read LINQ deferred (or immediate?) execution to have a better understanding.
As a result, your query should look like this
var result = _participantRepo.AsQueryable().Where(x => x.Id == ParticipantId).SelectMany(x =>
x.Relations).Where(x => x.UserId != AppUserId).Select(r => new RelationVM
{
IsOwner = r.UserId == participant.CreatedByUserId,
FirstName = r.FirstName,
LastName = r.LastName,
Email = r.Email,
UserId = r.UserId,
RelationType = r.RelationType,
Role = r.Role,
IsAccepted = r.IsAccepted,
AvatarUrl = r.AvatarUrl,
CreatedUtc = r.CreatedUtc
}).Reverse().OrderBy(g => g.CreatedUtc).ToList();
How about .OrderBy(g => g.CreatedUtc) ?
I don't have a lot of experience with IQueryable
What I am trying to do is search for a user based on a list of passed in constraints that can either be a username, or phone number. Depending on the type I want to return limited information. I then want to combine the 3 IQueryables into one and combine entries with matching id and username to maintain the most information.
Here is what i have so far:
public IQueryable<User> Search(String[] criteria)
{
var query = Database.Get<User>();
IQueryable<User> phoneQuery = null;
IQueryable<User> emailQuery = null;
IQueryable<User> nameQuery = null;
foreach (String str in criteria)
{
// Check if it is a phone number
if (Regex.IsMatch(str, #"([0-9]{3})?[-. ]?([0-9]{3})[-. ]?([0-9]{4})$"))
{
phoneQuery = query.Where(
u => u.PhoneNumber.ToLower() == (str))
.Select(i => new User
{
Id = i.Id,
UserName = i.Name,
PhoneNumber = i.PhoneNumber
})
}
// check if it is an email
else if (criteria.Contains("#"))
{
emailQuery = query.Where(
u => u.Email.ToLower() == (str))
.Select(i => new User
{
Id = i.Id,
UserName = i.Name,
Email = i.Email
})
}
else
{
nameQuery = query.Where(
u => u.UserName.ToLower() == (str))
.Select(i => new User
{
Id = i.Id,
UserName = i.Name,
})
}
}
// Merge the 3 queries combining entries if the username and id match and maintain the maximum amount of information
return query;
}
There are a few issues with your code:
ToList() will execute the query. If you call AsQueryable() later, you simply create an object query on the local objects. This basically loses the notion of IQueryable, so you'd better delete all ToList() and AsQueryable() calls.
You can make it a single query instead of merging the three queries, like so:
Expression predicateBody = Expression.Constant(false);
Expression userParameter = Expression.Parameter("user", typeof(User));
Expression userUserName = Expression.MakeMemberAccess(...);
Expression userPhoneNumber = Expression.Cal(...);
Expression userEmail = Expression.Call(...);
foreach (String str in criteria)
{
// Check if it is a phone number
if (Regex.IsMatch(str, #"([0-9]{3})?[-. ]?([0-9]{3})[-. ]?([0-9]{4})$"))
{
predicateBody = Expression.Or(predicateBody, Expression.Equals(userPhoneNumber, Expression.Constant(str)));
}
// check if it is an email
else if (criteria.Contains("#"))
{
predicateBody = Expression.Or(predicateBody, Expression.Equals(userEmail, Expression.Constant(str)));
}
else
{
predicateBody = Expression.Or(predicateBody, Expression.Equals(userUserName, Expression.Constant(str)));
}
}
return query.Where(Expression.Lambda<Func<User, bool>>(predicateBody, userParameter))
.GroupBy(u => u.Id)
.Select(users => new User()
{
Id = users.Key,
UserName = users.Select(u => u.UserName).Intersect(criteria).FirstOrDefault(),
Email = users.Select(u => u.Email).Intersect(criteria).FirstOrDefault(),
PhoneNumber = users.Select(u => u.PhoneNumber).Intersect(criteria).FirstOrDefault()
});
Edit Sorry, I misunderstood the merging problem.
Edit2 If the criterias are sorted in advance, there is also a solution that does not require to manually creating the expression tree.
Edit3 I see, I forgot the part with the limited information.
var phoneNumbers = new List<string>();
var emails = new List<string>();
var userNames = new List<string>();
foreach (var str in criteria)
{
// Check if it is a phone number
if (Regex.IsMatch(str, #"([0-9]{3})?[-. ]?([0-9]{3})[-. ]?([0-9]{4})$"))
{
phoneNumbers.Add(criteria);
}
// check if it is an email
else if (criteria.Contains("#"))
{
emails.Add(crietria);
}
else
{
userNames.Add(criteria);
}
}
return query.Where(u => phoneNumbers.Contains(u.PhoneNumber)
|| emails.Contains(u.Email)
|| userNames.Contains(u.UserName))
.GroupBy(u => u.Id)
.Select(users => new User()
{
Id = users.Key,
UserName = users.Select(u => u.UserName).Intersect(userNames).FirstOrDefault(),
Email = users.Select(u => u.Email).Intersect(emails).FirstOrDefault(),
PhoneNumber = users.Select(u => u.PhoneNumber).Intersect(phoneNumbers).FirstOrDefault()
});
This is what I ended up going with.
public IQueryable<User> Search(String[] criteria)
{
var query = Database.Get<User>();
List<User> res = new List<User>();
foreach (String str in criteria)
{
// Check if it is a phone number
if (Regex.IsMatch(str, #"([0-9]{3})?[-. ]?([0-9]{3})[-. ]?([0-9]{4})$"))
{
var users = query.Where(
u => u.PhoneNumber.ToLower() == (str))
.Select(i => new User
{
Id = i.Id,
UserName = i.Name,
Email = null,
PhoneNumber = i.PhoneNumber
});
// Multiple users can have the same phone so add all results
foreach (User u in users)
{
if (u != null) { res.Add(u); }
}
}
// Check if it is an email match
else if (str.Contains("#"))
{
var user = query.Where(
u => u.Email.ToLower() == (str))
.Select(i => new User
{
Id = i.Id,
UserName = i.Name,
Email = i.Email,
PhoneNumber = null
}).SingleOrDefault<User>(); // Only one user can use an email
if (user != null) { res.Add(user); }
}
// Otherwise it is a username
// NOTE: If a username is all digits and dashes it won't be
// searched for because it is interpreted as a phone number!
else
{
var user = query.Where(
u => u.UserName.ToLower() == (str))
.Select(i => new User
{
Id = i.Id,
UserName = i.Name,
Email = null,
PhoneNumber = null
}).SingleOrDefault<User>(); // Only one user can use an email
if (user != null) { res.Add(user); }
}
}
query = res.AsQueryable();
// Group the results by username and id and return all information that was found
query = from u in query
group u by new
{
u.Id,
u.UserName
} into g
select new User()
{
Id = g.Key.Id,
UserName = g.Key.UserName,
Email = g.Select(m => m.Email).SkipWhile(string.IsNullOrEmpty).FirstOrDefault(),
PhoneNumber = g.Select(m => m.PhoneNumber).SkipWhile(string.IsNullOrEmpty).FirstOrDefault()
};
return query;
}
I have the following database code:
static IEnumerable<dynamic> GetData(bool withchildren) {
using (var model = new testEntities()) {
var res = default(IQueryable<dynamic>);
if (withchildren) {
res = model.UserSet
.Where(u => u.name != "")
.Select(u => new {
Name = u.name,
Email = u.email,
Groups = u.GroupSet.Select(g => new {
Name = g.name,
Id = g.Id
})
});
} else {
res = model.UserSet
.Where(u => u.name != "")
.Select(u => new {
Name = u.name,
Email = u.email
});
}
return res.ToList()
}
}
I would like to shrink the code and write it like this:
static IEnumerable<dynamic> GetData(bool withchildren) {
using (var model = new testEntities()) {
var res = default(IQueryable<dynamic>);
res = model.UserSet
.Where(u => u.name != "")
.Select(u => {
dynamic item = new {
Name = u.name,
Email = u.email
};
if(withchildren) {
item.Groups = u.GroupSet.Select(g => new {
Name = g.name,
Id = g.Id
});
}
return item;
});
return res.ToList();
}
}
But Visual Studio complains, that it cannot convert the lambda expression into an expression tree.
My question is, is there a way to accomplish that with the Entity Framework and Linq? I really wouldn't want to use ADO.net directly.
Maybe there is even a better version to shrink it, than the code that I imagine.
Here is a related question with Linq-To-Objects.
EDIT
Before someone asks, I use dynamic in the example code to make it a bit easier and faster.
EDIT 2
My goal with this approach is, to only query the fields I need to improve performance. Check http://www.progware.org/Blog/post/Slow-Performance-Is-it-the-Entity-Framework-or-you.aspx.
At the moment we use something like
static IEnumerable<dynamic> GetData(bool withchildren) {
using (var model = new testEntities()) {
var res = default(IQueryable<dynamic>);
res = model.UserSet
.Where(u => u.name != "")
.ToList();
return res;
}
}
And the performance is, according to Glimpse, horrible.
EDIT 3
Short side note, I made up some quick and dirty code. That is, why the foreach at the end was not needed. The actual code is not available at the moment.
Is there any reason you couldn't use:
res = model.UserSet
.Where(u => u.name != "")
.Select(u => new {
Name = u.name,
Email = u.email,
Groups = withchildren
? u.GroupSet.Select(g => new {
Name = g.name,
Id = g.Id
})
: null;
})
};
or perhaps:
res = model.UserSet
.Where(u => u.name != "")
.ToList() // ToList() here to enumerate the items
.Select(u => {
dynamic item = new {
Name = u.name,
Email = u.email
};
if(withchildren) {
item.Groups = u.GroupSet.Select(g => new {
Name = g.name,
Id = g.Id
});
}
return item;
});
One approach that would allow you to eliminate some code would be:
var res = model.UserSet.Where(u => u.name != "");
if (withchildren) {
res = res.Select(u => new {
Name = u.name,
Email = u.email,
Groups = u.GroupSet.Select(g => new {
Name = g.name,
Id = g.Id
})
});
} else {
res = res.Select(u => new {
Name = u.name,
Email = u.email
});
}
One of the most requested by community feature is support of multi-line expressions in EF,
however so far, you can use only conditional operator "?:" as well as wrap result in
one common type, so both your results will have to have "Groups" field.
Also there are an extensions to linq-providers such as https://www.nuget.org/packages/LinqKit/ ,
however they are based on own conventions so any developer should study it in depth before
taking advance in applying and supporting code written upon that extensions.
I've been toying with this for a while and just can't get it. I'm new to Linq, C# and these Lambda things.
What I want to do is group entities according to two properties on each entity. It's a Message entity:
Message
{
int UserId; //The user generating the message
int UserIdTo; //The receiver of the message
|...| // Other stuff
}
So, I want it so that these UserId=5, UserIdTo=6 and UserId=6, UserIdTo=5 would be in the same group.
Here's my start:
var groupList = (from m in db.Messages
where m.UserId == userId || m.UserIdTo == userId
join u in db.Users on m.UserId equals u.UserId
join w in db.Users on m.UserIdTo equals w.UserId
orderby m.MessageTimestamp descending
select new DomMessage
{
MessageId = m.MessageId,
MessageContent = m.MessageContent,
MessageTimestamp = m.MessageTimestamp,
UserId = m.UserId,
UserIdTo = m.UserIdTo,
ScreenName = u.ScreenName,
ScreenName2 = w.ScreenName
}).GroupBy(m=>m.UserId == userId)
.ToList();
This does the first bit of grouping by UserId, but I'm stuck on trying to extend this so that where any UserId value in the resulting group equals the UserIdTo somewhere else add that to this group?
EDIT: I need the result to go to a List because there is other stuff I need to do with it...
Thanks!
Try this:
var payload = new[]
{
new{ To = 1, From = 2, Message = "msj1" },
new{ To = 1, From = 2, Message = "msj2" },
new{ To = 2, From = 1, Message = "msj3" },
new{ To = 4, From = 1, Message = "msj4" },
new{ To = 1, From = 3, Message = "msj5" }
};
var groupped = payload.Select(x => new { Key = Math.Min(x.To, x.From) + "_" + Math.Max(x.To, x.From), Envelope = x }).GroupBy(y => y.Key).ToList();
foreach (var item in groupped)
{
Console.WriteLine(String.Format(#"Group: {0}, messages:", item.Key));
foreach (var element in item)
{
Console.WriteLine(String.Format(#"From: {0} To: {1} Message: {2}", element.Envelope.From, element.Envelope.To, element.Envelope.Message));
}
}
Try the following GroupBy expression:
.GroupBy(m => Math.Min(m.UserId, m.UserIdTo) + ',' + Math.Max(m.UserId, m.UserIdTo))
I think this is the easiest way:
var groupList = from a in (
from m in db.Messages
where m.UserId == userId || m.UserIdTo == userId
join u in db.Users on m.UserId equals u.UserId
join w in db.Users on m.UserIdTo equals w.UserId
select new
{
MessageId = m.MessageId,
MessageContent = m.MessageContent,
MessageTimestamp = m.MessageTimestamp,
UserId = m.UserId,
UserIdTo = m.UserIdTo,
ScreenName = u.ScreenName,
ScreenName2 = w.ScreenName
})
group a by new {
UserId = a.UserId,
UserIdTo = a.UserIdTo
} into grp
orderby grp.Max(a => a.MessageTimestamp) descending
select new
{
UserId = grp.Key.UserId
UserIdTo = grp.Key.UserIdTo,
MessageId = grp.Max(a => a.MessageId),
MessageContent = grp.Max(a => a.MessageContent),
MessageTimestamp = grp.Max(a => a.MessageTimestamp),
ScreenName = grp.Max(a => a.ScreenName),
ScreenName2 = grp.Max(a => a.ScreenName2)
}
You have to tell it what to do with the fields you are not grouping by. In this case I got the MAX value for each.