EFCore adding default shadow properties - asp.net-core

I'm using EFCore (With Cosmos), and for some reason it's adding a 2nd key for me, on the convention of id, despite having a property name Id, with the KeyAttribute.
Anyway I can stop this?
public class User
{
public User()
{
}
public User(ClaimsPrincipal principal)
{
Id = principal.FindFirst(ClaimTypes.NameIdentifier).Value;
FirstName = principal.FindFirst(ClaimTypes.GivenName).Value;
Surname = principal.FindFirst(ClaimTypes.Surname).Value;
Email = principal.FindFirst(ClaimTypes.Email).Value;
}
[Key]
public string Id { get; set; }
public string FirstName { get; set; }
public string Surname { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public bool Active { get; set; }
public DateTime CreatedDate { get; set; }
public DateTime LastLoginTime { get; set; }
public Company Company { get; set; }
public User CreatedBy { get; set; }
//Hack to get around EF/Cosmos Enum error
private string UserType { get; set;}
[NotMapped]
public UserType UserTypeEnum
{
get
{
if (string.IsNullOrWhiteSpace(UserType))
{
return Models.UserType.User;
}
return (UserType)Enum.Parse(typeof(UserType), UserType);
}
set
{
UserType = value.ToString();
}
}
}

You can add [Newtonsoft.Json.JsonProperty(PropertyName="id")] above your Id property
Link

Related

Entity Framework: get data from many-to-many relationship

I work on an API and when I want to get data from a many-to-many relationship, I get back null.
The connection with the database is OK, and Post date work.
But when I get info from the database, the table for the many-to-many relationship is empty
public class User
{
[Key]
public int Id { get; set; }
[Required]
[MaxLength(30)]
public string Pseudo { get; set; }
[EmailAddress]
public string Mail { get; set; }
[Required]
public string Pwd { get; set; }
[Required]
public bool IsAdmin { get; set; }
public ICollection<Project> UsersProjectstry { get; set; }
public ICollection<UserProjectMTM> UsersProjects { get; set; }
}
public class Project
{
[Key]
public int Id { get; set; }
[Required]
[MaxLength(30)]
public string Name { get; set; }
[Required]
[MinLength(100)]
public string Description { get; set; }
public string? img { get; set; }
[Required]
public DateTime StartDate { get; set; }
[Required]
public DateTime EndDate { get; set; }
[Required]
public int SumGoal { get; set; }
public int Sum { get; set; }
[Required]
public User ProjectManager { get; set; }
public ICollection<UserProjectMTM> Donator { get; set; }
}
public class UserProjectMTM
{
public int PId { get; set; }
public Project Project { get; set; }
public int UId { get; set; }
public User User { get; set; }
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfiguration(new ProjectConfig());
modelBuilder.ApplyConfiguration(new UserConfig());
modelBuilder.ApplyConfiguration(new CommentConfig());
modelBuilder.Entity<UserProjectMTM>().HasKey(x => new { x.UId, x.PId });
modelBuilder.Entity<UserProjectMTM>().HasOne(p=> p.Project).WithMany(u=> u.Donator).HasForeignKey(x=>x.PId);
modelBuilder.Entity<UserProjectMTM>().HasOne(u => u.User).WithMany(p=> p.UsersProjects).HasForeignKey(x => x.UId);
}
public IEnumerable<TEntity> GetAll()
{
return _Context.Set<TEntity>();
}
public TEntity? GetById(params object[] Id)
{
return _Context.Set<TEntity>().Find(Id);
}
I try a lot of things - so far without success.
I'm junior and I can't find the solution - please help.

Getting error when adding new object with HTTP POST in .NETCore

I am new to .NetCore and Blazor. I am trying to do a POST of an new Anime, but I am allways getteing the error "The Genre field is required." I have added the genreId to the JSON Object but still the same error -> Screenshot of the error
It's one to many relation, where one animal can have only one genre but one genre can have many enemies.
I don't know if it's useful but here are screenshots of my two tables in the DB -> Anime table and the Genre tab
Here are my to Models:
Anime model
public class Anime
{
public int Id { get; set; }
public string Title { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public string CoverImage { get; set; } = string.Empty;
public string Author { get; set; } = string.Empty;
public Genre Genre { get; set; }
public string Studio { get; set; } = string.Empty;
public DateTime? ReleaseDate { get; set; }
public DateTime? EndDate { get; set; }
}
Genre model
public class Genre
{
public int Id { get; set; }
public string Title { get; set; } = string.Empty;
[JsonIgnore]
public List<Anime> Animes { get; set; }
}
AnimeService where I am adding the new anime to the DB
public async Task<ServiceResponse<List<Anime>>> AddAnime(Anime NewAnime)
{
ServiceResponse<List<Anime>> serviceResponse = new ServiceResponse<List<Anime>>();
_dataContext.Animes.Add(NewAnime);
await _dataContext.SaveChangesAsync();
var animes = await _dataContext.Animes
.Include(a => a.Genre)
.ToListAsync();
if (animes == null)
{
serviceResponse.Success = false;
serviceResponse.Message = "Animes could be found!";
}
serviceResponse.Data = animes;
return serviceResponse;
}
AnimeController
[HttpPost]
[Route("AddAnime")]
public async Task<ActionResult<ServiceResponse<List<Anime>>>> AddAnime(Anime NewAnime)
{
return Ok(await _animeService.AddAnime(NewAnime));
}
As we discussed on Discord:
You're using .NET 6 with nullables enabled.
As an Anime can exist before it has Genre assigned I would configure the tables like this:
public class Anime
{
public int Id { get; set; }
public string Title { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public string CoverImage { get; set; } = string.Empty;
public string Author { get; set; } = string.Empty;
public int? GenreId { get; set; }
[ForeignKey(nameof(GenreId))]
public Genre? Genre { get; set; }
public string Studio { get; set; } = string.Empty;
public DateTime? ReleaseDate { get; set; }
public DateTime? EndDate { get; set; }
}
public class Genre
{
public int Id { get; set; }
public string Title { get; set; } = string.Empty;
[JsonIgnore]
[InverseProperty(nameof(Anime.Genre))]
public List<Anime> Animes { get; set; }
}
It seems that your Anime instance don't habe a Genre object but it is requiered in your db context
You have to add navigation property GenreId as nullable if you think that Genre is optional
public class Anime
{
public int Id { get; set; }
... another properties
public int? GenreId { get; set; }
public virtual Genre Genre { get; set; }
}
after this you will have to make a new database migration

Is it possible to link one table to another with entity framework core without FKs?

I have 2 tables company and user. Company will have one created by user and one modified user - these will be admin users. User will belong to one company but one admin user could create or modify multiple companies.
I'm having a hard time using entity framework core in my .net core app to join company and user so when I get a company record I have the created by and modified user information.
My company and user classes look like this:
public class Company
{
[Key]
public Guid Id { get; set; }
public DateTime Created { get; set; }
public Guid Created_By { get; set; }
public virtual ApplicationUser CreatedByUser { get; set; }
public DateTime Modified { get; set; }
public Guid Modified_By { get; set; }
public virtual ApplicationUser ModifiedByUser { get; set; }
public string Company_Name { get; set; }
}
public class ApplicationUser: IdentityUser<Guid>
{
[Column("ID")]
public override Guid Id { get; set; }
[Column("CREATED")]
public DateTime Created { get; set; }
[Column("CREATED_BY")]
public Guid? CreatedBy { get; set; }
[Column("MODIFIED")]
public DateTime Modified { get; set; }
[Column("MODIFIED_BY")]
public Guid? ModifiedBy { get; set; }
[Column("FIRST_NAME")]
public string FirstName { get; set; }
[Column("LAST_NAME")]
public string LastName { get; set; }
[Column("EMAIL")]
public override string Email { get; set; }
[Column("NORMALIZED_EMAIL")]
public override string NormalizedEmail { get; set; }
[Column("EMAIL_CONFIRMED")]
public override bool EmailConfirmed { get; set; }
[Column("USER_NAME")]
public override string UserName { get; set; }
[Column("NORMALIZED_USER_NAME")]
public override string NormalizedUserName { get; set; }
[Column("COMPANY_ID")]
public Guid CompanyId { get; set; }
[Column("PHONE_NUMBER")]
public override string PhoneNumber { get; set; }
[Column("PHONE_NUMBER_CONFIRMED")]
public override bool PhoneNumberConfirmed { get; set; }
[Column("TITLE")]
public string Title { get; set; }
[Column("ACTIVE")]
public bool Active { get; set; }
[Column("ROLE_ID")]
public int UserRoleId { get; set; }
[Column("TYPE_ID")]
public int TypeId { get; set; }
[Column("PASSWORD_HASH")]
public override string PasswordHash { get; set; }
[Column("SECURITY_STAMP")]
public override string SecurityStamp { get; set; }
[Column("CONCURRENCY_STAMP")]
public override string ConcurrencyStamp { get; set; }
[Column("TWO_FACTOR_ENABLED")]
public override bool TwoFactorEnabled { get; set; }
[Column("LOCKOUT_END")]
public override DateTimeOffset? LockoutEnd { get; set; }
[Column("LOCKOUT_ENABLED")]
public override bool LockoutEnabled { get; set; }
[Column("ACCESS_FAILED_COUNT")]
public override int AccessFailedCount { get; set; }
}
My DbContext class looks like this:
public class DbContext : IdentityDbContext<ApplicationUser, ApplicationRole, Guid, ApplicationUserClaim, ApplicationUserRole, IdentityUserLogin<Guid>, IdentityRoleClaim<Guid>, IdentityUserToken<Guid>>
{
public DbContext(DbContextOptions<DbContext> options) : base(options)
{
}
public virtual DbSet<Company> Companies { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<Company>().ToTable("COMPANY").Property<Guid>("Created_By");
builder.Entity<Company>().HasOne(x => x.CreatedByUser).WithOne().HasForeignKey("Created_By");
builder.Entity<ApplicationUser>().ToTable("USER");
builder.Entity<ApplicationUser>().HasKey(x => x.Id);
builder.Entity<ApplicationUserClaim>().ToTable("USER_CLAIMS");
builder.Entity<ApplicationRole>().ToTable("IDENTITY_ROLES");
builder.Entity<IdentityUserRole<Guid>>().HasKey(p => new { p.UserId, p.RoleId });
builder.Entity<ApplicationUserRole>().ToTable("IDENTITY_USER_ROLES");
}
}
And I was trying to get companies like this:
public async Task<List<Company>> GetAllCompanies()
{
return await _locationDbContext.Companies.ToListAsync();
}
Currently I am getting this error:
System.InvalidOperationException: 'You are configuring a relationship between 'Company' and 'ApplicationUser' but have specified a foreign key on 'Created_By'. The foreign key must be defined on a type that is part of the relationship.'
Is there an easier way to do this? Really all I want is the username of the user that created of modified the company record? If I was doing this with just sql I would just use a basic Join but Im not sure how to do that with entity framework. Worst case I would just get all the companies and then loop through doing a select on the user table where ID = Created_By
As #IvanStoev noticed without FKs it is not possible. But you can still use EF to join 2 tables.
In your case you have to unmap user from company and maybe it is a good idea to make user Guid nullable:
public class Company
{
[Key]
public Guid Id { get; set; }
public string Company_Name { get; set; }
public DateTime Created { get; set; }
public DateTime Modified { get; set; }
public Guid Created_By { get; set; }
public Guid Modified_By { get; set; }
[NotMapped]
public ApplicationUser CreatedByUser { get; set; }
// or better
[NotMapped]
public string CreatedByUser { get; set; }
[NotMapped]
public ApplicationUser ModifiedByUser { get; set; }
//or better
[NotMapped]
public string ModifiedByUser { get; set; }
}
and remove
builder.Entity<Company>().HasOne(x => x.CreatedByUser).WithOne().HasForeignKey("Created_By");
you still can join them like this
var companies= (
from c in _locationDbContext.Companies
join uc in _locationDbContext.ApplicatonUser on c.Created_By equals uc.Id
join um in _locationDbContext.ApplicatonUser on c.Modified_By equals um.Id
select new Company
{
....
CreatedByUser = uc,
ModifiedByUser = um
// or usually
CreatedByUser = uc.FirstName + " " + uc.LastName,
ModifiedByUser = um.FirstName + " " + um.LastName,
}).ToList();
This is one way of accomplishing this.
This is a simplified version of class Company:
public class Company
{
public int CompanyID { get; set; }
public string Name { get; set; }
//
// Relations
public string CreatorID { get; set; }
public ApplicationUser Creator { get; set; }
public string LastModifiedByID { get; set; }
public ApplicationUser LastModifiedBy { get; set; }
}
CreatorID and LastModifiedByID will be used by EF for determining the FK's. These are the fields you have to work with when updating the Db, not Creator and LastModfiedBy, yet you can also do it but with more lines of code.
Now, you can add companies the usual way, this code searches for the company, and creates one if not found, just sample code, not serious, really:
var companyName = "My company";
var company = context.Companies.Include(c => c.Creator).Include(c => c.Creator).FirstOrDefault(c => c.Name == companyName);
if (company == null)
{
company = new Company
{
Name = "My company",
CreatorID = user.Id,
LastModifiedByID = user.Id
};
context.Companies.Add(company);
context.SaveChanges();
}
And retrieving the companies with all relations filled is a matter or using calls to Include() and ThenInclude(), like here:
var companies = context.Companies.Include(c => c.Creator).Include(c => c.Creator).ToList();
I omitted all filtering logic for the sake of simplicity.

The problem with Include method using on Db context - Asp.Net Core

I'v got a backend on Asp.Net Core. Structure of the database looks that:
User - the basics information about user: login, password etc.
Profile - this entity is connected to the"User" one to one relation
Profile photos- each of the users has a own collection of photos.
This entity is connected to the "Profile"
Here is the "User" entity:
public class User
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Username { get; set; }
public byte[] PasswordHash { get; set; }
public byte[] PasswordSalt { get; set; }
public Profile Profile { get; set; }
}
Then Profile:
public class Profile
{
[ForeignKey("User")]
public int Id { get; set; }
public string BannerImageUrl { get; set; }
public string ProfileImageUrl { get; set; }
public string ShortDescription { get; set; }
public string Description { get; set; }
public User User { get; set; }
public ICollection<ProfilePhotos> ProfilePhotos { get; set; }
}
And "ProfilePhotos":
public class ProfilePhotos
{
public int Id { get; set; }
public string ImageUrl { get; set; }
public int ProfileId { get; set; }
public Profile Profile { get; set; }
}
I want to get all profile photos so I created a endpoint to to that:
[HttpGet("{username}/photos")]
public IActionResult GetPhotos(string username)
{
var profilePhotos = _profileService.GetAllPhotos(username);
var model = _mapper.Map<IList<ProfilePhotosModel>>(profilePhotos);
return Ok(model);
}
To get all photos I use a method from "profileService":
public IEnumerable<ProfilePhotos> GetAllPhotos(string username)
{
return _context.ProfilePhotos.Include(a=>a.Profile).ThenInclude(b=>b.User).Where(x => x.Profile.User.Username == username);
}
On response I want to get a id of photo, photoUrl and username so I mapped my profile photos to "ProfilePhotosModel"
public class ProfilePhotosModel
{
public int Id { get; set; }
public string ImageUrl { get; set; }
public string Username { get; set; }
}
but unfortunately on response I only get Id and photoUrl. The username is null :(
What am I doing wrong?
You could add custom mapping for the Username property.
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<ProfilePhotos, ProfilePhotosModel>()
.ForMember(m => m.Username, exp => exp.MapFrom(p => p.Profile.User.Username));
});

Criteria question with NHibernate

I'm trying to use NHibernate for data access, and I have 2 simple entities that look like this:
public class User : IEntity
{
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string Logon { get; set; }
public string Password { get; set; }
public ICollection<Role> Roles { get; set; }
public bool IsNew
{
get { return (ID == 0) ? true : false; }
}
public User()
{
Roles = new List<Role>();
}
}
public class Role : IEntity
{
public int ID { get; set; }
public string RoleName { get; set; }
public string RoleDescription { get; set; }
public bool IsNew
{
get { return (ID == 0) ? true : false; }
}
}
My question......how do I construct a Criteria if I want to find any user that contains a Role with an ID of 1 in it's Roles collection?
Never mind, this ended up being relatively straight forward to do:
// role was the role passed in to my Find method.
var criteria = DetachedCriteria.For(typeof (User))
.CreateCriteria("Roles")
.Add(Restrictions.Eq("ID", role.ID));