Entity Framework - Store parent reference on child relationship (one -> many) - c#

I have a setup like this:
[Table("tablename...")]
public class Branch
{
public Branch()
{
Users = new List<User>();
}
[Key]
public int Id { get; set; }
public string Name { get; set; }
public List<User> Users { get; set; }
}
[Table("tablename...")]
public class User
{
[Key]
public int Id {get; set; }
public string Username { get; set; }
public string Password { get; set; }
[ForeignKey("ParentBranch")]
public int? ParentBranchId { get; set; } // Is this possible?
public Branch ParentBranch { get; set; } // ???
}
Is it possible for the User to know what parent branch it belongs to? The code above does not populate the ParentBranch.
Entity Framework version 5.0
.NET 4.0
c#

Try making navigation properties virtual,
[Table("tablename...")]
public class Branch
{
public Branch()
{
Users = new List<User>();
}
[Key]
public int Id { get; set; }
public string Name { get; set; }
public virtual List<User> Users { get; set; }
}
[Table("tablename...")]
public class User
{
[Key]
public int Id {get; set; }
public string Username { get; set; }
public string Password { get; set; }
[ForeignKey("ParentBranch")]
public int? ParentBranchId { get; set; } // Is this possible?
public virtual Branch ParentBranch { get; set; } // ???
}

Related

FK collection returned by EF is null, but validation for duplicates, confirms thats, the collection has content

I'm trying to get a data of all the user drone's, but ef core returning empty collection.
public class User : AuditableEntity
{
public Guid Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public string UserData { get; set; }
public bool IsActive { get; set; }
public bool IsAdmin { get; set; }
public DateTime LastActivity { get; set; }
public IEnumerable<Drone> Drones { get; set; }
}
public class Drone : AuditableEntity
{
public Guid Id { get; set; }
public string CustomName { get; set; }
public string Model { get; set; }
public int Serial { get; set; }
public bool IsActive { get; set; }
public string DroneData { get; set; }
public virtual User User { get; set; }
}
and user.Drones always has null value and i can't get and show the data, but this line of validation code
if (user.Drones.Contains(drone))
return ("This drone has been alredy registred", false);
shows me that, the user.Drones contains values.
Why i should do, to get user.Drones collection?
Ef uses a lazy loading, to load child instances you have to use Include
var user = context.Users.Where (i=> i.Id==userId)
.Include(i=>i.Drones)
.FirstOrDefault();
//or if you want just drones
var userDrones=context.Users.Where (i=> i.Id==userId)
.Select(i=>i.Drones)
.FirstOrDefault();
but it is better to add a foreign key UserId to Drone explicitly
public class Drone : AuditableEntity
{
public Guid Id { get; set; }
.....
public Guid UserId { get; set; }
public virtual User User { get; set; }
}
and use this query
var userDrones=context.Drones.Where (i=> i.UserId==userId).ToList();

How to create a relation between existing entities in EF core?

I have a case scenario with two tables References and Products alreading containing many entries which can be dynamically related on demand.
public class Reference
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid ReferenceId { get; set; }
public string Name { get; set; }
public string Status { get; set; }
public virtual ICollection<Product> ManyProducts { get; set; }
public Reference() {}
}
public class Product
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid ProductId { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
[ForeignKey("Reference")]
public Guid ReferenceId { get; set; }
public virtual Reference OneReference { get; set; }
public Product() {}
}
When a user ask to link a reference to a product I simply do :
product.ReferenceId = reference.ReferenceId ;
await context.SaveChangesAsync() ;
The entry in Products table is updated correctly, but when I try to access a reference's related data, it does not retrieve any ?? After eager loading :
var reference = await context.References
.Include(r => r.ManyProducts)
.SingleAsync(r => r.ReferenceId == referenceId) ;
or explicit loading :
var reference = await context.References.FindAsync(referenceId) ;
await context.Entry(reference).Collection(s => s.ManyProducts).LoadAsync() ;
reference.ManyProducts is empty. So I have to do something like this :
var reference = await context.References.FindAsync(referenceId) ;
var products = await context.Products.Where(l => l.ReferenceId == referenceId).ToListAsync() ;
result.ManyProducts = products ;
which works fine, but I would like to understand why ?
I´m using DataAnnotation
Sample
public class spread
{
[Key]
public int spreadid { get; set; }
[Required]
public DateTime insertdate { get; set; }
[Required]
public int exchangeid { get; set; }
[ForeignKey("exchangeid"), Display(Name = "Exchange origem")]
public virtual exchange exchange { get; set; } // One to one
[ForeignKey("spreadid")]
public virtual ICollection<spreadhelper> spreadhelper { get; set; } // One to many
}
public class spreadhelper
{
[Key]
public int spreadhelperid { get; set; }
[Required]
public int spreadid { get; set; }
[Required]
public int exchangeid { get; set; }
[ForeignKey("exchangeid"), Display(Name = "Exchange")] // One to one
public virtual exchange exchange { get; set; }
[Required, Range(0, 200)]
public decimal spreadvalue { get; set; }
}
one to one - sample
public class exchange
{
[Key]
public int exchangeid { get; set; }
[Required]
public DateTime insertdate { get; set; }
[Required, MaxLength(50)]
public string name { get; set; }
[MaxLength(128)]
public string token { get; set; }
}
One to many sample

EF core 2.0, OwnsOne in TPH model classes

I have problem when I try to migrate my model in EF Core 2.0.
public class Profile
{
[Key]
public Guid Id { get; set; }
public Guid UserId { get; set; }
public ExternalUser User { get; set; }
}
public class OrganizationCustomerProfile : Profile
{
public string CompanyName { get; set; }
public Address LegalAddress { get; set; }
public Address ActualAddress { get; set; }
public BusinessRequisites Requisites { get; set; }
public string President { get; set; }
public IEnumerable<ContactPerson> ContactPerson { get; set; }
}
public class PersonCustomerProfile : Profile
{
public FullName Person { get; set; }
public Address Address { get; set; }
public string PhoneNumber { get; set; }
}
public class ContactPerson
{
[Key]
public Guid Id { get; set; }
public FullName Person { get; set; }
public string Rank { get; set; }
public string Email { get; set; }
public string PhoneNumber { get; set; }
public Guid ProfileId { get; set; }
public Profile Profile { get; set; }
}
Here I want to add complex datatypes Address and BusinessRequisites, which are:
public class BusinessRequisites
{
public string OGRN { get; set; }
public string INN { get; set; }
public string KPPCode { get; set; }
public string SettlementAccount { get; set; }
public string RCBIC { get; set; }
public string CorrespondentAccount { get; set; }
public string BankName { get; set; }
}
public class Address
{
public string FullAddress { get; set; }
public float Latitude { get; set; }
public float Longtitude { get; set; }
}
Code which I use for TPH binding:
public DbSet<Profile> UserProfiles { get; set; }
public DbSet<ContactPerson> ContactPerson { get; set; }
public DbSet<OrganizationCustomerProfile> OrganizationCustomerProfile { get; set; }
...
modelBuilder.Entity<Profile>().HasKey(u => u.Id);
modelBuilder.Entity<OrganizationCustomerProfile>().OwnsOne(e => e.ActualAddress);
modelBuilder.Entity<OrganizationCustomerProfile>().OwnsOne(e => e.LegalAddress);
modelBuilder.Entity<OrganizationCustomerProfile>().OwnsOne(e => e.Requisites);
But when I try to make a migration, I get an error:
"Cannot use table 'UserProfiles' for entity type
'OrganizationCustomerProfile.ActualAddress#Address' since it has a
relationship to a derived entity type 'OrganizationCustomerProfile'.
Either point the relationship to the base type 'Profile' or map
'OrganizationCustomerProfile.ActualAddress#Address' to a different
table."
So, what the reason of this error? Is it not possible to create hierarchy inheritance in EF Core 2.0?
Thank you!
It seems like this isn't supported at the moment:
https://github.com/aspnet/EntityFrameworkCore/issues/9888

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

Join 3 tables using Entity Framework

How can I build a model using Entity Framework to join 3 tables?
At the moment I have:
public class KeywordAdCategory
{
[Key]
[Column("Keyword_Id", Order = 0)]
public int Keyword_Id { get; set; }
[Key]
[Column("Ad_Id", Order = 1)]
public int Ad_Id { get; set; }
[Key]
[Column("Category_Id", Order = 2)]
public int Category_Id { get; set; }
}
But I don't have any navigation properties.
Is there a better way to build a relashionship between 3 tables using Entity Framework?
Also the Keyword, Ad and Category models:
public class Keyword
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Address> Addresses { get; set; }
}
public class Ad
{
// Primary properties
[Key]
public int Id { get; set; }
// Navigation properties
public AdOperation AdOperation { get; set; }
public Member Member { get; set; }
public Address Address { get; set; }
public Category Category { get; set; }
public virtual ICollection<Picture> Pictures { get; set; }
private ICollection<Feature> _features;
public virtual ICollection<Feature> Features
{
get { return _features ?? (_features = new HashSet<Feature>()); }
set { _features = value; }
}
}
public class Category
{
// Primary properties
public int Id { get; set; }
public int? CategoryParent_Id { get; set; }
public int? CategoryGroup_Id { get; set; }
public bool IsActive { get; set; }
// Navigation properties
public Keyword Keyword { get; set; }
}
Thanks.
I'm assuming that you're using Code-First Entity Framework here, and that you have your KeywordAdCategory object in your database as well. In which case, just simply do the following in your KeywordAdCategory class to do the proper mapping:
[Key, ForeignKey("Keyword")]
[Column("Keyword_Id", Order = 0)]
public int Keyword_Id { get; set; }
[Key, ForeignKey("Ad")]
[Column("Ad_Id", Order = 1)]
public int Ad_Id { get; set; }
[Key, ForeignKey("Category")]
[Column("Category_Id", Order = 2)]
public int Category_Id { get; set; }
public virtual Keyword Keyword { get; set; }
public virtual Ad Ad { get; set; }
public virtual Category Category { get; set; }
Doing this should do the proper mappings, put FKs on your KeywordAdCategory table, and thus give you the ability to have good navigation properties to the other objects.

Categories