Entity Framework 7 Accessing Entity Objects inside ICollection - c#

I have two entities Users and Addresses. Addresses is an ICollection property on the Users entities. However i am not able to access the individual addresses inside the ICollection. Is that possible and i just have the wrong syntax or does entity framework not allow that.
The Code:
Users:
public partial class User
{
public User()
{
Addresses = new HashSet<Address>();
Comments = new HashSet<Comment>();
Orders = new HashSet<Order>();
Posts = new HashSet<Post>();
}
public long Id { get; set; }
public bool Active { get; set; }
public DateTime? BirthDate { get; set; }
public DateTime Created { get; set; }
public string Email { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Password { get; set; }
public DateTime? Updated { get; set; }
public string Username { get; set; }
public long? UserTypeId { get; set; }
public virtual ICollection<Address> Addresses { get; set; }
public virtual ICollection<Comment> Comments { get; set; }
public virtual ICollection<Order> Orders { get; set; }
public virtual ICollection<Post> Posts { get; set; }
public virtual UserType UserType { get; set; }
}
Addresses:
public partial class Address
{
public long Id { get; set; }
public bool Active { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public string City { get; set; }
public DateTime Created { get; set; }
public string State { get; set; }
public DateTime? Updated { get; set; }
public long? UserId { get; set; }
public string Zip { get; set; }
public virtual User User { get; set; }
}
Repository (Parent):
public class Repository<T> where T : class
{
private DemoContext _context;
protected DbSet<T> DbSet;
public Repository(DemoContext context)
{
_context =context;
DbSet = _context.Set<T>();
}
public virtual T Get(int Id)
{
// TODO: Implement with return of DbSet.Find();
// DbSet.Find(Id);
return null;
}
public virtual List<T> GetAll()
{
return DbSet.ToList();
}
public virtual void Add(T entity)
{
DbSet.Add(entity);
}
public virtual void Update(T user)
{
_context.Entry<T>(user)
.State = EntityState.Modified;
}
public virtual void SaveChanges()
{
_context.SaveChanges();
}
public virtual void Delete(int Id)
{
// TODO: Implement with return of DbSet.Find();
// DbSet.Remove(Dbset.Find(Id));
}
UserRepository:
public class UserRepository : Repository<User>
{
public UserRepository(DemoContext context)
: base(context)
{
}
public override User Get(int Id)
{
return DbSet
.Where(o => o.Id == Id)
.Include(o => o.Orders)
.Include(o => o.Addresses)
.ToList()
.Single();
}
public override List<User> GetAll()
{
return DbSet
.Include(o => o.Orders)
.Include(o => o.Addresses)
.ToList();
}
public override void Delete(int Id)
{
DbSet.Remove(Get(Id));
}
}
The following code will not give me access to the first address in the ICollection property on the user entity
[HttpGet("[action]")]
public IActionResult Create()
{
var user = userRepository.Get(1);
var order = new Order
{
Address = user.Addresses[0].Address,
City = user.Addresses[0].City,
State = user.Addresses[0].State,
Zip = user.Addresses[0].Zip,
User = user,
SubTotal = 100,
Tax = 25,
Total = 125
};
orderRepository.Add(order);
orderRepository.SaveChanges();
return RedirectToAction("Index");
}
Please advise on how to correct my code so i have access to the entities in the collection.

Just in case anyone was wondering how i solved this here is the answer:
As mentiond by COLD TOLD ICollection does not support array like indexing. However Lists do so change it to a List instead of an ICollection and deleted the hashset that was created in the custructor
public partial class User
{
public User()
{
}
public long Id { get; set; }
public bool Active { get; set; }
public DateTime? BirthDate { get; set; }
public DateTime Created { get; set; }
public string Email { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Password { get; set; }
public DateTime? Updated { get; set; }
public string Username { get; set; }
public long? UserTypeId { get; set; }
public virtual List<Address> Addresses { get; set; }
public virtual List<Comment> Comments { get; set; }
public virtual List<Order> Orders { get; set; }
public virtual List<Post> Posts { get; set; }
public virtual UserType UserType { get; set; }
}
This allows me to access those nested entities as like this:
[HttpGet("/")]
public IActionResult Create()
{
var user = userRepository.Get(1);
var order = new Order
{
Address = user.Addresses[0].Address1,
City = user.Addresses[0].City,
State = user.Addresses[0].State,
Zip = user.Addresses[0].Zip,
User = user,
SubTotal = 100,
Tax = 25,
Total = 125
};
orderRepository.Add(order);
orderRepository.SaveChanges();
return RedirectToAction("Index");
}

I do not think it a good idea to have that many includes in your select it might cause problems in your performance , you might try to change the order of the query
return DbSet.Include(o => o.Orders)
.Include(o => o.Addresses)
.Where(o => o.Id == Id)
.FirstOrDefault();

Related

MVC5 How to connect object with current user?

I am making Library using MVC and I nearly finished it all. Last thing i want to do is to let customers borrow books. In order to do that I want to find out how can I connect current logged user with certain book. I want user to be able to click on Borrow Book and he will have book available for him. Can you please explain me how may I connect these two?
Home Controller:
public ActionResult Borrow(string CustomerId, string Id)
{
Customer customer = customerContext.Find(CustomerId);
Book book = context.Find(Id);
if (customer != null && book != null)
{
customer.borrowedBooks.Add(book);
context.Insert(book);
context.Commit();
return RedirectToAction("Index", "Manage");
}
else
{
return HttpNotFound();
}
}
Customer class:
public class Customer : BaseEntity
{
public string UserId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string Street { get; set; }
public string City { get; set; }
public string County { get; set; }
public string PostCode { get; set; }
public List<Book> borrowedBooks { get; set; }
public Customer() {
}
}
Book class:
public class Book : BaseEntity
{
[StringLength(30)]
[DisplayName("Book Title")]
public String Title { get; set; }
[DisplayName("Writer First Name")]
public String WriterFirstName { get; set; }
[DisplayName("Writer Last Name")]
public String WriterLastName { get; set; }
[DisplayName("Released")]
public DateTime ReleaseDate { get; set; }
[DisplayName("Publisher")]
public String Publisher { get; set; }
[DisplayName("Number of Books")]
public int NumberOfBooks { get; set; }
public String Genre { get; set; }
public String Format { get; set; }
public String Description { get; set; }
public String Image { get; set; }
public static int Count { get; set; }
public Book()
{
}
}
IRepository:
public interface IRepository<T> where T : BaseEntity
{
IQueryable<T> Collection();
void Commit();
void Delete(string Id);
T Find(string Id);
void Insert(T t);
void Update(T t);
}
Thanks
If you are using Entity Framework , we need to config many to many relationship.
https://www.entityframeworktutorial.net/code-first/configure-many-to-many-relationship-in-code-first.aspx
After that we can use : customer.borrowedBooks.Add(book) to add it to the many to many table.

C# ASP.NET Core AutoMapper: Child object is null

I have a many to many relationship between Users and Accounts.
When I do an HttpGet, I want to return AccountForDetailDto which includes an ICollection<UserForListDto> to list the users attached to that account.
I get the User IDs of the users attached to the account, but the User objects are null.
I assume that I need to do an Include somewhere, but I'm not sure where.I've tried .Include(account => account.UserAccounts.Select(userAccounts => userAccounts.User).
Or am I missing a map/property of a map.
Here's what I've got:
AccountsController.cs
[HttpGet("{id}", Name = "GetAccount")]
public async Task<IActionResult> GetAccount(int id)
{
var accountFromRepo = await _repo.GetAccount(id);
var accountToReturn = _mapper.Map<AccountForDetailedDto>(accountFromRepo);
return Ok(accountToReturn);
}
AccountRepository.cs
public async Task<Account> GetAccount(int id)
{
return await _context.Accounts
.Include(a => a.UserAccounts)
.FirstOrDefaultAsync(a => a.Id == id);
}
AutoMapperProfiles.cs
CreateMap<UserAccount, UserForListDto>();
CreateMap<Account, AccountForDetailedDto>()
.ForMember(dto => dto.Users, opt => opt.MapFrom(a => a.UserAccounts));
Models
public class Account
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<UserAccount> UserAccounts { get; set; }
}
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public ICollection<UserAccount> UserAccounts { get; set; }
}
public class UserAccount
{
public int? UserId { get; set; }
public User User { get; set; }
public int? AccountId { get; set; }
public Account Account { get; set; }
}
Dtos
public class AccountForDetailedDto
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<UserForListDto> Users { get; set; }
}
public class UserForListDto
{
public int Id { get; set; }
public string Username { get; set; }
}

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

EF7 nested Include not showing in Razor .net

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

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

Categories