I have a problem with a simple class. One property of my class is a reference to another class, but when I read, it's always null.
public class Product
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public String Description { get; set; }
public virtual Trademark Trademark { get; set; }
}
public class Trademark
{
public int TrademarkId { get; set; }
public String Description { get; set; }
}
Those are my classes, very simple.
Then when I get the first element:
Product p = context.Products.First();
And p contains the right product, but Trademark is null.
Even if I want to do a query using linq, like:
var prods = context.Products.Where(p => p.Trademark.TrademarkId == 1).ToList();
The database is generated Ok.
Using EF 4.3.1, with SqlServer compact edition 4.0
Thanks for any suggestions.
Add: This is my context class:
public class HPContext : DbContext
{
public HPContext()
: base()
{
this.Configuration.LazyLoadingEnabled = false; //Just for test
}
public DbSet<Product> Products { get; set; }
public DbSet<Trademark> Trademarks { get; set; }
}
Add: Database schema:
Table:
Products
Fields:
Id int primaryKey
Description nvarchar(4000)
Trademark_TrademarkId int
Table:
Trademarks
Fields:
TrademarkId int PrimaryKey
Description nvarchar(4000)
You shall use Include to include navigation properties if lazy loading is not enabled;
Product p = context.Products.Include("TradeMark").First();
Related
Consider the following scenario. I have 3 classes, representing a many-to-many (N-to-N) relationship between Student and Subject:
public class Student
{
public long Id { get; set; }
public string Name { get; set; }
public DateTime Birthday { get; set; }
public long RegistrationNumber { get; set; }
public virtual ICollection<Grade> Grades { get; set; }
}
public class Grade
{
public long Id { get; set; }
public int Value { get; set; }
public virtual Student Student { get; set; }
public virtual Subject Subject { get; set; }
}
public class Subject
{
public long Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Grade> Grades { get; set; }
}
I want to retrieve the list of all students, with their grades, for each subject. To do so, I use:
// context being DbContext
var res = context.Student.Include(s => s.Grades).ThenInclude(g => g.Subject);
As properties are lazy-loaded, I expected each subject to only contain their "Name" property. However, upon inspection, I found that the "Grades" list is also set, with a list of all the grades assigned to the subject. This, of course, causes an object cycle.
I want to avoid that circular referencing, i.e. obtain a list where each subject's only set property is "Name". How can I do it?
If you use asp.net core 3.0 MVC/Web API, just follow below steps to overcome circular reference using NewtonsoftJson.
1.Install Microsoft.AspNetCore.Mvc.NewtonsoftJson package(version depends on your project)
Install-Package Microsoft.AspNetCore.Mvc.NewtonsoftJson -Version 3.0.0
2.Add below code in startup
services.AddControllersWithViews().AddNewtonsoftJson(x =>
{
x.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
});
You can always manually select the name only, like
context.Student.Select(x => x.Name);
But this does not work with the Navigation properties and the automaticly generated Joins between the tables. There it's 'all or nothing'.
Or you have to do the join completly manually, without navigation properties.
But your structure isn't that complicated and not vulnerable to circularities.
Just start of with the Grade, with the anchor element in the middle.
context.Grade.Include(x => Subject).Include(x =>Student)
This is at least the easier way to load your entire structure and may be an approach for a starting point for manual joins.
Maybe you add a
.GroupBy(x => x.Student)
To get closer to your list of students.
You cannot skip the "loading" of the collection, cause it's the grades, that is loaded first. So first there are the elements of the collection, than there is the subject entity. It makes no sense not to put the data in the collection.
Following Jawad's advice, I ended up using LINQ Select statements.
First, I wrote some DTO's:
public class StudentDTO
{
public string Name { get; set; }
public DateTime Birthday { get; set; }
public long RegistrationNumber { get; set; }
public IEnumerable<GradeDTO> Grades { get; set; }
}
public class GradeDTO
{
public int Value { get; set; }
public virtual StudentDTO Student { get; set; }
public virtual SubjectDTO Subject { get; set; }
}
public class SubjectDTO
{
public string Name { get; set; }
public virtual IEnumerable<GradeDTO> Grades { get; set; }
}
And then:
var res = from student in context.Student
select new StudentDTO
{
Name = student.Name,
Birthday = student.Birthday,
RegistrationNumber = student.RegistrationNumber,
Grades = from grade in student.Grades
select new GradeDTO
{
Value = grade.Value,
Subject = new SubjectDTO
{
Name = grade.Subject.Name
}
}
};
I'm using Entity Framework 6.0 and MVC and I have one of the following model:
public class Person
{
[Key]
public int Id { get; set; }
public String Name { get; set; }
public String SocialNumber {get;set;}
}
public class Organization
{
[Key]
public Guid? OrgId { get; set; }
public List<Person> Members { get; set; }
}
I'm getting this data by doing something like
bool hideData = false;
var sResult = DBContext.Organization.FirstOrDefault(s => s.OrgId == inputID)
Is there an elegant way for me to set the SocialNumber = "######" when hideData is true and show the value when it is false?
Before you tell me that I can just loop through the object and set them explicitly, the reason I don't want to do this because I simpled down the above example, in reality, my class is buried deep in many many objects.
I am using Entity Framework Core 2.0.1 and I have the following models
public class Article
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public int Id { get; set; }
[Required]
public string Title { get; set; }
public string Slug { get; set; }
public int Approved { get; set; }
public DateTime ArticleDate { get; set; }
// ... some other fields
public virtual ICollection<ArticleCategoryRelation> ArticleCategoryRelations { get; set; }
}
public class ArticleCategory
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
//... soem other fields
[ForeignKey("ArticleCategoryParent")]
public int? ArticleCategoryParentID { get; set; }
public virtual ArticleCategory ArticleCategoryParent { get; set; }
public virtual ICollection<ArticleCategory> SubCategories { get; set; }
public virtual ICollection<ArticleCategoryRelation> ArticleCategoryRelations { get; set; }
}
public class ArticleCategoryRelation
{
[Column(Order = 0)]
public int ArticleId { get; set; }
public Article Article { get; set; }
[Column(Order = 1)]
public int ArticleCategoryId { get; set; }
public ArticleCategory ArticleCategory {get; set;}
}
Every article belongs to one or more categories. Categories might have parent category.
I want to get from database last two articles (where Approved = 1) with related category details, for each category that belongs to a parent category which id is given as input.
I have tried but with no success. I can't filter results of an .Include() entity. Is it possible... or I don't know how to do it?
All my data are accessed through entity framework with appContext (the context used to get entities from database). Can I achieve what I want through entity framework core (lambda expression is preferred over Linq if possible), or should I use ADO.NET library (which I know how to execute custom queries).
P.S. I want to get data only to show in the view... no edit is needed.
You don't actually need to include here at all, as far as I can tell. Whenever you use data from a nav property, EF will go get the data from that table, as best it can filter it.
var CategoriesUnderParent = AppContext.ArticleCategories
.Where(c => c.ArticleCategoryParent == {parent});
foreach(var category in CategoriesUnderParent)
{
var ArticlesAllowed = category.ArticleCategoryRelations
.Where(acr => acr.Article.Approved == 1).Select(a => a.Article);
var ArticlesPicked = ArticlesAllowed
.OrderByDescending(ar => ar.ArticleDate)
.Take(2);
// Do something with your data
}
I'm using EntityFramework for my Microsoft Sql Data Base.
First entity is Product:
public class Product
{
public Product()
{
ProductStories = new HashSet<ProductStory>();
}
public int ProductId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public bool Deleted { get; set; }
public HashSet<ProductStory> ProductStories { get; set; }
}
And another entity is ProductStory, which stores story about income or outcome of Products.
public class ProductStory
{
public int ProductStoryId { get; set; }
public virtual Product.Product Product { get; set; }
public int Count { get; set; }
public DateTime DateTime { get; set; }
}
So one Product could be in mane ProductStories, or in none.
I will not show all code(too big), so when I firstly create a single Product instance and save it in DB. Then I create a single ProductStory and reference to property Product to that instance of Product.
Then I save this ProductStory, there becomes 2 instances of ProductStory.
As I read, and I made this as virtual property:
public virtual Product.Product Product { get; set; }
How this problem could be solved?
I'm using EntityTypeConfiguration for tables configuration.
public class ProductMap : EntityTypeConfiguration<Product>
{
public ProductMap()
{
ToTable("Products").HasKey(x => x.ProductId);
Property(x => x.ProductId).IsRequired();
Property(x => x.Name).IsRequired().HasMaxLength(255).HasColumnName("Name");
//.HasColumnAnnotation("Index", new IndexAnnotation(new IndexAttribute("IX_Name") { IsUnique = true }));
Property(x => x.Description).IsOptional().HasColumnName("Description");
Property(x => x.Deleted).HasColumnName("Deleted");
}
}
And for ProductStory:
class ProductStoryMap: EntityTypeConfiguration<ProductStory>
{
public ProductStoryMap()
{
ToTable("ProductStories").HasKey(ps => ps.ProductStoryId);
Property(ps => ps.ProductStoryId).IsRequired();
//Property(ps => ps.ProductId).IsRequired().HasColumnName("ProductId");
Property(ps => ps.Count).HasColumnName("Count");
Property(ps => ps.DateTime).HasColumnName("DateTime");
}
}
You have some errors in your code:
//Change this:
public HashSet<ProductStory> ProductStories { get; set; }
//For this (virtual is needed here, also use ICollection rather than any specific implementation)
public virtual ICollection<ProductStory> ProductStories { get; set; }
//Change this:
public virtual Product.Product Product { get; set; }
//For this (virtual makes no sense here)
public Product.Product Product { get; set; }
And lastly, ProductStory needs a way to keep the reference to its parent Product. This is what creates the Foreign Key relationship in your database and allows Entity Framework to link the tables. So add this to ProductStory:
public int ProductId { get; set; }
If you are still getting a duplicated object (which may happen), ensure you are setting the ProductId to the ProductStory you are saving.
The solution was about Entity Framework "bug/feature".
As I add new ProductStory into DataBase, it attaches the whole graph(including all other entities references and recreates them).
So before commiting new ProductStory, I have to set to null all it's navigation properties to avoid recreating.
I've been building application using TPT (Table-Per-Type) in entity framework 6.
I have these classes
public class Person
{
public Guid Id { get; set; }
public string Name { get; set; }
}
public class Customer : Person
{
public string BillingAddress { get; set; }
}
public class Vendor : Person
{
public string Address { get; set; }
}
public class IndividualCustomer : Customer
{
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime Birthday { get; set; }
}
public class CorporateCustomer : Customer
{
public string ContactPerson { get; set; }
public string CorporateTaxNo { get; set; }
}
public class SalesInvoice
{
public Guid Id { get; set; }
public Guid CustomerId { get; set; }
public Customer Customer { get; set; }
}
The query is very ugly and took a long time to generate when i query against the base class (which are Person and Customer), But quite ok when querying the derived class (IndividualCustomer & CorporateCustomer).
I have read that using TPT is very bad for performance and i'm considering to use TPH (Table-Per-Hierarchy) because of this. But when i use TPH, when adding SalesInvoice, i will have to validate if the user is sending the correct CustomerId. Because database will allow the user sending a vendorid as CustomerId. While using TPT, database will throw error. So TPH will add complexity when doing CRUD.
Are there any suggestion how to improve TPT query on my case?
Note:
I found that when querying the base class, if i specify which fields i want to retrieve, eg Select(x => new {x.Id, x.Name}), the query looks better and does not include left outer join the derived classes.
My Queries:
//Querying base class
var people = ctx.People.Select(x => new {x.Id, x.Name}).ToList();
//Querying derived class
var corporates = ctx.CorporateCustomers.ToList();