problems with include in EF Core 6 against an existing database - entity-framework-core

I have this Model which is a mapping of existing tables in a SAP database (where there is not a single relationship defined between the tables) that I want to attack via EF Core and LINQ
I have coded the relationships many-to-many
modelBuilder.Entity<Actividad>().ToTable("OCLG");
modelBuilder.Entity<Proyecto>().ToTable("OPRJ");
modelBuilder.Entity<Empleado>().ToTable("OHEM");
modelBuilder.Entity<Usuario>().ToTable("OUSR");
modelBuilder.Entity<Fase>().ToTable("[#ITAPROYSEG]");
modelBuilder.Entity<Actividad>()
.HasKey(a => new { a.U_Proy, a.U_Fase, a.AttendUser});
modelBuilder.Entity<Actividad>()
.HasOne(a => a.Proyecto)
.WithMany(p => p.Actividades)
.HasForeignKey(a => a.U_Proy);
modelBuilder.Entity<Actividad>()
.HasOne(a => a.Fase)
.WithMany(f => f.Actividades)
.HasForeignKey(a => a.U_Fase);
modelBuilder.Entity<Actividad>()
.HasOne(a => a.Usuario)
.WithMany(u => u.Actividades)
.HasForeignKey(a => a.AttendUser);
}
And the relationship one-to-one
public class Empleado
{
public int EmpId { get; set; }
[Key]
public string U_CodEmpl { get; set; }
public string Email { get; set; }
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
public string NombreCompleto=>$"{FirstName} {MiddleName} {LastName}";
public Usuario Usuario { get; set; }
}
public class Usuario
{
[Key]
[Column(TypeName ="smallint")]
public int Internal_K { get; set; }
[ForeignKey("Empleado")]
public string User_Code { get; set; }
public Empleado Empleado { get; set; }
public List<Actividad> Actividades { get; set; }
}
When I query this in database directly
I get 479 rows
Now I try it in my Solution
void GetActividadesByEmpleadoYAñoRaw(string codigoSAPEmpleado, int año)
{
var empleado=_context.Empleados.Include(e=>e.Usuario).FirstOrDefault(e=>e.U_CodEmpl==codigoSAPEmpleado);
When I debug here I get this
Include gives me the Usuarioassociated but If I try to receive the Actividades associated to the Usuario
void GetActividadesByEmpleadoYAñoRaw(string codigoSAPEmpleado, int año)
{
var empleado = _context.Empleados.Include(e => e.Usuario).ThenInclude(u => u.Actividades).FirstOrDefault(e => e.U_CodEmpl == codigoSAPEmpleado);
I get only 143 rows
If I try a FromRaw query
var empleado = _context.Empleados.Include(e => e.Usuario).ThenInclude(u => u.Actividades).FirstOrDefault(e => e.U_CodEmpl == codigoSAPEmpleado);
var actividades = _context.Actividades
.FromSqlRaw(#"select AttendUser, U_Proy, U_Fase, Recontact, Durtype, Duration
from oclg
where AttendUser=" + empleado.Usuario.Internal_K + " and year(recontact)=" + año +
"order by Recontact, U_Proy, U_Fase").ToList();
Any idea, please?
Thanks

Related

Entity Framework Core 3 : Pulling data from multiple sources using join

I basically have three tables that I need to query information to get PersonNotes. I am using Entity Framwork Core 3.
Person Table
PersonNote Table
PersonNoteAttachment Table
One person can have many personnotes and one personnote can contain many PersonNoteAttachment.
I need Person table to get the FirstName and LastName which is mapped to the AuthorName in the PersonNote User data model. You can see the mapping section which shows the mapping.
DataModels
namespace Genistar.Organisation.Models.DataModels
{
[Table(nameof(PersonNote), Schema = "common")]
public class PersonNote
{
public int Id { get; set; }
public int PersonId { get; set; }
[ForeignKey("PersonId")]
public Person Person { get; set; }
public string Note { get; set; }
public int AuthorId { get; set; }
[ForeignKey("AuthorId")]
public Person Author { get; set; }
public string CreatedBy { get; set; }
public DateTime Created { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime RecordStartDateTime { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime RecordEndDateTime { get; set; }
}
}
namespace Genistar.Organisation.Models.DataModels
{
[Table(nameof(PersonNoteAttachment), Schema = "common")]
public class PersonNoteAttachment
{
public int Id { get; set; }
public int PersonNoteId { get; set; }
[ForeignKey("PersonNoteId")]
public PersonNote PersonNote { get; set; }
public string Alias { get; set; }
public string FileName { get; set; }
public string MimeType { get; set; }
public int Deleted { get; set; }
public string CreatedBy { get; set; }
public DateTime Created { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime RecordStartDateTime { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime RecordEndDateTime { get; set; }
}
}
User Model - This is the model that I am returning to the client application
namespace Genistar.Organisation.Models.User
{
[Table(nameof(PersonNote), Schema = "common")]
public class PersonNote
{
public int Id { get; set; }
public int PersonId { get; set; }
public string Note { get; set; }
public int AuthorId { get; set; }
public string AuthorName { get; set; }
public string CreatedBy { get; set; }
public DateTime Created { get; set; }
}
}
Mapping
CreateMap<Genistar.Organisation.Models.DataModels.PersonNote, Genistar.Organisation.Models.User.PersonNote>()
.ForMember(t => t.Id, opt => opt.MapFrom(s => s.Id))
.ForMember(t => t.PersonId, opt => opt.MapFrom(s => s.PersonId))
.ForMember(t => t.AuthorName, opt => opt.MapFrom(s => s.Author.FirstName + " " + s.Author.LastName))
.ForMember(t => t.Note, opt => opt.MapFrom(s => s.Note))
.ForMember(t => t.AuthorId, opt => opt.MapFrom(s => s.AuthorId))
.ForMember(t => t.CreatedBy, opt => opt.MapFrom(s => s.CreatedBy))
.ForMember(t => t.Created, opt => opt.MapFrom(s => s.Created));
The following query works but is only pulling data from Person and PersonNote table. I am looking at getting the PersonNoteAttachment as well. How do I do that ? I would basically need FileName & MimeType
field populated in User.PersonNote model. If you see above I have created a PersonNoteAttachment data model
Repository
public IQueryable<PersonNote> GetPersonNotes(int personId)
{
var personNotes = _context.PersonNotes.Include(x => x.Person).Include(x=> x.Author).Where(p => p.PersonId == personId);
return personNotes;
}
API :
[FunctionName(nameof(GetPersonNote))]
[UsedImplicitly]
public Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "person-note/{id}")] HttpRequest req,
int id) => _helper.HandleAsync(async () =>
{
//await _helper.ValidateRequestAsync(req, SecurityPolicies.ViewNotes);
var personNotes = await _organisationRepository.GetPersonNotes(id).ProjectTo<PersonNote>(_mapper.ConfigurationProvider).ToListAsync();
return new OkObjectResult(personNotes);
});
My approach was to do it the following way in the repository but I need to return the PersonNote datamodel in the repository. I cannot add those additional fields in the model because it say invalid columns.How do I approach this ?
var personNotes = _context.PersonNotes
.Include(x => x.Person)
.Include(x => x.Author)
.Where(p => p.PersonId == personId)
.Join(_context.PersonNotesAttachments, c => c.Id, cm => cm.PersonNoteId, (c, cm) => new
{
cm.PersonNote.Id,
cm.PersonNote.PersonId,
cm.PersonNote.Person,
cm.PersonNote.Note,
cm.FileName,
cm.MimeType,
cm.Alias,
cm.PersonNote.AuthorId,
cm.PersonNote.CreatedBy,
cm.PersonNote.Created
});
I have resolved the issue
I just had to add the following line in PersonNote datamodel
public PersonNoteAttachment PersonNoteAttachment { get; set; }
Added the new fields to the PersonNote usermodel and did the following mapping
.ForMember(t => t.FileName, opt => opt.MapFrom(s => s.PersonNoteAttachment.FileName))
.ForMember(t => t.MimeType, opt => opt.MapFrom(s => s.PersonNoteAttachment.MimeType))
.ForMember(t => t.Alias, opt => opt.MapFrom(s => s.PersonNoteAttachment.Alias))

Inserting a dependent entity when inserting a parent with Entity Framework

I am having an issue inserting a record for a dependent entity when inserting the parent. Below is my entity definitions and mappings
EBUser
public partial class EBUser : ModelBase, IUser<long>
{
public string UserName { get; set; }
public string Password { get; set; }
public long AccountId { get; set; }
public EBAccount EbAccount { get; set; }
public string Email { get; set; }
public DateTime CreatedDate { get; set; }
public DateTime? LastUpdateDate { get; set; }
public virtual EBUserInfo UserInfo { get; set; }
}
EBUserInfo
public partial class EBUserInfo : ModelBase
{
public string Email { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string DisplayName { get; set; }
public virtual EBUser User { get; set; }
}
EBUserMapping
public class EBUserMapping : BaseEntityMapping<EBUser>
{
public EBUserMapping()
{
this.ToTable("Users");
this.Property(u => u.AccountId).HasColumnName("AccountId");
this.Property(u => u.Password).HasColumnName("Password");
this.Property(u => u.UserName).HasColumnName("UserName");
this.Property(u => u.Email).HasColumnName("Email");
this.Property(u => u.CreatedDate).HasColumnName("CreatedDate");
this.Property(u => u.LastUpdateDate).HasColumnName("LastUpdateDate").IsOptional();
//this.HasRequired(u => u.UserInfo).WithRequiredDependent(u => u.User);
this.HasRequired(t => t.EbAccount)
.WithMany(t => t.Users)
.HasForeignKey(d => d.AccountId);
}
}
EBUserInfoMapping
public class EBUserInfoMapping :BaseEntityMapping<EBUserInfo>
{
public EBUserInfoMapping()
{
this.ToTable("UserInfo");
this.Property(u => u.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
this.Property(u => u.Email).HasColumnName("Email");
this.Property(u => u.FirstName).HasColumnName("FirstName");
this.Property(u => u.LastName).HasColumnName("LastName");
this.Property(u => u.DisplayName).HasColumnName("DisplayName");
// Relationships
this.HasRequired(t => t.User).//WithRequiredDependent(t => t.UserInfo);
WithOptional(t => t.UserInfo);
}
}
In the database schema, all tables have an ID column but in the EBUserInfor class the Id column is both the primary and foreign key to the EBUsers table.
The BaseEntityuMapping maps the Id column and set the DatabaseGenerationOptions to identity but in the EBUserinfoMapping class I overwrite that with a DatabaseGenerationOption of None.
When I insert a new EBUser record using Entity Framework, the user record is created but no userInfo record is created
Please help
Thanks
Map user to userInfo as
EBuser user= new EBuser();
// fill here user
EBuserInfo info = new EBuserInfo();
Info.userInfo= user;
// fill here rest info
db.add(info);
db.saveChanges();

Entity Framework Many To Many returns null

I have Many To Many relationship defined and when I try to query for the records that should be in the map I get null.
public class Record
{
public int RecordId { get; set; }
public DateTime DateRecordCreated { get; set; }
public ICollection<Street> Streets { get; set; }
public ICollection<Street> CrossStreets { get; set; }
}
public class RecordMap : EntityTypeConfiguration<Record>
{
public RecordMap()
{
// Primary Key
this.HasKey(t => t.RecordId);
this.HasMany(r => r.Streets)
.WithMany(c => c.Records)
.Map(sl =>
{
sl.ToTable("StreetRecordMap", "dbo");
sl.MapLeftKey("RecordId");
sl.MapRightKey("StreetId");
});
this.HasMany(r => r.CrossStreets)
.WithMany(c => c.AnotherRecord)
.Map(sl =>
{
sl.ToTable("AnotherStreetRecordMap", "dbo");
sl.MapLeftKey("RecordId");
sl.MapRightKey("StreetId");
});
this.Property(t => t.DateRecordCreated).IsRequired();
}
}
public class House : Record
{
public string HouseNumber { get; set; }
public string StreeName { get; set; }
public int ZipCode { get; set; }
}
public class Street
{
public int StreetId { get; set; }
public string StreetName { get; set; }
public ICollection<Record> Records { get; set; }
public ICollection<Record> AnotherRecord { get; set; }
}
Now when I run the following query below I get houses.CrossStreets as null, I tried adding enabling lazy loading and had the same out come.
public static void GetRecords()
{
using (var context = new SandboxContext())
{
var entities = context.Houses.Include(r => r.CrossStreets);
var houses = entities.ToList();
}
}

Entity Framework many to many fluent API

I'm working with an existing database whose schema is fixed.
There are 3 many-to-many relations, Contact-Group, Contact-Department and Contact-Team.
There is a common table, ContactRelation that acts as the middle table for all 3 relations.
public class Contact
{
[Column("CtcID")]
public int Id { get; set; }
[Column("CtcFirstName")]
public string FirstName { get; set; }
[Column("CtcFamilyName")]
public string LastName { get; set; }
public virtual ICollection<ContactRelation> GroupRelations { get; set; }
public virtual ICollection<ContactRelation> DepartmentRelations { get; set; }
public virtual ICollection<ContactRelation> TeamRelations { get; set; }
}
public class Group
{
[Key, Column("GRID")]
public int Id { get; set; }
[Column("GRName")]
public string Name { get; set; }
public virtual ICollection<ContactRelation> ContactRelations { get; set; }
}
public class Department
{
[Key, Column("DEPID")]
public int Id { get; set; }
[Column("DEPName")]
public string Name { get; set; }
public virtual ICollection<ContactRelation> ContactRelations { get; set; }
}
public class Team
{
[Key, Column("TMID")]
public int Id { get; set; }
[Column("TransCode")]
public string TransCode { get; set; }
public virtual ICollection<ContactRelation> ContactRelations { get; set; }
}
public class ContactRelation
{
[Key]
[Column("CtcRelnID")]
public int Id { get; set; }
[Column("GRID")]
public int GroupId { get; set; }
[Column("DEPID")]
public int DepartmentId { get; set; }
[Column("TMID")]
public int TeamId { get; set; }
[Column("CUCtcID")]
public int ContactId { get; set; }
[Column("RCode")]
public string RoleCode { get; set; }
}
In my mapping, I have the following code:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Group>()
.HasMany(g => g.ContactRelations)
.WithRequired()
.HasForeignKey(r => r.GroupId);
modelBuilder.Entity<Department>()
.HasMany(c => c.ContactRelations)
.WithRequired()
.HasForeignKey(r => r.DepartmentId);
modelBuilder.Entity<Team>()
.HasMany(s => s.ContactRelations)
.WithRequired()
.HasForeignKey(r => r.TeamId);
modelBuilder.Entity<Contact>()
.HasMany(c => c.GroupRelations)
.WithRequired()
.HasForeignKey(r => r.ContactId);
modelBuilder.Entity<Contact>()
.HasMany(c => c.DepartmentRelations)
.WithRequired()
.HasForeignKey(r => r.ContactId);
modelBuilder.Entity<Contact>()
.HasMany(c => c.TeamRelations)
.WithRequired()
.HasForeignKey(r => r.ContactId);
}
I then try to execute the following query:
var ctc = repo.Contacts
.Include("GroupRelations")
.Include("DepartmentRelations")
.FirstOrDefault(c => c.FirstName.ToLower() == "jason");
and I keep getting error:
System.Data.SqlClient.SqlException:
Invalid column name 'Contact_Id1'.
Invalid column name 'Contact_Id'.
Invalid column name 'Contact_Id1'.
Invalid column name 'Contact_Id2'.
I read somewhere that a table cannot participate in more than one many-to-many relations. Is that true? Is it because the ContactRelation table is used more than once that I'm getting this error?
If so, what's the correct way to map these relations, without modifying the database schema.?
PS: I'm working with EF6.1
Thanks for your help.
Remove this mapping
modelBuilder.Entity<Contact>()
.HasMany(c => c.TeamRelations)
.WithRequired()
.HasForeignKey(r => r.ContactId);
and then try executing your query.

Entity framework (CTP5, Fluent API). Rename column of navigation property

I have two entites:
public class Address
{
public int Id { get; set; }
public string FirstName { get; set;
public string LastName { get; set; }
}
public partial class Customer
{
public int Id { get; set; }
public string Email { get; set; }
public string Username { get; set; }
public virtual Address BillingAddress { get; set; }
public virtual Address ShippingAddress { get; set; }
}
Below are mapping classes:
public partial class AddressMap : EntityTypeConfiguration<Address>
{
public AddressMap()
{
this.ToTable("Addresses");
this.HasKey(a => a.Id);
}
}
public partial class CustomerMap : EntityTypeConfiguration<Customer>
{
public CustomerMap()
{
this.ToTable("Customer");
this.HasKey(c => c.Id);
this.HasOptional<Address>(c => c.BillingAddress);
this.HasOptional<Address>(c => c.ShippingAddress);
}
}
When database is generated, my 'Customer' table has two columns for 'BillingAddress' and 'ShippingAddress' properties. Their names are 'AddressId' and 'AddressId1'.
Question: how can I rename them to 'BillingAddressId' and 'ShippingAddressId'?
Basically you want to customize the FK column name in an independent association and this code will do this for you:
public CustomerMap()
{
this.ToTable("Customer");
this.HasOptional<Address>(c => c.BillingAddress)
.WithMany()
.IsIndependent().Map(m =>
{
m.MapKey(a => a.Id, "BillingAddressId");
});
this.HasOptional<Address>(c => c.ShippingAddress)
.WithMany()
.IsIndependent().Map(m =>
{
m.MapKey(a => a.Id, "ShippingAddressId");
});
}