Issue with Include in Entity framework - entity-framework-core

I have Employee, Address and Organization classes. below are the details
public partial class Employees
{
public Employees()
{
AddressDetails = new HashSet<AddressDetails>();
OrganizationDetails = new HashSet<OrganizationDetails>();
}
public string Id { get; set; }
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
public ICollection<AddressDetails> AddressDetails { get; set; }
public ICollection<OrganizationDetails> OrganizationDetails { get; set; }
}
public partial class AddressDetails
{
public int Id { get; set; }
public string Address { get; set; }
public string Type { get; set; }
public string EmployeeId { get; set; }
public Employees Employee { get; set; }
}
public partial class OrganizationDetails
{
public int Id { get; set; }
public string Name { get; set; }
public string Location { get; set; }
public string EmployeeId { get; set; }
public Employees Employee { get; set; }
}
I have used fully defined relationship, you can see Employee has collections of both AddressDetails and OrganizationDetails as navigation properties. And each of them have EmployeeId and Employee in the same way.
My problem is, when i try to fetch Employee details using Include(), both AddressDetails and OrganizationDetails are loaded that's fine, but when i checked both the collections each entity has again loaded Employee information and so on.
for example: if i check AddressDetails collection which is loaded, Address object has information of Employee and again that Employee has collections of both AddressDetails and OrganizationDetails.
Please help me how can i avoid this. I don't want to remove Employee object property from AddressDetails and OrganizationDetails. is there anyway to make it work with Include().
here is the query i'm using to load these navigation properties.
List employees = _context.Employees.Include(emp => emp.AddressDetails).Include(emp => emp.OrganizationDetails).ToList();

Related

LINQ query throw exception on FirstOrDefault method

I'm using EF core, and I have a many-to-many relationship between two entity
IotaProject <--> User
Here's entities & dto related to the question
public class IotaProject
{
[Key]
public int Id { get; set; }
[Required]
public string ProjectName { get; set; }
[Required]
public DateTime Create { get; set; }
public ICollection<ProjectOwnerJoint> Owners { get; set; } = new List<ProjectOwnerJoint>();
}
public class ProjectOwnerJoint
{
public int IotaProjectId { get; set; }
public IotaProject IotaProject { get; set; }
public int UserId { get; set; }
public User User { get; set; }
}
public class User
{
[Key]
public int Id { get; set; }
[Required]
public string FullName { get; set; }
[Required]
public string ShortName { get; set; }
[Required]
public string Email { get; set; }
public ICollection<ProjectOwnerJoint> OwnedProjects { get; set; } = new List<ProjectOwnerJoint>();
}
public class ApplicationDbContext : DbContext
{
public DbSet<IotaProject> IotaProjects { get; set; }
public DbSet<User> Users { get; set; }
public DbSet<ProjectOwnerJoint> ProjectOwnerJoint { get; set; }
}
public class IotaProjectDisplayDto
{
public int Id { get; set; }
public string ProjectName { get; set; }
public DateTime Create { get; set; }
public UserMinDto Owner { get; set; }
public int Count { get; set; }
public IEnumerable<UserMinDto> Reviewers { get; set; }
}
public class UserMinDto
{
public int Id { get; set; }
public string FullName { get; set; }
public string ShortName { get; set; }
}
Following LINQ is the problem, the LINQ purpose is to convert IotaProject to IotaProjectDisplayDto, and key part is that Owners property of IotaProject is ICollection and Owner property in IotaProjectDisplayDto is just one single element UserMinDto, so I only need to get the first element of IotaProject's Owners and that's FirstOrDefault() comes.
IEnumerable<IotaProjectDisplayDto> results = _db.IotaProjects.Select(x => new IotaProjectDisplayDto
{
Id = x.Id,
ProjectName = x.ProjectName,
Create = x.Create,
Owner = x.Owners.Select(y => y.User).Select(z => new UserMinDto { Id = z.Id, FullName = z.FullName, ShortName = z.ShortName }).FirstOrDefault()
});
return results;
it throws run-time exception
Expression of type 'System.Collections.Generic.List`1[ToolHub.Shared.iota.UserMinDto]' cannot be used for parameter
of type 'System.Linq.IQueryable`1[ToolHub.Shared.iota.UserMinDto]'
of method 'ToolHub.Shared.iota.UserMinDto FirstOrDefault[UserMinDto](System.Linq.IQueryable`1[ToolHub.Shared.iota.UserMinDto])' (Parameter 'arg0')
I'm guessing it's probably related to deferred execution, but after read some posts, I still can't resolve it.
Any tips would be appreciated.
Right now, the only way I can get this work is I change type of Owner property in IotaProjectDisplayDto into IEnumrable, which will no longer need FirstOrDefault() to immediate execution. And later on, I manually get the first element in the client to display.
This issue happened in Microsoft.EntityFrameworkCore.SqlServer 3.0.0-preview7.19362.6
I end up downgrade to EF core stable 2.2.6 as Ivan suggested in comment, and everything works fine.

EF Core code first Inheritance of separate table

Say I have a table Company defined in following entity:
public class Company
{
public Guid CompanyId { get; set; }
[Required]
public string Name { get; set; }
[MaxLength(50)]
public string Uid { get; set; }
...
}
And I need another table CompanyHistory what will have all fields of Company extended with CompanyHistoryId, EffectiveDate, DEffectiveDate.
I have tried it like this:
public class CompanyHistory : Company
{
public Guid CompanyHistoryId { get; set; }
public virtual Company { get; set; }
}
But instead of 2 tables migration makes one and combines all the columns.
How can I get same result without writing all the column again as is done here:
public class CompanyHistory
{
public Guid CompanyHistoryId { get; set; }
public Guid CompanyId { get; set; }
public virtual Company Company { get; set; }
[Required]
public string Name { get; set; }
[MaxLength(50)]
public string Uid { get; set; }
...
}

Entity Framework relationship between another fields than id

I have two class (tables)
Person { id(primary key) , code, name, address, ...}
Order {id(primary key) , order_number, customer, create_date, description, ...}
I want to create relationship between Person.code and Order.customer (one two many).
How can I create that relationship in EF code first.
OK it has an easy solution
[Table("Person")]
public partial class Person
{
public long ID { get; set; }
[Key()]
[StringLength(10)]
public string code { get; set; }
[StringLength(100)]
public string name { get; set; }
[Column(TypeName = "text")]
public string address{ get; set; }
public ICollection<Order> Orders { get; set; }
}
and for order
[Table("Order")]
public partial class Order
{
public long ID { get; set; }
public int order_number { get; set; }
[StringLength(10)]
public string customer { get; set; }
[Column(TypeName = "text")]
public string description { get; set; }
//...
[ForeignKey("customer")]
public Library Person { get; set; }
}
I will create a new question about specification various composite keys per each navigation property.

Mapping Many to many in Entity framework

simply I ask this How to Map , How to ProductCustomer in the sample ??
public class ProductCustomer
{
public virtual Product Product { get; set; }
public virtual Customer Customer { get; set; }
}
and about Product and Customer :
public class Customer
{
public int Id { get; set; }
public string CustomerName { get; set; }
}
public class Product
{
public int Id { get; set; }
public string ProductName { get; set; }
public decimal Amount { get; set; }
}
thanks!
You don't need to create the ProductCustomer object.
In EF, you create your Customer and Product, and then you create collections to each. This will automatically create the proper link tables.
public class Customer
{
public int Id { get; set; }
public string CustomerName { get; set; }
public virtual List<Product> Products {get;set;}
}
public class Product
{
public int Id { get; set; }
public string ProductName { get; set; }
public decimal Amount { get; set; }
public virtual List<Customer> Customers {get;set;}
}
This is only the case, however, if your link table has no payload (has no additional data). If it does, then you will need to create the link table as an entity similar to what you originally did, but you add 1:many links in your product and customer classes to the link entity. You then have to modify your queries to query through the link table.

Entity framework foreign key

I have 2 classes
public class Person
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
}
and
public class PersonWebsite
{
public int Id { get; set; }
public string Website { get; set; }
public int PersonId{ get; set; }
}
I've seen stuff like this being done before
public class Person
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public ICollection<PersonWebsite> PersonWebsites{ get; set; }
}
How could I go about implementing the code that when a Person is initialized, the PersonWebsites List will automatically be initialised and get all the PersonWebsite objects that have the same PersonId as the class that calls it.
Lazy Loading:
You can make PersonWebsites property virtual:
public virtual ICollection<PersonWebsite> PersonWebsites{ get; set; }
Entity framework will load it from the database as soon as it's required.
Also this method requires you to have lazy loading enabled which is by default:
DbContext.ContextOptions.LazyLoadingEnabled = true;
Eager Loading:
You can use include to force entity framework to load PersonWebsites on the first query:
DbSet.Include(p => p.PersonWebsites);
You may also want to change your PersonWebsite class like this, in order to navigate to the person from the PersonWebsite object (using Lazy-Loading):
public class PersonWebsite
{
public int Id { get; set; }
public string Website { get; set; }
[ForeignKey("Person")]
public int PersonId{ get; set; }
public virtual Person Person {get;set;}
}