I have two tables with the following layout
username | displayname username| FirstName | surname
---------|------------ -----------------------------
foo test foo | andrew | blah
fa display
What I'm trying to do is display all of the displayname entries in the left hand table on my page. However, if there is a match on the username in the right hand table, I want the value test to be replaced by andrew.
Example out:
username | displayname
---------|------------
foo andrew
fa display
Everything is a string.
Below is my attempt at what I need but I've messed it up as its applying the firstname value to every entry in my first table.
_users = UserService.GetAll().ToList();
_updates = UserUpdateService.GetAll().ToList();
foreach (var user in _users)
{
foreach (var update in _updates)
if (user.FirstName != "" && user.UserName == update.UserName)
{
update.DisplayName = user.FirstName;
}
else
{
update.DisplayName = user.UserName;
}
}
Is there a way to do this with a single LINQ query?
As mentioned in the comment by #HimBromBeere which I also agree that --- LINQ is just syntactic sugar, it adds absoluetely no behaviour to your code.
As to write different style of writing you can use the below code which uses few Linq operators
_users = UserService.GetAll().ToList();
_updates = UserUpdateService.GetAll().ToList();
_updates.ForEach(u =>
{
var record == _users.FirstOrDefault(us => !string.IsNullOrEmpty(us.FirstName) && string.Equals(us.UserName, u.UserName));
u.DisplayName = record != null ? record.FirstName : u.DisplayName;
});
It won't be exactly as you posted, because we are going to be creating new instances instead of modifying existing ones, but you can do this query:
var table = _users
.Select(user => new {
user.UserName,
DisplayName = _updates.Any(update =>
user.FirstName != "" && user.UserName == update.UserName)
? user.FirstName
: user.UserName
})
);
This is ordinal LINQ query with LEFT JOIN, don't try to do that with lists.
var users = UserService.GetAll();
var updates = UserUpdateService.GetAll();
var query =
from update in updates
join user in users on user.UserName equals update.UserName into gj
from user in gj.DefaultIfEmpty()
select new
{
update.UserName,
DisplayName = !string.IsNullOrEmpty(user.FirstName) ? user.FirstName : update.DisplayName
};
var result = query.ToList();
Related
I have linq where data in one of column may have spaces or special character between string, for example my survey, your's survey. I need to remove this so after filtering linq should return mysurvey and yourssurvey
column I am interested to remove spaces and special character is consultation = consultation.Name
I am using C# .net core and entity framework
var query = (from consultation in Context.Consultations
join survey in Context.Surveys on consultation.Id equals survey.ConsultationId into surveys
select new
{
consultationId = consultation.Id,
consultation = consultation.Name,
surveyId = surveys.FirstOrDefault() == null? null : surveys.Select(x=>x.Id),
survey = surveys.FirstOrDefault() == null ? null : surveys.Select(x => x.Name),
subject ="survey"
});
If you want to remove spaces & special characters in a string use like below:
consultation = Regex.Replace(consultation.Name, "[^0-9A-Za-z]+", "");
Using namespace
using System.Text.RegularExpressions;
Write an extension method like this
public static string StripSpacesAndSpecialCharacters(this string text)
{
var specChars = new string[] { "'", ";" }; // add more spec chars here
var procesesedString = text.Trim();
foreach (var spec in specChars)
{
procesesedString = procesesedString.Replace(spec, string.Empty);
}
return procesesedString;
}
then use it in your query like
var query = (from consultation in Context.Consultations
join survey in Context.Surveys on consultation.Id equals survey.ConsultationId into surveys
select new
{
consultationId = consultation.Id,
consultation = consultation.Name.StripSpacesAndSpecialCharacters(),
surveyId = surveys.FirstOrDefault() == null? null : surveys.Select(x=>x.Id),
survey = surveys.FirstOrDefault() == null ? null : surveys.Select(x => x.Name),
subject ="survey"
});
This is my action method which fetches all the users with their Id.
public JsonResult GetUsers()
{
var ret = (from user in db.Users.ToList()
select new
{
UserName = user.UserName,
// i am stuck here, i want to get all those ids whom current logged user is following
Idfollowing = user.FollowTables.Contains()
Idnotfollowing =
});
return Json(ret, JsonRequestBehavior.AllowGet);
}
the structure of FollowTable is like this:
ID UserId FollowId
1 4 11
2 4 12
2 4 13
here, current loggedin user's id is 4 and he is following 11, 12, 13 so i want to return only 11, 12 and 13 to Idfollowing and rest remaining id in the Idnotfollowing. how to get it done.
Well, i think with list or array, i will not get desired result. so, i want to add something here.
Well, with every UserName an id is also passed to view page. So, i have break them into two.Now, how to assign values to these ids.
Comapre User.Id with Current loggedin user's follow table's followId column.If match is found .i.e if id matches or found then assign that user.Id to Idfollowing and null to Idnotfollowing and vice versa in opposite case.
I have to generate follow unfollow button based on these ids returned.
public JsonResult GetUsers()
{
int currentUserId = this.User.Identity.GetUserId<int>();
var ret = (from user in db.Users.ToList()
let Id = user.FollowTables.Where(x => x.UserId == currentUserId).Select(f => f.FollowId).ToList()
let Idnot = (from user2 in db.Users
where !Id.Contains(user2.Id)
select user2.Id).ToList()
select new
{
UserName = user.UserName,
Id = Id,
//Id = user.FollowTables.Where(x => x.UserId == currentUserId)
// .Select(x => x.FollowId).Single(),
Idnot = Idnot,
It looks like you have a standard one-to-many relationship from User to FollowTable. This data model enforces that user.FollowTables only contains followers. You won't be able to fill in Idnotfollowing from the FollowTables property directly.
Something like this may work:
var query = (
from user in db.Users // note: removed ToList() here
// to avoid premature query materialization
where //TODO ADD WHERE CLAUSE HERE ?
let followIds = user.FollowTables.Select(f => f.FollowId)
let notFollowIds = (from user2 in db.Users
where !followIds.Contains(user2.Id)
select user2.Id)
select new
{
UserName = user.UserName,
Idfollowing = followIds.ToArray(),
Idnotfollowing = notFollowIds.ToArray()
})
// TODO add paging? .Skip(offset).Take(pageSize)
.ToList();
Do verify the SQL generated by this query and make sure it performs ok though...
Also, note that I removed the .ToList() from db.Users.ToList() to avoid premature query materialization. It is generally a bad idea anyway to extract all data from a table unconstrained, you will typically want to a
var ret = (from user in db.Users.ToList()
select new
{
UserName = user.UserName,
Idfollowing = user.FollowTables.Select(x=> x.Id)
Idnotfollowing = db.FollowTables.Where(x=> !user.FollowTables.Select(x=> x.Id).Contains(x.Id)).Select(x=> x.Id)
});
it's ugly but will work, there must be another better way to do.
You can simply use a Where method to filter the table and use Select to project FollowiId:-
var ret = (from user in db.Users.ToList()
select new
{
UserName = user.UserName,
Idfollowing = user.FollowTables.Where(x => x.UserId == user.Id)
.Select(x => x.FollowId).ToArray(),
Idnotfollowing = user.FollowTables.Where(x => x.UserId != user.Id)
.Select(x => x.FollowId).ToArray()
});
Assuming, Idfollowing & Idnotfollowing are array if integers (if FollowId is integer) otherwise you can replace it with ToList if its a list instead.
I’m new to LINQ and need some help with a query.
I need all of the Resources from (tblResources) that belong to the Anonymous, and Public ResourceGroups (tblResourceGroups). In addition, I also need all of the Resources that belong to any of the ResourceGroups that the currentUser belongs to.
If the currentUser isn’t logged in (currentUser == null) then only Resources belonging to Anonymous, and Public ResourceGroups should be returned.
NOTE: My data model does not contain an entity for tblResourceAccess. I'm not sure why this entity wasn't added when the model was created.
string currentUser = IdentityHelper.GetUserIdFromRequest(Context.Request);
var result = from r in DbContext.Resources
where r.Active == true // && r.ResourceGroups?????
select new
{
ResourceTypeName = r.ResourceType.Name,
Name = r.Name,
Version = r.Version,
Description = r.Description,
Path = r.Path,
Active = r.Active
};
The tblResourceAccess was abstracted away by EF and the ResourceGroups property added to the Resource table to provide the functionality. Using this relationship we can piece together the following query:
from r in DBContext.Resources.ToList()
where (currentUser == null
&& ("anonymous,public").Contains(
r.ResourceGroups.Name.ToLower()))
|| (currentUser != null)
select new
{
ResourceTypeName = r.ResourceType.Name,
Name = r.Name,
Version = r.Version,
Description = r.Description,
Path = r.Path,
Active = r.Active
};
Finally, after a lot of trial and error! I'm not sure if this is best way performance wise to implement this query, but it works.
Thanks for your help #The Sharp Ninja!
string currentUser = IdentityExtensions.GetUserId(HttpContext.Current.User.Identity);
var resources = from r in DbContext.Resources
where r.ResourceGroups.Any(
rg => rg.Name == "Anonymous" ||
rg.Name == "Public" ||
rg.ResourceUserGroups.Any(ug => ug.UserID == currentUser))
select r;
I writing a email system where we have a table of users "tblUsers" and a table of messages. A user can have many messages (from other users in tblusers) in his or her inbox (one:many).
In tblUsers table, I have a column called ImageURL (string) that contains the URL to the user's avatar. In this case, I'm looping through the messages in an inbox belonging to a user and what I'm trying to do is, once I get the message, walk up the tree to the tblUser and get the value in the ImageURL column for the owner of that message as marked "SenderAvatar" below.
Here's what I tried. The problem is that the sub linq for SenderAvatar below is throwing a nullpointer exception even though I have confirmed that there is a value for ImageURL (this is dev so there's only three users). Somehow my logic and linq's logic is at odds here. Can someone please help? Thanks!
Edit
I found two bugs. The first bug is Dzienny pointed me to the right direction where I was comparing apples and oranges. The second bug is FromUserId = ux.tblUserId, where I'm setting the current user id to FromUserId Guys, thank you for all your help on this.
public List<UserInboxMsg> GetUserInboxMsg(IKASLWSEntities conx, int userid)
{
var u = (from m in conx.tblUsers where m.Id == userid select m).FirstOrDefault();
if (u != null)
{
return (from ux in u.tblInboxes
orderby ux.CreationTS descending
select new UserInboxMsg
{
CreationTS = ux.CreationTS,
ExpirationDate = ux.ExpirationDate,
FromUserId = ux.tblUserId,
HasImage = ux.HasImage,
ImageId = ux.ImageId ?? 0,
IsDeleted = ux.IsDeleted,
IsRead = ux.IsRead,
MsgId = ux.Id,
MsgSize = ux.MessageSize,
ParentId = ux.ParentId,
Title = ux.Title,
ToUserId = userid,
FromUserName = ux.Title,
SenderAvatar = conx.tblMessages.Where(mu=>mu.Id == ux.Id).FirstOrDefault().tblUser.ImageURL,
Message = ux.Message
}).ToList<UserInboxMsg>();
}
else
{
return new List<UserInboxMsg>();
}
}
}
If in the entity-framework, there is a foreign key reference between the two tables you could probably do this:
SenderAvatar = conx.tblMessages.FirstOrDefault( mu=>mu.Id == ux.Id).ImageURL,
Try this.
public List<UserInboxMsg> GetUserInboxMsg(IKASLWSEntities conx, int userid)
{
var u = (from m in conx.tblUsers where m.Id == userid select m).FirstOrDefault();
if (u != null && conx != null)
{
return (from ux in u.tblInboxes
orderby ux.CreationTS descending
select new UserInboxMsg
{
...
...
SenderAvatar = conx.tblMessages.Any(mu=>mu.Id == ux.Id) ? (conx.tblMessages.First(mu=>mu.Id == ux.Id).tblUser != null? conx.tblMessages.First(mu=>mu.Id == ux.Id).tblUser.ImageURL : null) : null,
Message = ux.Message
}).ToList<UserInboxMsg>();
}
else
{
return new List<UserInboxMsg>();
}
}
}
if you are getting null for the Avatar, it is either because there are no entries in tblMessages where mu.Id equals ux.Id or the tblMessage entry is there but the tblUser property is null
There are several problems here.
The first is that the second statement is executed in memory, while it's possible to make the whole query run as SQL:
from u in conx.tblUsers where m.Id == userid
from ux in u.tblInboxes
orderby ux.CreationTS descending
select new UserInboxMsg
{
CreationTS = ux.CreationTS,
ExpirationDate = ux.ExpirationDate,
FromUserId = ux.tblUserId,
HasImage = ux.HasImage,
ImageId = ux.ImageId ?? 0,
IsDeleted = ux.IsDeleted,
IsRead = ux.IsRead,
MsgId = ux.Id,
MsgSize = ux.MessageSize,
ParentId = ux.ParentId,
Title = ux.Title,
ToUserId = userid,
FromUserName = ux.Title,
SenderAvatar = conx.tblMessages.Where(mu => mu.Id == ux.Id)
.FirstOrDefault().tblUser.ImageURL,
Message = ux.Message
}
This has three benefits:
you fetch less data from the database
you get rid of the null reference exception, because SQL doesn't have null references. It just returns null if a record isn't found.
you can return the result of this statement without the if-else.
Second, less important, is that you should use a navigation property like Inbox.Messages in stead of joining (sort of) the inbox and its messages. This makes it less likely that you use the wrong join variables and it condenses your code:
SenderAvatar = ux.Messages.
.FirstOrDefault().User.ImageURL,
Now if there is no avatar, there is no avatar. And there's no null reference exception.
(By the way, you can see that I hate these prefixes in class and property names).
I can only guess this part of your code is wrong : SenderAvatar = conx.tblMessages.Where(mu=>mu.Id == ux.Id).FirstOrDefault().tblUser.ImageURL
I think for example you should use (mu=>mu.UserId == ux.Id) instead of (mu=>mu.Id == ux.Id). In your code, you are comparing "Id" of a table to "Id" of another table which normally in one to many relations is wrong. (only works in one to one relations)
I said I can guess because you didn't mention any information about tblInboxes and tblMessages fields. If you could provide me more information about their structure, I could answer in more detail.
By the way to make your code more clear you can use:
var u = conx.tblUsers.FirstOrDefault(m=>m.Id == userid);
instead of
var u = (from m in conx.tblUsers where m.Id == userid select m).FirstOrDefault();
OR
conx.tblMessages.FirstOrDefault(mu=>mu.Id == ux.Id)
instead of
conx.tblMessages.Where(mu=>mu.Id == ux.Id).FirstOrDefault()
I have the following tables
Users
- ID
- FirstName
- LastName
MultiplyItems
- ItemID
- Title
UserMultiplyItems
- UserID
- ItemID
I have a variable
List<int> delegateList = {1, 3, 5};
where 1, 3, 5 are ItemID
I want to select all users, where at least one ItemID linked selectable user.
I try the following:
var result = from i in _dbContext.Users
where
((delegateList == null) || i.MultiplyItems.Any(p=> delegateList.Any(a => a == p.ItemID)))
select new UserModel()
{
....
};
but it does not work. error:
Cannot compare elements of type 'System.Collections.Generic.List`1'.
Only primitive types, enumeration types and entity types are
supported.
How to do it correctly?
Thanks
I would write this one:
var filteredUsers = delegateList == null
? _dbContext.Users
: _dbContext.Users.Where(user => user.MultiplyItems
.Any(item => delegateList.Contains(item.Id)));
var result = filteredUsers.Select(user => new UserModel
{
//the UserModel initialization
});
You should not check the following line inside the query:
delegateList == null
It is translated to SQL, and SQL has no idea about what is List and how to compare it with null.
var result = from i in _dbContext.Users
from mi in i.multiplyItems
select new yourClass();
if(delegateList!=null)
{
result = result.Where(delegateList.contains(mi.ItemID));
}
result.ToList();
I dont have visual studio open to test, but it should be a lot like that.
I'm not exactly sure if this is what you wanted, but i thought its better to try and help.
This will output all users from the Users Table where the ItemID is contained in the delegateList. The magic lays in the Contains operator you can get elements from list a contained in list b
var selection = from a in db.UserMultiplyItems
from b in db.Users
where delegateList.Contains(a.ItemID) && a.UserID == b.ID
select b;
Try changing it to:
var result = from u in _dbContext.Users
where
((delegateList == null) || u.MultiplyItems.Any( mi => delegateList.Contains(mi.ItemID)))
select new UserModel()
{
....
};
Note I've also renamed your "i" and "p" things to "u" and "mi" to make it easier to read.
Alternatively you could not use LINQ at all and just stick with lambda expressions:
List<UserModel> usersWithItems =
context
.Users
.Where(u => u.MultiplyItems.Any(mi => (delegateList == null) || (delegateList.Contains(mi.ItemID))))
.Select(um => (new UserModel() { ... } ) )
.ToList();
Which personally I prefer, it means you don't need to know linq at all.