I'm using EF Core 2.1 and I have these class in my Domain.
public class HomeSection2
{
public HomeSection2()
{
HomeSection2Detail = new List<HomeSection2Detail>();
}
public Guid ID { get; set; }
public string Title { get; set; }
public string Header { get; set; }
public List<HomeSection2Detail> HomeSection2Detail { get; set; }
}
public class HomeSection2Detail
{
public Guid ID { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string Link { get; set; }
public int? Sequence { get; set; }
public HomeSection2 HomeSection2 { get; set; }
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.RemovePluralizingTableNameConvention();
//HomeSection2
modelBuilder.Entity<HomeSection2>().HasKey(s => s.ID);
modelBuilder.Entity<HomeSection2>().Property(s => s.ID).ValueGeneratedOnAdd();
modelBuilder.Entity<HomeSection2>().Property(s => s.Title).IsRequired();
modelBuilder.Entity<HomeSection2>().Property(s => s.Header).IsRequired();
//HomeSection2Detail
modelBuilder.Entity<HomeSection2Detail>()
.HasOne(p => p.HomeSection2)
.WithMany(b => b.HomeSection2Detail);
modelBuilder.Entity<HomeSection2Detail>().HasKey(s => s.ID);
modelBuilder.Entity<HomeSection2Detail>().Property(s => s.ID).ValueGeneratedOnAdd();
modelBuilder.Entity<HomeSection2Detail>().Property(s => s.Title).IsRequired();
modelBuilder.Entity<HomeSection2Detail>().Property(s => s.Sequence).IsRequired();
}
And I have a generic repo
public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{
protected readonly DbContext Context;
public Repository(DbContext context)
{
Context = context;
}
public IEnumerable<TEntity> GetAll()
{
return Context.Set<TEntity>().ToList();
}
}
When I call GetAll from the Application var obj = _uow.HomeSection2s.GetAll() like this, it won't fill the Detail.
What you mean is reffered to as 'Lazy Loading'. It would require you to make those properties virtual, like:
public virtual List<HomeSection2Detail> HomeSection2Detail { get; set; }
You can also take a look at this anwser
More documentation on loading related data
Related
I am trying to preform a insert action using Entity Framework Core 6.0.11 with identity framework as the base, with Postgres as the database.
I am getting the following error when trying to preform an insert operation:
Serialization and deserialization of 'System.Action' instances are not supported. Path: $.MoveNextAction
This is the code of my insert:
public async Task<CampaignDetialDto> CreateCampaign(BaseCampaignDto campaignDto)
{
var userCampaign = new UserCampaign
{
UserId = UserId,
Campaign = _mapper.Map<Campaign>(campaignDto),
CampaignRole = CampaignRoleEnum.Player
};
await _userCampaignRepo.AddAsync(userCampaign);
return _mapper.Map<CampaignDetialDto>(userCampaign.Campaign);
}
The userCampaignRepo:
public class UserCampaignRepository : GenericRepository<UserCampaign>, IUserCampaignRepository
{
private readonly PostGresContext _context;
private readonly IMapper _mapper;
public UserCampaignRepository(PostGresContext context, IMapper mapper) : base(context, mapper)
{
_context = context;
_mapper = mapper;
}
}
The base repo preforming the action:
public async Task<T> AddAsync(T entity)
{
await _context.AddAsync(entity);
await _context.SaveChangesAsync();
return entity;
}
The context and models:
public class PostGresContext : IdentityDbContext<User>
{
public PostGresContext(DbContextOptions options) : base(options)
{
}
public DbSet<Campaign> Campaigns { get; set; }
public DbSet<UserCampaign> UserCampaigns { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.ApplyConfiguration(new RoleConfiguration());
modelBuilder.Entity<UserCampaign>().HasKey(x => new
{
x.UserId,
x.CampaignId
});
}
}
public class UserCampaign
{
[Key, Column(Order = 0)]
public string UserId { get; set; }
public virtual User User { get; set; }
[Key, Column(Order = 1)]
public int CampaignId { get; set; }
public virtual Campaign Campaign { get; set; }
public CampaignRoleEnum CampaignRole { get; set; } = CampaignRoleEnum.Player;
}
public class Campaign
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public DateTime CreateDate { get; set; } = DateTime.UtcNow;
public ActiveStateEnum Active { get; set; } = ActiveStateEnum.Active;
public virtual ICollection<UserCampaign> UserCampaigns { get; set; }
}
public class User : IdentityUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual ICollection<UserCampaign> UserCampaigns { get; set; }
}
public static IServiceCollection AddApplicationPostGresContext(this IServiceCollection services, IConfiguration _config)
{
services.AddDbContext<PostGresContext>(options =>
{
options.UseNpgsql(_config.GetConnectionString("PostGresConnectionString"));
});
return services;
}
I tried altering the add methods hoping it might bring a change but with no effect, I also tried to call the context directly by using _context.addAsync(XXX) or _context.SaveChangesAsync() - this also had the same results.
I would like to add a model into the Postgres database.
I'm have a issue,
i'm studyng nhibernate with c# and .net core, but i dont know how to procede on this case.
i create my table with 2FK:
public class Venda
{
public Venda()
{
VendaId = Guid.NewGuid();
DataVenda = DateTime.Now;
}
public virtual Guid VendaId { get; set; }
public virtual Guid BombomId { get; set; }
public virtual Guid ClienteId { get; set; }
public virtual DateTime DataVenda { get; set; }
public virtual IList<Bombom> Bomboms { get; set; }
public virtual IList<Cliente> Clientes { get; set; }
}
My migration is
[FluentMigrator.Migration(2)]
public class CreateTableVenda : FluentMigrator.Migration
{
public override void Up()
{
Create.Table("Venda")
.WithColumn("VendaId").AsGuid().NotNullable().PrimaryKey().Indexed()
.WithColumn("DataVenda").AsDateTime().NotNullable()
.WithColumn("BombomId").AsGuid().NotNullable().Indexed()
.WithColumn("ClienteId").AsGuid().NotNullable().Indexed();
Create.ForeignKey().FromTable("Venda").ForeignColumn("BombomId")
.ToTable("Bombom").PrimaryColumn("BombomId");
Create.ForeignKey().FromTable("Venda").ForeignColumn("ClienteId")
.ToTable("Cliente").PrimaryColumn("ClienteId");
}
public override void Down()
{
Delete.Table("Venda");
}
}
But my mapping, i'm have issues, because i want to LAZYLOAD the map entity together when i get
public class VendaMap : ClassMap<Venda>
{
public VendaMap()
{
Id(x => x.VendaId);
Map(x => x.DataVenda);
Map(x => x.BombomId).Column("Bombom").Access.CamelCaseField();
Map(x => x.ClienteId);
LazyLoad();
}
}
Could someone help me please?
First of all I have these two models to store a post in two tables one for shared data and the other contains cultured data for English and Arabic
public class Post
{
public int Id { set; get; }
public bool Active { get; set; }
public bool Featured { get; set; }
public virtual ICollection<PostContent> Contents { get; set; }
}
public class PostContent
{
public int Id { set; get; }
public string Title { get; set; }
public string Summary { get; set; }
public string Details { get; set; }
[StringLength(2)]
public string Culture { get; set; }
public int PostId { get; set; }
[InverseProperty("PostId")]
public virtual Post Post{ set; get; }
}
Mapping
public class PostMap : EntityTypeConfiguration<Post>
{
public PostMap()
{
HasKey(p => p.Id);
Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
ToTable("Posts");
}
}
public class PostContentMap : EntityTypeConfiguration<PostContent>
{
public PostContentMap()
{
HasKey(p => p.Id);
Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
HasRequired(p => p.Post).WithMany(p => p.Contents).HasForeignKey(p=>p.PostId);
ToTable("PostContents");
}
}
I have two questions
1- Is these models are connected properly. Is there something else I need to do ?
2- I need to select all Posts with their contents where the culture of the content 'en' for example. I used this:
var res = context.Posts.Include(p => p.Contents.Single(c => c.Culture.Equals("en")));
and have this error:
The Include path expression must refer to a navigation property defined on the type. Use dotted paths for reference navigation properties and the Select operator for collection navigation properties.Parameter name: path
If you know you are not going to support more than two cultures then I would just add to your Post class.
public class Post
{
public Post()
{
Contents = new List<PostContent>();
}
public int Id { set; get; }
public bool Active { get; set; }
public bool Featured { get; set; }
public int? EnglishContentId { get;set;}
public int? ArabicContentId { get;set;}
PostContent EnglishContent {get;set;}
PostContent ArabicContent {get;set;}
}
public class PostContent
{
public int Id { set; get; }
public string Title { get; set; }
public string Summary { get; set; }
public string Details { get; set; }
[StringLength(2)]
public string Culture { get; set; }/*This property is not required*/
}
public class PostMap : EntityTypeConfiguration<Post>
{
public PostMap()
{
HasKey(p => p.Id);
Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
ToTable("Posts");
HasOptional(p => p.EnglishContent).WithMany().HasForeignKey(p=>p.EnglishContentId);
HasOptional(p => p.ArabicContent).WithMany().HasForeignKey(p=>p.ArabicContentId);
}
}
public class PostContentMap : EntityTypeConfiguration<PostContent>
{
public PostContentMap()
{
HasKey(p => p.Id);
Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
ToTable("PostContents");
}
}
The Above design will simplify your design and queries, will improve the performance alot.
But if you might have to support more cultures then you got the design and mapping right.
As far as EF 5, include does not allow filters, but I am not sure about EF 6.0
atleast you can get all posts that have english contents as follows
Add using System.Data.Entity;
var res = context.Posts.Include(p => p.Contents).Where(c => c.Contents.Any(cp=>cp.Culture.Equals("en")));
In my C# project, I get an error when EF attempts to create my database
The error occurs when I call
Database.SetInitializer(new CreateDatabaseIfNotExists<ApplicationDatabase>());
The error message is
The expression 'x => x.Dependancies' is not a valid property expression. The expression should represent a property: C#: 't => t.MyProperty' VB.Net: 'Function(t) t.MyProperty'.
My Domain classes are as follows
[Table("LoggedEntity")]
public class LoggedEntity
{
public int Id { get; set; }
}
[Table("TemplateTaskDependancy")]
public class TemplateTaskDependancy : LoggedEntity
{
[Column]
public int NeededTaskId { get; set; }
[Column]
public int TaskId { get; set; }
[Required]
[ForeignKey("TaskId")]
public virtual TemplateTask Task { get; set; }
[Required]
[ForeignKey("NeededTaskId")]
public virtual TemplateTask NeededTask { get; set; }
}
[Table("TemplateTask")]
public class TemplateTask : LoggedEntity
{
public ICollection<TemplateTaskDependancy> Dependancies;
public ICollection<TemplateTaskDependancy> NeededTasks;
public virtual Template Template { get; set; }
}
[Table("Template")]
public class Template : LoggedEntity
{
public string Description { get; set; }
}
My Configuration Class is as follows
public class TemplateTaskConfiguration : EntityTypeConfiguration<TemplateTask>
{
public TemplateTaskConfiguration()
{
HasMany(x => x.Dependancies)
.WithRequired(d => d.Task)
.HasForeignKey(d => d.TaskId)
.WillCascadeOnDelete(false);
HasMany(x => x.NeededTasks)
.WithRequired(d => d.NeededTask)
.HasForeignKey(d => d.NeededTaskId)
.WillCascadeOnDelete(false);
}
}
My Context is as follows
public class ApplicationDatabase : DbContext
{
public DbSet<TemplateTask> TemplateTasks { get; set; }
public DbSet<TemplateTaskDependancy> TemplateTaskDependancies { get; set; }
public DbSet<Template> Templates { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
modelBuilder.Configurations.Add(new TemplateTaskConfiguration());
}
public void InitializeDatabase()
{
Database.SetInitializer(new CreateDatabaseIfNotExists<ApplicationDatabase>());
}
}
Quite literally, the problem is what is described. Dependancies is a field, not a property. Just define it as:
public virtual ICollection<TemplateTaskDependancy> Dependancies
{
get;
set;
}
And it should solve your problem.
I've got the following model and I want ShiftRequest and MissionRequest to have a single table in the DB.
public class RequestBase
{
public int Id { get; set; }
public DateTime? RequestDate { get; set; }
public int UserId { get; set; }
public virtual ICollection<Notification> Notifications { get; set; }
}
public class ShiftRequest : RequestBase
{
public virtual Column Column { get; set; }
}
public class MissionRequest : RequestBase
{
public virtual Mission Mission { get; set; }
}
I've tried to do it in the override void OnModelCreating(ModelBuilder modelBuilder) method but only one RequestBases table is created:
modelBuilder.Entity<ShiftRequest>().MapSingleType().ToTable("dbo.ShiftRequests");
modelBuilder.Entity<MissionRequest>().MapSingleType().ToTable("dbo.MissionRequest");
What am I doing wrong?
EDIT
Column and Mission are also entities in my model, is that acceptable?
Check the section about TPH in this article. If Mission and Column are complex types you will also find there how to map them. Generally you have to use MapHiearchy and Case methods instead of MapSingleType.
Edit:
Here is the example:
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration;
namespace EFTest
{
public class RequestBase
{
public int Id { get; set; }
public DateTime? RequestedDate { get; set; }
public int UserId { get; set; }
}
public class Mission
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<MissionRequest> MissionRequests { get; set; }
}
public class Column
{
public string Name { get; set; }
}
public class MissionRequest : RequestBase
{
public virtual Mission Mission { get; set; }
}
public class ShiftRequest : RequestBase
{
public Column Column { get; set; }
}
public class TestContext : DbContext
{
public DbSet<RequestBase> Requests { get; set; }
public DbSet<Mission> Missions { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ContainerName = "EFTest";
modelBuilder.IncludeMetadataInDatabase = false;
// Example of complex type mapping. First you have to define
// complex type. Than you can access type properties in
// MapHiearchy.
var columnType = modelBuilder.ComplexType<Column>();
columnType.Property(c => c.Name).HasMaxLength(50);
modelBuilder.Entity<Mission>()
.Property(m => m.Id)
.IsIdentity();
modelBuilder.Entity<Mission>()
.HasKey(m => m.Id)
.MapSingleType(m => new { m.Id, m.Name })
.ToTable("dbo.Missions");
modelBuilder.Entity<RequestBase>()
.Property(r => r.Id)
.IsIdentity();
// You map multiple entities to single table. You have to
// add some discriminator to differ entity type in the table.
modelBuilder.Entity<RequestBase>()
.HasKey(r => r.Id)
.MapHierarchy()
.Case<RequestBase>(r => new { r.Id, r.RequestedDate, r.UserId, Discriminator = 0 })
.Case<MissionRequest>(m => new { MissionId = m.Mission.Id, Discriminator = 1 })
.Case<ShiftRequest>(s => new { ColumnName = s.Column.Name, Discriminator = 2 })
.ToTable("dbo.Requests");
}
}
}
Edit 2:
I updated example. Now Mission is entity instead of complex type.