EF7 nested Include not showing in Razor .net - c#

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

Related

Query through multiple navigation properties

I am getting confused with my LINQ query and wondering if there is a way I can achieve the following:
A user has a list of liked Categories:
public class ApplicationUser : IdentityUser
{
[PersonalData]
public string Name { get; set; }
[PersonalData]
[DataType(DataType.Date)]
public DateTime DOB { get; set; }
[PersonalData]
public PersonGender Gender { get; set; }
[PersonalData]
[ForeignKey("Suburb")]
public int SuburbId { get; set; }
//Information about user preferences
public ICollection<UserCategory> LikedCategories { get; set; }
public virtual Suburb Suburb { get; set; }
}
Where UserCategory is defined as follows:
public class UserCategory
{
[Key]
public int Id { get; set; }
public ApplicationUser applicationUser { get; set; }
public Category Category { get; set; }
[ForeignKey("ApplicationUser")]
public string applicationUserId { get; set; }
[ForeignKey("Category")]
public int CategoryId { get; set; }
}
And Category is:
public class Category
{
[Key]
public int ID { get; set; }
public string Name { get; set; }
public int CategoryFollowers { get; set; }
public ICollection<EventCategory> EventCategories { get; set; }
public ICollection<UserCategory> UserCategories { get; set; }
}
}
Finally, in my Events class I have the following:
public class Event
{
[Key]
public int ID { get; set; }
[Required]
public string Name { get; set; }
[Display(Name = "Is Featured?")]
public bool isFeatured { get; set; }
public byte[] EventImage1 { get; set; }
public byte[] EventImage2 { get; set; }
public virtual ICollection<EventCategory> EventCategories { get; set; }
public virtual ICollection<UserEvent> UserEvents { get; set; }
My view requires an object of type Event, so I am trying to return a list of Events where the EventCategory is contained in the UserCategory table. In other words I just want to show events that the user has liked the category for.
MORE CLARIFICATION
I am able to filter my events by category from a different function that takes in a hardcoded category Id from the view and this works fine:
//GET:// Browse event by category
public async Task<IActionResult> BrowseByCategory(int? id, int? alt_id)
{
if (id == null)
{
return NotFound();
}
var eventsContext = _context.Events
.Include(m => m.Venue)
.ThenInclude(mf => mf.Suburb)
.ThenInclude(mc => mc.Constituency)
.ThenInclude(md => md.City)
.Where(e => e.EventCategories.Any(c => c.Category.ID == id || c.Category.ID == alt_id))
.Take(15)
.OrderByDescending(o => o.StartDate);
return View("Browse",await eventsContext.ToListAsync());
}
I would like to do the exact same as above, but rather than pass in the hardcoded ID queries from the form, I want the query to check the categoryIDs that are saved in the UserCategory table. There is no set number of how many UserCategory items there are.

Automapper many to many mapping confusion

I have many to many relationship tables such as "User & Notification & UserNotification" and their entities, view models also.
There is only a difference between ViewModel and Entity classes. HasRead property is inside NotificationViewModel. How Can I map this entities to view models? I could not achieve this for HasRead property.
What I did so far is,
Mapping Configuration:
CreateMap<Notification, NotificationViewModel>();
CreateMap<User, UserViewModel>().ForMember(dest => dest.Notifications, map => map.MapFrom(src => src.UserNotification.Select(x => x.Notification)));
Notification class:
public class Notification : IEntityBase
{
public Notification()
{
this.UserNotification = new HashSet<UserNotification>();
}
public int Id { get; set; }
public string Header { get; set; }
public string Content { get; set; }
public System.DateTime CreateTime { get; set; }
public bool Status { get; set; }
public virtual ICollection<UserNotification> UserNotification { get; set; }
}
User Class
public class User : IEntityBase
{
public User()
{
this.UserNotification = new HashSet<UserNotification>();
}
public int Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public string Email { get; set; }
public string PhoneNumber { get; set; }
public bool Status { get; set; }
public virtual ICollection<UserNotification> UserNotification { get; set; }
}
UserNotification class:
public class UserNotification : IEntityBase
{
public int Id { get; set; }
public int UserId { get; set; }
public int NotificationId { get; set; }
public bool HasRead { get; set; }
public virtual Notification Notification { get; set; }
public virtual User User { get; set; }
}
UserViewModel class
public class UserViewModel : IValidatableObject
{
public int Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public string Email { get; set; }
public string PhoneNumber { get; set; }
public bool Status { get; set; }
public IList<NotificationViewModel> Notifications { get; set; }
}
NotificationViewModel class
public class NotificationViewModel
{
public int Id { get; set; }
public string Header { get; set; }
public string Content { get; set; }
public System.DateTime CreateTime { get; set; }
public bool Status { get; set; }
public bool HasRead { get; set; } // this is the difference
}
In order to fix up the HasRead, maybe you can utilize the AfterMap(Action<TSource, TDestination> afterFunction) function. It's not as elegant as the rest of automapper, but it might work.
CreateMap<User, UserViewModel>()
.ForMember(dest => dest.Notifications, map => map.MapFrom(src => src.UserNotification.Select(x => x.Notification)))
.AfterMap((src, dest) =>
{
foreach (var notificationVM in dest.Notifications)
{
notificationVM.HasRead = src.UserNotification.Where(x => x.NotificationId == notificationVM.Id).Select(x => x.HasRead).FirstOrDefault();
}
});

add ICollection User in the same entity User

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

EntityFramework code first keys

I have following tables: User, UserGroups, and UserInGroups. You can see them on picture below. When i call User i want to be able to get Groups that user is in (UserInGroups). I am reading materials about EntityFramework but i am unable to make connections in code to to that, what am i missing? Do i need to connect them onModelCreating?
Currently i am not getting any data from UserInGroup or UserGroups.
DB looks like this
My classes look like this:
public class User : BaseEntity
{
public int RoleId { get; set; }
public Role Role { get; set; }
public UserGroup UserGroup { get; set; }
public UserInGroup UserInGroup { get; set; }
}
public class UserGroup : BaseEntity
{
public UserGroup()
{
Users = new List<User>();
UserInGroups = new List<UserInGroup>();
}
[StringLength(255)]
public string Name { get; set; }
public string KeyName { get; set; }
public List<User> Users { get; set; }
public List<UserInGroup> UserInGroups { get; set; }
}
public class UserInGroup : BaseEntity
{
public UserInGroup()
{
Users = new List<User>();
UserGroups = new List<UserGroup>();
}
public int UserId { get; set; }
public User User { get; set; }
public int UserGroupId { get; set; }
public UserGroup UserGroup { get; set; }
public List<User> Users { get; set; }
public List<UserGroup> UserGroups { get; set; }
}
DbContext:
public DbSet<GlobalSettings> GlobalSettings { get; set; }
public DbSet<Role> Roles { get; set; }
public DbSet<User> Users { get; set; }
public DbSet<UserGroup> UserGroups { get; set; }
public DbSet<UserInGroup> UsersInGroups { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<GlobalSettings>().Property(x => x.Key).HasColumnAnnotation("Index", new IndexAnnotation(new[] { new IndexAttribute("Index_VariablenName") { IsClustered = false, IsUnique = true } }));
}
public abstract partial class BaseEntity
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
}
public class User : BaseEntity
{
public string Username { get; set; }
public string Title { get; set; }
public FirstName { get; set; }
public string LasName { get; set; }
public Genders Gender { get; set; }
public string Email { get; set; }
public int RoleId { get; set; }
public string TechUser { get; set; }
public DateTime TechChangeDate { get; set; }
public int TechVersion { get; set; }
public bool IsDeleted { get; set; }
public virtual Role Role { get; set; }
public virtual ICollection<UserInGroup> UserInGroups { get; set; }
}
public class UserInGroup : BaseEntity
{
public int UserId { get; set; }
public int UserGroupId { get; set; }
public string TechUser { get; set; }
public DateTime TechChangeDate { get; set; }
public int TechVersion { get; set; }
public bool IsDeleted { get; set; }
public virtual User User { get; set; }
public virtual UserGroup UserGroup { get; set; }
}
public class UserGroup : BaseEntity
{
public string Name { get; set; }
public string KeyName { get; set; }
public string TechUser { get; set; }
public DateTime TechChangeDate { get; set; }
public int TechVersion { get; set; }
public bool IsDeleted { get; set; }
}
public class Role : BaseEntity
{
public string Name { get; set; }
}
public enum Genders
{
Male = 1,
Female = 2
}
You can use two methods to fill navigation properties. First is lazy-loading and second is explicit specifying of required properties.
For lazy-loading you should declare your navigation properties as virtual:
public class User : BaseEntity
{
public int RoleId { get; set; }
public virtual Role Role { get; set; }
public virtual UserGroup UserGroup { get; set; }
public virtual UserInGroup UserInGroup { get; set; }
}
public class UserGroup : BaseEntity
{
public UserGroup()
{
Users = new HashSet<User>(); // HashSet is more effective than List
UserInGroups = new HashSet<UserInGroup>();
}
[StringLength(255)]
public string Name { get; set; }
public string KeyName { get; set; }
public virtual ICollection<User> Users { get; set; } // ICollection is less restrective
public virtual ICollection<UserInGroup> UserInGroups { get; set; }
}
Now, you can load f.e. single user:
var justUser = dbContext.Users.Single(u => u.Id == 100);
When you need its properties they will by transparently loaded:
foreach (var userInGroup in user.UsersInGroups) // here is second loading
{
. . .
}
The second way is the calling of the Include method to explicit specifying required properties:
var userWithGroups = dbContext.Users
.Include(u => u.UserInGroups) // include single navigation property
.Include(ugs => ugs.Groups.Select(ug => ug.Group)) // include collection navigation property
.Single(u => u.Id == 100); // get the user with specified id and filled specified properties

Entity Framework Union and Except Query On Different Types

i want to User entity with Action's of Site Role's but point is ExtraAction entity,Action data will be filter by ExtraAction entity,
in ExtraAction entity:
if Type property == 1 this to be UNION to Action entity
if Type property == 0 this to be EXCEPT to Action entity
public class User
{
public int Id { get; set; }
public string Email { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public ICollection<SiteRole> SiteRoles { get; set; }
public ICollection<ExtraAction> ExtraActions { get; set; }
}
public class SiteRole
{
public int Id { get; set; }
public string Description { get; set; }
public virtual ICollection<Action> Actions { get; set; }
public virtual ICollection<User> User { get; set; }
}
public class ExtraAction
{
public int Id { get; set; }
public int UserId { get; set; }
public int ActionId { get; set; }
public byte Type { get; set; }
public virtual Action Action { get; set; }
public virtual User User { get; set; }
}
public class Action
{
public int Id { get; set; }
public string Name { get; set; }
public string ActionName { get; set; }
public string ControllerName { get; set; }
public ICollection<SiteRole> SiteRoles { get; set; }
public virtual ICollection<ExtraAction> ExtraActions { get; set; }
}
finally my solution is below
var list = dbContext.Actions.Where(u =>
u.Roles.SelectMany(r => r.User).Any(su => su.Id == Id)).Select(row => new { Action = row }).
Union(dbContext.ExtraActions.Where(suea => suea.Type == 1 && suea.UserId == Id).Select(row => new { Action = row.Action })).
Except(dbContext.ExtraActions.Where(suea => suea.Type == 0 && suea.UserId == Id).Select(row => new { Action = row.Action })).ToList();

Categories