Here's my model schema.
This is the dependent entity
public class ArticleFee
{
public int ID { get; set; }
public string Description { get; set; }
public Type Type { get; set; }
public double? FixedFee { get; set; }
public int? RangeStart { get; set; }
public int? RangeEnd { get; set; }
public double? Percentage { get; set; }
[StringLengthAttribute(1, MinimumLength = 1)]
public string ArticleLetter { get; set; }
public Article Article { get; set; }
}
public class Article
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[KeyAttribute]
[StringLengthAttribute(1, MinimumLength = 1)]
public string Letter { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public ICollection<ArticleFee> ArticleFees { get; set; }
}
Here's how I show data on my route but the ArticleFees just shows an empty array.
[HttpGetAttribute]
public IEnumerable<Article> Get()
{
return _context.Articles
.Include(a => a.ArticleFees)
.ToList();
}
Your model is good(*) and the Get() method too. Your issue is that an infinite loop is detected during the JSON serialization because Article points to ArticleFee and ArticleFee points to Article.
To solve your problem, you must configure the app in Startup.cs so that it "ignore" instead of "throw exception" when such a loop is detected. The solution in .NET Core from this SO answer:
services.AddMvc().AddJsonOptions(options => {
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
}); ;
You will need to add using Newtonsoft.Json; to the file.
(*) Assuming that your Type entity is fine.
Related
I use SQLite as a database. I have two entities as follows:
public class Topic
{
[Key]
public int Id { get; set; }
[Required]
[DataType(DataType.Text)]
[MaxLength(100)]
public string Name { get; set; }
public virtual IEnumerable<SubTopic> SubTopics { get; set; }
}
public class SubTopic
{
[Key]
public int Id { get; set; }
[Required]
[DataType(DataType.Text)]
[MaxLength(100)]
public string SubTopicname { get; set; }
[Required]
[DataType(DataType.Text)]
[MaxLength(100)]
public string Subject { get; set; }
[Required]
[DataType(DataType.Text)]
[MaxLength(300)]
public string ShortDescription { get; set; }
[Required]
[DataType(DataType.Text)]
public string Description { get; set; }
[Required]
[DataType(DataType.Date)]
public DateTime Date { get; set; }
[Required]
[DataType(DataType.Text)]
public string Reference { get; set; }
[ForeignKey("Name")]
public virtual Topic Topic { get; set; }
}
The action for getting all topics:
public async Task<IActionResult> GetAllTopics()
{
var myTopics = await _myContext.Topics.ToListAsync();
return Ok(myTopics);
}
My Context is:
public class MyContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder option)
{
option.UseSqlite("Data Source=Database.db;");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Topic>().ToTable("Topics");
modelBuilder.Entity<ImportantPost>().ToTable("ImportantPost");
modelBuilder.Entity<About>().ToTable("About");
modelBuilder.Entity<FeaturedPost>().ToTable("FeaturedPost");
modelBuilder.Entity<SubTopic>().ToTable("SubTopic");
}
public DbSet<Topic> Topics { get; set; }
public DbSet<ImportantPost> ImportantPosts {get; set;}
public DbSet<About> About { get; set; }
public DbSet<FeaturedPost> FeaturedPosts { get; set; }
public DbSet<SubTopic> SubTopic { get; set; }
}
When I try to get all topics using Swagger the output is:
My table is empty. My expected output is [] while it shows another thing.
I need an array in my output. This output causes problems in my Angular UI. How can I fix this?
I think something are wrong; response body s hould like this
{
id : 1,
name : "foo",
subTopics : []
}
check your code again or share it with us
This StackOverflow link helped me to solve my problem:
JsonSerializerOptions
I used this settings in service registration:
services.AddControllers().AddJsonOptions(x => x.JsonSerializerOptions.ReferenceHandler = null);
I've made a dotnet ef scaffold from database and the classes generated were:
public partial class Course
{
public int Id { get; set; }
public string? Description { get; set; }
}
public partial class Student
{
public int Id { get; set; }
public string? Name { get; set; }
}
public partial class StudentCourse
{
public int? IdStudent { get; set; }
public int? IdCourse { get; set; }
public virtual Student? IdStudentNavigation { get; set; }
public virtual Course? IdCourseNavigation { get; set; }
}
I want to get a List of Student where id of Course is X
I've tried _context.Student.Include("StudentCourse").Where(x=>x.Any(....) but Intellisense does not accept "Any" function.
How can i get this ?
Any(...) is a method provided by Enumerable class so you can not use it on a single Student (which is obviously not an Enumerable object).
Your configuration of many-to-many relationship is maybe missing some lines, here is my suggestion:
public partial class Course
{
public int Id { get; set; }
public string? Description { get; set; }
public List<StudentCourse> StudentCourses { get; set; }
}
public partial class Student
{
public int Id { get; set; }
public string? Name { get; set; }
public List<StudentCourse> StudentCourses { get; set; }
}
public partial class StudentCourse
{
public int? IdStudent { get; set; }
public int? IdCourse { get; set; }
public virtual Student? StudentNavigation { get; set; }
public virtual Course? CourseNavigation { get; set; }
}
In Context file:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<StudentCourse>()
.HasOne(sc => sc.StudentNavigation)
.WithMany(s => s.StudentCourses)
.HasForeignKey(sc => sc.IdStudent);
modelBuilder.Entity<StudentCourse>()
.HasOne(sc => sc.CourseNavigation)
.WithMany(c => c.StudentCourses)
.HasForeignKey(sc => sc.IdCourse);
}
Finally, your query could be:
IEnumerable<Student> students = await _context.Students
.Include(s => s.StudentCourses)
.Where(s => s.StudentCourses.Any(sc => sc.IdCourse == X)))
I am just taking your code as example but this is not a way you design entity in EF core.
Try following though.
var students
=_context.StudentCourse.Include("IdStudentNavigation").Where(x=>x.IdCourse == 1).Select(x => x.IdStudentNavigation).ToList();
Replace one with your course id.
After reading the documentation, I am not sure but I have come to the conclusion that when creating QueryDb, you cannot choose the columns to join by? And I am under the impression, you must have DTO object to copy to? You cannot copy to a regular object or a dynamic object?
public class SampleAutoQueryDb : QueryDb<MailResponseDetailOrm, object>, ILeftJoin<MailResponseDetailOrm, MailResponseOrm> { }
Can anyone provide any insight on joining my MailResponseOrm to MailResponseDetailOrm. MailResponseDetailOrm has 5 fields namely the Email address. And I would like MailResponseOrm to be joined to it by Email as well. I also, for good measure do not want to alter either columnname. Would I have to create a custom implementation or a service to do this?
UPDATE
Here is my code as posted below:
[Alias("MailReportsDetail")]
public class MailResponseDetailOrm
{
public string Email { get; set; }
public int ID { get; set; }
[Alias("RespDate")]
public DateTime? AddedDateTime { get; set; }
[Alias("DLReport")]
public string Action { get; set; }
public string ActionDetail { get; set; }
public string IP { get; set; }
public string UserAgent { get; set; }
public string EmailReferrer { get; set; }
}
[Alias("MailReports")]
public class MailResponseOrm
{
public int ID { get; set; }
public string Email { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public string City { get; set; }
public string Company { get; set; }
public string Contact { get; set; }
public string Country { get; set; }
[Alias("LastMail")]
public DateTime? ModifiedDateTime { get; set; }
[Alias("LastReport")]
public string Action { get; set; }
public DateTime? OptOut { get; set; }
public string Part { get; set; }
public string Phone { get; set; }
public string PostalCode { get; set; }
public string Source { get; set; }
public string State { get; set; }
public string Title { get; set; }
#region Obsolete
[Obsolete]
public string Class { get; set; }
[Obsolete]
public string IP { get; set; }
#endregion
}
public class SampleAutoQueryDb : QueryDb<MailResponseDetailOrm> { }
public class MyQueryServices : Service
{
public IAutoQueryDb AutoQuery { get; set; }
// Override with custom implementation
public object Any(SampleAutoQueryDb query)
{
var q = AutoQuery.CreateQuery(query, base.Request);
q.Join<MailResponseDetailOrm, MailResponseOrm>((x, y) => x.Email == y.Email)
// .Select<MailResponseDetailOrm, MailResponseOrm>((x, y) => new { x.ID, y.Email })
;
return AutoQuery.Execute(query, q);
}
}
Joins in AutoQuery needs to use OrmLite's Joins Reference conventions and all AutoQuery Services results are returned in a Typed DTO, which by default is the table being queried or you can use the QueryDb<From,Into> base class to return a custom result of columns from multiple joined tables.
You would need to use a Custom AutoQuery Implementation or your own Service implementation if you need customizations beyond this, e.g:
public class SampleAutoQueryDb : QueryDb<MailResponseDetailOrm> { }
public class MyQueryServices : Service
{
public IAutoQueryDb AutoQuery { get; set; }
// Override with custom implementation
public object Any(SampleAutoQueryDb query)
{
var q = AutoQuery.CreateQuery(query, base.Request);
q.Join<MailResponseDetailOrm,MailResponseOrm>((x, y) => x.Email == y.Email);
return AutoQuery.Execute(query, q);
}
}
// The query to join 2 objects on field names not specifically set in the class.
var q = Db.From<MailResponseDetailOrm>().Join<MailResponseDetailOrm>(x,y) => x.Email = y.Email);
// Run the query
var results = Db.Select(q);
First off, I know there are a lot of posts about the Unable to determine the principal end of an association between the types error but ever single one I see does not match my issue, if I missed one sorry about that.
I have built an Entity that will end up referencing it's self twice and when I put the code in for the first self reference it works fine, as soon as ad the code for the second it breaks. Doing some testing I have found that if I use either of the self references by them self everything works fine, it is only when I add the second self reference that it breaks. The code I am using for the self references is:
[ForeignKey("ManagerID")]
public User Manager { get; set; }
//Auditing Fields
public DateTime DateCreated { get; set; }
public int? UpdatedByUserID { get; set; }
public DateTime? DateUpdated { get; set; }
public DateTime LastAutoUpdate { get; set; }
[ForeignKey("UpdatedByUserID")]
public User UpdatedByUser { get; set; }
The full entity code block is:
public class User
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
public int ADPFileNumber { get; set; }
[Required]
public string ADUserName { get; set; }
public int AirCardCheckInLateCount { get; set; }
[Required]
public string Email { get; set; }
public DateTime? EndDate { get; set; }
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
public int ManagerID { get; set; }
public string MobilePhone { get; set; }
[Required]
public string Office { get; set; }
[Required]
public string Phone { get; set; }
public decimal PTO { get; set; }
public DateTime StartDate { get; set; }
public int VehicleCheckInLateCount { get; set; }
public int WexCardDriverID { get; set; }
[ForeignKey("ManagerID")]
public User Manager { get; set; }
//Auditing Fields
public DateTime DateCreated { get; set; }
public int? UpdatedByUserID { get; set; }
public DateTime? DateUpdated { get; set; }
public DateTime LastAutoUpdate { get; set; }
[ForeignKey("UpdatedByUserID")]
public User UpdatedByUser { get; set; }
}
What am I missing that cause the second self reference to break?
You have to indicate the principal end of both associations explicitly. You can do that with the class you had originally, without inverse collection properties:
modelBuilder.Entity<User>()
.HasOptional(u => u.Manager)
.WithMany()
.HasForeignKey(u => u.ManagerID);
modelBuilder.Entity<User>()
.HasOptional(u => u.UpdatedByUser)
.WithMany()
.HasForeignKey(u => u.UpdatedByUserID);
Note that ManagerID should be an int? as well. You can't create any User if it requires another user to preexist. That's a chicken-and-egg problem.
As mentionned in Multiple self-referencing relationships in Entity Framework, you seem to be missing the other part of the relationship.
i.e.
[InverseProperty("Manager")]
public virtual ICollection<User> ManagedUsers {get;set;}
[InverseProperty("UpdatedByUser")]
public virtual ICollection<User> UpdatedUsers {get;set;}
EDIT: based on #Gert Arnold's answer you should indeed add the [InverseProperty] attribute
I'm new to mvc. I've creted an MVC app, in which i have used code first approach. Right now i have two tables Deal and Comment. Now i want to add a new table Category in the database and new column categoryId in Deal table.
How i can update database and model?
I'm using Sql Server 2008 R2 for Database.
I've following structure of class:
namespace FBWebApp.Models
{
public class Deal
{
public int ID { get; set; } // ID
public string Title { get; set; } // Titolo del deal
public string Description { get; set; } // Descrizione dell'annuncio
public string FacebookUID { get; set; } // UID facebook dell'utente
public string Visibility { get; set; } // Visibility
public string Category { get; set; }
public int Option1 { get; set; }
public int Option2 { get; set; }
public int Option3 { get; set; }
public int Option4 { get; set; }
public string PhotoURL { get; set; } // URL of the facebook photo profile
public string Name { get; set; } // Name of the user
public string ProfileUrl { get; set; } // URL of the facebook profile
public string Photo1 { get; set; } // URL of the Photo1 (local )
public string Photo2 { get; set; }
public string Photo3 { get; set; }
public string Photo4 { get; set; }
public string Photo5 { get; set; }
}
public class Comment
{
[Key]
public int CommentId { get; set; }
public string CommentText { get; set; }
public int ID { get; set; }
[ForeignKey("ID")]
public Deal DelNav { get; set; }
}
public class DealDBContext : DbContext
{
public DealDBContext() : base("DealDBContext") { }
public DbSet<Deal> Deals { get; set; }
public DbSet<Comment> Comments { get; set; }
}
}
Try using 'update-database -force -verbose' in the Package Manager Console.
If it doesn't work, modify the migration but typing 'add-migration somename' and it will apear in the Migrations folder.
If you are new to MVC and EF, definitely check out this tutorial. It explains all about that and everything else you need to know:
http://pluralsight.com/training/Player?author=scott-allen&name=mvc4-building-m1-intro&mode=live&clip=0&course=mvc4-building
first add your model :
public class Category
{
public int ID { get; set; }
public int cateName { get; set; }
}
in Deal class :
public class Deal
{
//..
[ForeignKey("CatId")]
public virtual Category Category { get; set; }
}
after Enable Migration you should use this command in console manager to update your database :
update-database