here i have a group containing many users and assets ,i want list of assets linked to a single user.
What is the best way of using linq.
public class Asset
{
public Guid Id { get; set; }
public virtual ICollection<GroupToAssets> GroupToAssets { get; set; }
}
public class User: IdentityUser
{
public virtual ICollection<GroupToUsers> GroupToUsers { get; set; }
}
public class GroupToAssets
{
public Guid GroupId { get; set; }
public virtual Group Group { get; set; }
public Guid AssetId { get; set; }
public virtual Asset Asset { get; set; }
}
public class GroupToUsers
{
public Guid GroupId { get; set; }
public virtual Group Group { get; set; }
public string UserId { get; set; }
public virtual User User { get; set; }
}
I think the following linq satisfy my case don't know if there is a better method
var Groups =
await _Context.Groups.Where(x => x.GroupToUsers.Any(d => d.UserId == UserId && d.GroupId == x.Id)).ToListAsync();
var assets =
await _Context.GroupToAssets.Where(x => Groups.Contains(x.Group)).Select(x => new { x.Asset }).Distinct().ToListAsync();
Related
I have three classes set up like this:
public class Item
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<UserItem> UserItems { get; set; }
}
public class User
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<UserItem> UserItems { get; set; }
}
public class UserItem
{
public int ID { get; set; }
public int UserID { get; set; }
public int ItemID { get; set; }
}
Entity Framework generates the tables in the SQL Server database with the correct foreign keys. I can query the Item and User to get the UserItem collections within like this:
var a = context.DBUser.Include(s => s.UserItems);
var b = context.DBItem.Include(s => s.UserItems);
What I am not happy about is how I get from the User to the Item. In SQL I would do something like this
SELECT * FROM Users U
LEFT JOIN UserItems UI
ON U.ID = UI.UserID
LEFT JOIN Items I
ON UI.ItemID = I.ID
In C# I've needed to resort to this:
var c = from user in _context.DBUsers
join userItem in _context.DBUserItems
on user.ID equals userItem.UserID into ui
from userItem in ui.DefaultIfEmpty()
join item in _context.DBItems
on userItem.ItemID equals item.ID into i
from item in i.DefaultIfEmpty()
select new
{
user,
userItem,
item
};
Which given there should already be relationships between the tables in EF doesn't feel right
You need to add navigations to UserItem also:
public class UserItem
{
public int ID { get; set; }
public int UserID { get; set; }
public int ItemID { get; set; }
public User User { get; set; }
public Item Item { get; set; }
}
Then your query can be like this:
var users = context.DBUser
.Include(x => x.UserItems)
.ThenInclude(x => x.Item);
Also note that if you are using .NET 5, the UserItem "join entity" can be omitted altogether:
public class Item
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<User> Users { get; set; }
}
public class User
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<Item> Items { get; set; }
}
Which would simplify your query:
var users = context.DBUser.Include(x => x.Items);
Whether it is possible so: it is a Messenger where the entity User content ICollection User that are collection Friends consist from the same Users?
If that possible please tell me how create a correct relationship between them in the DbContext file?
Or how better build this relationship. May be create separate entity?
Thanks in advance!
namespace Tinkl.Data.Core.Domain
{
public class User
{
public User()
{
Contacts = new List<User>();
Conversations = new List<Conversation>();
Invites = new List<User>();
}
public int UserId { get; set; }
public string NickName { get; set; }
public string EMail { get; set; }
public string Password { get; set; }
public DateTime? CreationDate { get; set; }
public DateTime? ExitDate { get; set; }
public string Picture { get; set; }
public string Status { get; set; }
public virtual ICollection<User> Invites { get; set; }
public virtual ICollection<User> Contacts { get; set; }
public virtual ICollection<Conversation> Conversations { get; set; }
}
}
You are going in right direction, see my below code same type of self-relationship in EF code first
public class ContentEntityRef : BaseModel
{
public ContentEntityRef()
{
RoleRefs = new HashSet<RoleRef>();
}
public int EntityId { get; set; }
public string EntityName { get; set; }
public int? ParentEntityId { get; set; }
public virtual ICollection<RoleRef> RoleRefs { get; set; }
public virtual ContentEntityRef Parent { get; set; }
public virtual ICollection<ContentEntityRef> Children { get; set; }
}
I had created seprate configuration file, you can same use in dbContext "OnModelCreating" method.
internal class ContentEntityRefConfiguration : EntityTypeConfiguration<ContentEntityRef>, IEntityConfiguration
{
public ContentEntityRefConfiguration()
{
this.HasKey(x => x.EntityId).Property(t => t.EntityId).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
this.Property(x => x.EntityName).IsRequired().HasMaxLength(50);
this.HasMany(c => c.Children).WithOptional(c => c.Parent).HasForeignKey(c => c.ParentEntityId);
this.HasMany<RoleRef>(role => role.RoleRefs)
.WithMany(content => content.ContentEntities)
.Map(contentRole =>
{
contentRole.MapLeftKey("EntityID");
contentRole.MapRightKey("RoleID");
contentRole.ToTable("RoleEntityMap");
});
}
}
hope this will help you :)
I have my models like this:
Goup.cs
GroupUser (pivot table)
ApplicationUser (User) -> 4. Profile
And now I want to show the data in Profile on a details page when the User belongs to the group. I'm doing this like this:
private IEnumerable<GroupUser> GetUsers(int groupId)
{
IEnumerable<GroupUser> model = null;
if(groupId == 0)
{
model = _kletsContext.GroupUser.OrderByDescending(o => o.GroupId).AsEnumerable();
}
else
{
model = _kletsContext.GroupUser.Where(g => g.GroupId == groupId).Include(p => p.User.Profile).OrderByDescending(o => o.GroupId).AsEnumerable();
}
return model;
}
This works, if I just want to display the UserId, ... (so the data in the Pivot table) with this code:
#model IEnumerable<App.Models.GroupUser>
#if(Model != null && Model.Count() > 0)
{
#foreach(var user in Model)
{
#user.UserId</h2>
}
}
But for some reason I can't display the data in the Included tables?
Normally you would do something like this: #user.User.Profile.XXXX but then I get the error: System.NullReferenceException: Object reference not set to an instance of an object
So this would mean the return is null, but there are users in the pivot table with a profile.
The models:
Group.cs:
namespace App.Models
{
public class Group : Item
{
public Group() : base()
{
}
[Key]
public Int16 Id { get; set; }
public string Name { get; set; }
public string Images { get; set; }
/* Foreign Keys */
public Nullable<Int16> RegionId { get; set; }
public virtual Region Region { get; set; }
public virtual ICollection<Lets> Lets { get; set; }
public virtual ICollection<GroupUser> Users { get; set; }
}
}
ApplicationUser:
namespace App.Models.Identity
{
public class ApplicationUser : IdentityUser
{
public string Description { get; set; }
public DateTime CreatedAt { get; set; }
public Nullable<DateTime> UpdatedAt { get; set; }
public Nullable<DateTime> DeletedAt { get; set; }
/* Virtual or Navigation Properties */
public virtual Profile Profile { get; set; }
public virtual ICollection<GroupUser> Groups { get; set; }
public virtual ICollection<Lets> Lets { get; set; }
public virtual ICollection<Category> Categories { get; set; }
public virtual ICollection<Region> Regions { get; set; }
public virtual ICollection<Status> Status { get; set; }
public virtual ICollection<Tag> Tags { get; set; }
}
}
GroupUser:
namespace App.Models
{
public class GroupUser
{
public GroupUser()
{
}
public Nullable<Int16> GroupId { get; set; }
public string UserId { get; set; }
public virtual Group Group { get; set; }
public virtual ApplicationUser User { get; set; }
}
}
Profile.cs:
namespace App.Models
{
public class Profile : Item
{
public Profile() : base()
{
}
[Key]
public string UserId { get; set; }
public string FirstName { get; set; }
public string SurName { get; set; }
public string Email { get; set; }
public string Gender { get; set; }
public Int16 Age { get; set; }
public string City { get; set; }
public string Image { get; set; }
public Int16 Credits { get; set; }
public Int16 Postalcode { get; set; }
}
}
How can i display the nested data with razor?
model = _kletsContext.GroupUser.Where(g => g.GroupId == groupId)
.Include(gu => gu.User)
.ThenInclude(u => u.Profile)
.OrderByDescending(o => o.GroupId)
.AsEnumerable();
Don't get freaked out when intellisense doesn't work for the ThenInclude, just type it, it will compile.
try to include the user-reference
model = _kletsContext.GroupUser.Where(g => g.GroupId == groupId).Include(p => p.User).Include(p => p.User.Profile).OrderByDescending(o => o.GroupId).AsEnumerable();
Hi I have been trying to change the shape of one data structure into another for a few days with no luck.
The original data structure is as follows.
public class Role : IdentityRole
{
public string Name { get; set; }
public virtual List<PermissionInRole> PermissionsInRole { get; set; }
}
public class PermissionInRole
{
public Guid Id { get; set; }
public virtual Permission Permission { get; set; }
public virtual Feature Feature { get; set; }
}
public class Permission
{
public Guid Id { get; set; }
public string Description { get; set; }
}
public class Feature
{
public Guid Id { get; set; }
public string Name { get; set; }
public virtual Application Application { get; set; }
}
public class Application
{
public Guid Id { get; set; }
public string Name { get; set; }
public virtual List<Feature> Features { get; set; }
}
I would like to use Linq to change it to this shape.
public class ApplicationApiModel
{
public string Name { get; set; }
public ICollection<FeaturesApiModel> Features { get; set; }
}
public class FeaturesApiModel
{
public string Name { get; set; }
public IEnumerable<PermissionsApiModel> Permissions { get; set; }
}
public class PermissionsApiModel
{
public string PermissionName { get; set; }
public IEnumerable<Role> Role { get; set; }
}
public class Role
{
public string Name { get; set; }
}
I would like to have a Collection of Applications, that contains a Collection of Features, that contains a collection of permissions , that contains a collection of roles that have the parent permission.
I am sure that this can be done with linq however I have not managed any help would be great.
After many attempts i ended up with following working code
var query = from role in roles
group role by role.PermissionsInRole
into g
select new
{
PinR = g.Key,
role = g.ToList()
};
var transferList = (from w in query
from pr in w.PinR
select new
{
Feature = pr.Feature, Permission = pr.Permission, TransferRole = w.role.Single()
})
.ToList()
.GroupBy(o => o.Feature, (key, o) =>
new FeaturesApiModel
{
Name = key.Name,
Permissions = o.GroupBy(transferObject => transferObject.Permission, (subKey,transferObject) =>
new PermissionsApiModel
{
PermissionName = subKey.Description,
Role = transferObject.Select(flatTransferObject => new RoleAPIModel {Name = flatTransferObject.TransferRole.Name})
}
)
});
I'm building a system for producing surveys and handling the responses, I have a viewmodel SurveyTakeViewModel
namespace Survey.ViewModel
{
public class SurveyTakeViewModel
{
public Models.Survey Survey { get; set; }
public bool TakenBefore { get; set; }
}
}
namespace Survey.Models
{
public class Survey
{
[Key]
public int SurveyId { get; set; }
public string Name { get; set; }
public bool Active { get; set; }
public string MessageStart { get; set; }
public string MessageEnd { get; set; }
public virtual ICollection<Question> Question { get; set; }
}
}
namespace Survey.Models
{
public class Question
{
public virtual int SurveyId { get; set; }
[Key]
public int QuestionId { get; set; }
public int DisplayOrder { get; set; }
public string QuestionText { get; set; }
public string QuestionNote { get; set; }
public int QuestionTypeId { get; set; }
public virtual QuestionType QuestionType { get; set; }
public virtual ICollection<QuestionAnswerOption> QuestionAnswerOption{ get; set; }
public virtual ICollection<QuestionAnswer> QuestionAnswer { get; set; }
}
}
namespace Survey.Models
{
public class QuestionAnswer
{
public virtual int QuestionId { get; set; }
[Key]
public int QuestionAnswerId { get; set; }
public int SurveyId { get; set; }
public string UserId { get; set; }
public string QuestionActualResponse { get; set; }
}
}
It all works fine for the questionnaire, however when a user revisits a questionnaire they have previously answered I want to populate the QuestionAnswer part of the viewmodel with answers only by a specific userid, at the moment I get everyanswer. I have tried loads of different Linq queries, initially my ICollections<> were List<>, which I am told could cause all records to be returned.
at the moment I am using
Survey = db.Survey.FirstOrDefault(s => s.SurveyId == 1)
which returns all QuestionAnswer
I have tried things like
Survey = db.Survey.FirstOrDefault(a => a.Question
.Any(b => b.QuestionAnswer
.Any(c => c.UserId == userId)));
but it still returns all QuestionAnswer for every userId
It seems like you know which survey you want, but when you access the various properties, they are populating with extra information... you'd like fewer grandchild records.
I don't know enough LinqToEntities to limit the loading in the way that is needed (LinqToSql would use DataLoadOptions.LoadsWith and DataLoadOptions.AssociateWith). Instead, I offer this manual shaping of the data - after loading. Perhaps it will help an expert understand the question and then they can express it in a LinqToEntities query.
int surveyId = GetSurveyId();
int userId = GetUserId();
Survey mySurvey = db.Survey.FirstOrDefault(s => s.SurveyId == surveyId);
ILookup<int, QuestionAnswer> qaLookup = db.QuestionAnswer
.Where(qa => qa.SurveyId == surveyId && qa.UserId == userId)
.ToLookup(qa => qa.QuestionId);
foreach(Question q in mySurvey.Questions)
{
//limit q.QuestionAnswer to only this user's answers.
q.QuestionAnswer = qaLookup[q.QuestionId].ToList();
}