Assistance with Linq Query - c#

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;

Related

C# LINQ value substitution

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();

C# Linq nested selects not returning everything

I'm trying to select information from multiple tables via Linq. The query works if I take out the last Api query from apis in Database.PluginApis where apis.PluginId == tenantPlugin.PluginId. If I put the query back in, it results in an error System.Collections.Generic.KeyNotFoundException: The given key 'Name' was not present in the dictionary. Anyone able to see what I am doing wrong?
var results = (from tenantPlugin in Database.TenantPlugins
where tenantPlugin.TenantId == tenantId
select new TenantPlugin
{
PluginId = tenantPlugin.PluginId,
IsEnabled = tenantPlugin.IsEnabled,
TenantId = tenantPlugin.TenantId,
TenantPluginId = tenantPlugin.TenantPluginId,
Plugin = (from plugin in Database.Plugins
where plugin.PluginId == tenantPlugin.PluginId
select new Plugin
{
PluginId = plugin.PluginId,
Name = plugin.Name,
Description = plugin.Description,
ImagePath = plugin.ImagePath,
IsActive = plugin.IsActive,
Apis = (from apis in Database.PluginApis
where apis.PluginId == tenantPlugin.PluginId
select new PluginApi
{
Name = apis.Name
}).ToList<PluginApi>()
}).FirstOrDefault<Plugin>()
});
return results.ToList();
It's seems you have a perfect case to use Include() method. Try:
var results = Database.TenantPlugins
.Include(t => t.Plugin.Select(p => p.Apis))
.Where(t => t.TenantId == tenantId);

Linq: let Count result into a specified column

I've got a table Installation which can contains one or many Equipements.
And for functionnal reasons, I've overwritten my table Installation and added a field NbrEquipements.
I want to fill this field with Linq, but I'm stuck...
Due to special reasons, there is no relation between these to tables. So, no Installation.Equipements member into my class. Therefore, no Installation.Equipements.Count...
I'm trying some stuff. Here is my code:
var query = RepoInstallation.AsQueryable();
// Some filter
query = query.Where(i => i.City.RegionId == pRegionId));
int?[] etatIds = { 2, 3 };
query = (from i in query
select new Installation
{
NbrEquipements= (from e in RepoEquipement.AsQueryable()
where e.InstallationSpecialId == i.SpecialId
&& (etatIds.Contains(e.EquEtat))
select e.SasId
).Count()
});
But with this try, I got this error:
The entity or complex type 'myModel.Installation' cannot be constructed in a LINQ to Entities query
I've tried some other stuff but I'm always turning around...
Another thing that can be useful for me: It would be great to fill a field called Equipements which is a List<Equipement>.
After that, I would be able to Count this list...
Is it possible ?
Tell me if I'm not clear.
Thanks in advance.
Here is the final code:
//In the class:
[Dependency]
public MyEntities MyEntities { get; set; }
//My Methode code:
var query = MyEntities .SasInstallations.AsQueryable();
// Some filter
query = query.Where(i => i.City.RegionId == pRegionId));
var liste = new List<Installation>();
var queryWithListEquipements =
from i in query
select new
{
Ins = i,
EquipementsTemp = (from eq in MyEntities.Equipements.AsQueryable()
where eq.SpecialId == i.SpecialId
&& (etatIds.Contains(eq.SasEquEtat))
select eq
).ToList()
};
var listWithListEquipements = queryWithListEquipements.ToList();
foreach (var anonymousItem in listWithListEquipements)
{
var ins = anonymousItem.Ins;
ins.Equipements = anonymousItem.EquipementsTemp;
ins.NumberEquipements = ins.Equipements.Count();
liste.Add(ins);
}
return liste;
By the way, this is very very fast (even the listing of Equipements). So this is working exactly has I wished. Thanks again for your help everyone!
Use an anonymous type. EF does not like to instantiate entity classes inside a query.
var results = (from i in query
select new
{
NbrEquipements= (from e in RepoEquipement
where e.InstallationSpecialId == i.SpecialId
&& (etatIds.Contains(e.EquEtat))
select e.SasId
).Count()
})
.ToList();
Notice how I used select new instead of select new Installation.
You can then use the data inside the list (which is now in memory) to create instances of type Installation if you want like this:
var installations = results.Select(x =>
new Installation
{
NbrEquipements = x.NbrEquipements
}).ToList();
Here is how to obtain the list of equipment for each installation entity:
var results = (from i in query
select new
{
Installation = i,
Equipment = (from e in RepoEquipement
where e.InstallationSpecialId == i.SpecialId
&& (etatIds.Contains(e.EquEtat))
select e).ToList()
})
.ToList();
This will return a list of anonymous objects. Each object will contain a property called Installation and another property called Equipment (which is a list). You can easily convert this list (of anonymous objects) to another list of whatever type that you want.

Linq help - sub query gives null pointer exception

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()

Retrieving a field that does not belong to the model

Considere this piece of code in LINQ (please focus on var list2):
var list1 = ....... /* This linq doesnt matter. Just for clarify that it is used in the below linq */
var list2 = dba.OrderForm
.Where(q => q.OrderPriority.OrderPriorityID == orderpriorityID
&& q.StockClass.StockClassID == stockclassID
&& dba.AuditTrailLog.Where(log => q.OrderID == log.ObjectID)
.Any(log => log.ColumnInfoID == 486
&& log.OldValue == "2"
&& log.NewValue == "3")
&& dba.AuditTrailLog.Where(log2 => q.OrderID == log2.ObjectID)
.Any(log2 => log2.ColumnInfoID == 487
&& log2.OldValue == "1"
&& log2.NewValue == "2")
&& lista.Contains(q.OrderID));
This way I have in list2 a list of records that belongs to OrderForm model. I need to pass it to another model called ViewResult:
What I need is to get the variable log2.ModificationDate that belongs to AuditTrailLog table but it is not included on OrderForm Model
List<ViewResult> vr = new List<ViewResult>();
foreach (OrderForm o in list2)
{
ViewResult r = new ViewResult();
r.NumOrden = o.FormNo;
r.Title = o.Title;
r.Com = o.OrderPriority.Descr;
r.OClass = o.StockClass.Descr;
r.RodT = /* <<------ Here is where I need to assign log2.ModificationDate
vr.Add(r);
}
Thanks.
What I understand is AuditTrailLog relation is null while you are getting data. And you want to fill it with related data.
You must Include this table like:
(That means you are doing join on sql)
var list2 = dba.OrderForm.Include("AuditTrailLog")...
It is important the relation between them. "One to many" or "many to one". Use AuditTrailLog or AuditTrailLogs according to your relation.

Categories

Resources