How to extend Application User to hold a collection of orders? - asp.net

I'm trying to extend Application User (using Code-First) to hold a collection of orders, but I'm getting errors.
My Order class is
public class Order
{
public Order()
{
OrderDetails = new HashSet<OrderDetails>();
}
public int ID { get; set; }
public DateTime OrderDate { get; set; }
public string UserId { get; set; }
public bool IsDelivered { get; set; }
public bool IsReturned { get; set; }
public virtual ApplicationUser User { get; set; }
public virtual ICollection<OrderDetails> OrderDetails { get; set; }
}
And I'm trying to extend Application user like this
public class ApplicationUser : IdentityUser
{
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
public string FirstName { get; set; }
public string LastName { get; set; }
public string CompanyName { get; set; }
public string Profession { get; set; }
public string TaxAuthority { get; set; }
public string TaxNumber { get; set; }
public string Address { get; set; }
public string PostalCode { get; set; }
public string City { get; set; }
public string Region { get; set; }
public string Country { get; set; }
public string Phone { get; set; }
public string MobilePhone { get; set; }
public bool NewsLetterSubscribe { get; set; } = false;
public DateTime TimeStamp { get; set; } = DateTime.Now;
public ICollection<Order> Orders { get; set; }
}
And I'm getting the following errors:
QCMS.Models.IdentityUserLogin: : EntityType 'IdentityUserLogin' has no key defined. Define the key for this EntityType.
QCMS.Models.IdentityUserRole: : EntityType 'IdentityUserRole' has no key defined. Define the key for this EntityType.
IdentityUserLogins: EntityType: EntitySet 'IdentityUserLogins' is based on type 'IdentityUserLogin' that has no keys defined.
IdentityUserRoles: EntityType: EntitySet 'IdentityUserRoles' is based on type 'IdentityUserRole' that has no keys defined.
Can you please help me to solve this problem?
UPDATE:
I'm using two db contexts. The one provided for Individual User Account (when the project is first created) and a second one named "qvModel" that is for all other database classes of my project.
public partial class qvModel : DbContext
{
public qvModel()
: base("name=qvModel")
{
}
//APPSETTINGS
public virtual DbSet<AdminLog> AdminLog { get; set; }
public virtual DbSet<WebLog> WebLog { get; set; }
//LANGUAGES
public virtual DbSet<Language> Languages { get; set; }
.
.
.
public virtual DbSet<Order> Orders { get; set; }
public virtual DbSet<OrderDetails> OrderDetails { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Precision attribute for decimals
Precision.ConfigureModelBuilder(modelBuilder);
modelBuilder.Entity<Language>()
.HasMany(e => e.Brochures)
.WithRequired(e => e.Language)
.WillCascadeOnDelete(true);
.
.
.
modelBuilder.Entity<Order>()
.HasMany(c => c.OrderDetails)
.WithRequired(c => c.Order)
.WillCascadeOnDelete(true);
modelBuilder.Entity<ApplicationUser>()
.HasMany(c => c.Orders)
.WithRequired(c => c.User)
.WillCascadeOnDelete(true);
base.OnModelCreating(modelBuilder);
}
}

I found a solution that is very simple.
The solution is to inherit from IdentityDbContext like this
public class qvModel : IdentityDbContext<ApplicationUser>
{
public qvModel()
: base("name=qvModel")
{
}
I was also missing the following line from OnModelCreating
base.OnModelCreating(modelBuilder);
After these changes, my migration is working and I stopped getting the errors I mentioned.

Related

EF Core 3.18 get sum and count from related table

I have a web api where I am trying to get sum and count of a related table. Using .net core 3 and EF Core 3.1.8.
This is what I have tried:
_context.Books
.Include(r => r.BookCategories)
.Include(r => r.Resources)
.Include(r => r.Ratings.GroupBy(g => g.Bookid).Select(s => new { SumAllVotes = s.Sum(item => item.Rating) }))
.ToListAsync();
But I just get an error message. (see below).
I find it difficault debugging with EF Core as I dont know where it is going wrong. Have been trying a couple of hours, but whatever I write I get the same error message.
Thought maybe you guys were able to see what was wrong.
What I want
I am trying to get Sum of all Rating inside table Ratings.
Rating contains only 0 or 1. And I am trying to sum ratings on each bookid. I wanted to have it in this class public int SumAllVotes { get; set; }.
Because I list out all Books...and one of the properties will then be SumAllVotes. (And also CountAllVotes, when I have finished this problem).
By the end I will have a SumAllVotes and CountAllVotes and can calculate the percentage of how many have pressed "1".
Error message:
An unhandled exception occurred while processing the request.
InvalidOperationException: Lambda expression used inside Include is
not valid.
Microsoft.EntityFrameworkCore.Query.Internal.NavigationExpandingExpressionVisitor.ProcessInclude(NavigationExpansionExpression
source, Expression expression, bool thenInclude)
What I have tried:
[HttpGet]
public async Task<ActionResult<IEnumerable<Books>>> GetBooks()
{
Guid userid = Guid.Parse(this.User.FindFirst(ClaimTypes.NameIdentifier).Value);
return await _context.Books
.Include(r => r.BookCategories)
.Include(r => r.Resources)
.Include(r => r.Ratings.GroupBy(g => g.Bookid).Select(s => new { SumAllVotes = s.Sum(item => item.Rating) }))
.ToListAsync();
}
Books and Ratings are defined as -
public partial class Books
{
public Books()
{
Bookmarks = new HashSet<Bookmarks>();
Comments = new HashSet<Comments>();
Favourites = new HashSet<Favourites>();
BookCategories = new HashSet<BookCategories>();
Resources = new HashSet<Resources>();
Ratings = new HashSet<Ratings>();
}
public int Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public byte Scaleoffun { get; set; }
public byte Scaleoflearning { get; set; }
public int? Goal { get; set; }
public int? Secondgoal { get; set; }
public int? Thirdgoal { get; set; }
public int? Subjectid { get; set; }
public int? Categoryid { get; set; }
public string Language { get; set; }
public string Estimatedtime { get; set; }
public string Image { get; set; }
public int? File { get; set; }
public int? Ownerid { get; set; }
public DateTime Createdon { get; set; }
public DateTime? Lastmodifiedon { get; set; }
public string Active { get; set; }
public string Url { get; set; }
public Guid Userid { get; set; }
public byte? Grade { get; set; }
[NotMapped]
public int SumAllVotes { get; set; }
[NotMapped]
public int CountAllVotes { get; set; }
public virtual Categories Category { get; set; }
public virtual Curriculum GoalNavigation { get; set; }
public virtual Users Owner { get; set; }
public virtual Curriculum SecondgoalNavigation { get; set; }
public virtual Subjects Subject { get; set; }
public virtual Curriculum ThirdgoalNavigation { get; set; }
public virtual ICollection<Bookmarks> Bookmarks { get; set; }
public virtual ICollection<Comments> Comments { get; set; }
public virtual ICollection<Favourites> Favourites { get; set; }
public virtual ICollection<BookCategories> BookCategories { get; set; }
public virtual ICollection<Resources> Resources { get; set; }
public virtual ICollection<Ratings> Ratings { get; set; }
}
public partial class Ratings
{
public int Id { get; set; }
public int? Bookid { get; set; }
public string Type { get; set; }
public Int16? Rating { get; set; }
public Guid Userid { get; set; }
public string Subject { get; set; }
public DateTime Createdon { get; set; }
public DateTime? Modifiedon { get; set; }
public byte? Active { get; set; }
public virtual Books Book { get; set; }
//public virtual Users User { get; set; }
}
These are some other solutions I have tried, but got the same error message:
.Include(r=> r.Ratings.Sum(i=>i.Rating))
and
.Include(r => new { m = r.Ratings.GroupBy(g => g.Bookid) })
You don't need to group child entities by parent's Id. When you Include one-to-many child entities, they are added to their parent's child list, and hence grouped by their parent's identity, based on the relationship between them. All you need to do is tell EF what values you want from that child list.
Change your query to -
_context.Books
.Include(r => r.BookCategories)
.Include(r => r.Resources)
.Include(r => r.Ratings)
.Select(p => new
{
// set ALL the primitive properties from Books entity
Id = p.Id,
Title = p.Title,
// etc ...
// set the computed properties
CountAllVotes = p.Ratings.Count,
SumAllVotes = p.Ratings.Sum(x => x.Rating)
// set the related entities
BookCategories = p.BookCategories,
Resources = p.Resources
})
.ToListAsync();
AutoMapper has a ProjectTo method that generates the required query and does the projection (the Select part) automatically. You can use that to avoid the hassle of setting all those properties manually.
I suggest you don't use Include with Select. Read article how to make queries with Projection (Select). Note, that Rating.Rating is nullable and you need to handle this. Here is a possible code sample:
var view = await _context.Books
.Where(your condition)
.Select(item => new
{
//Todo: fill other props
SumAllVotes = item.Ratings.Sum(rating => (Int16?) rating.Rating),
CountAllVotes = item.Ratings.Count,
})
.ToListAsync()

Best way to create a map between two entities with a third one from another context

Hi I'd like to create a map between two entities (source: User, target: UserInfosDto) while one member of the target DTO (UserItemPreference) needs info from a third entity inside another context.
public class UserInfosDto
{
//public int UserId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public UserItemPreferencesDto UserItemPreferences { get; set; }
}
public class UserItemPreferencesDto
{
public bool SeeActuality { get; set; }
public bool IsInEditorMode { get; set; }
}
public class User
{
public string IdentityId { get; set; }
//...
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
}
public class UserIdentity
{
public string IdentityId { get; set; }
//...
public bool SeeActuality { get; set; }
public bool IsInEditorMode { get; set; }
}
User and UserIdentity come from different databases but have a common property IdentityId. I thought about using ITypeConverter in which I would inject the UserIdentity dbContext. Problem is that I can't find a way to use ITypeConverter on one member only.
Use an IValueResolver instead, which allows to resolve separate members instead of full types.
For your case above it will look like
public class UserItemPreferencesResolver
: IValueResolver<User, UserInfosDto, UserItemPreferencesDto>
{
private readonly UserEntityDbContext _dbContext;
public UserItemPreferencesResolver(UserEntityDbContext dbContext)
{
_dbContext = dbContext;
}
public UserItemPreferencesDto Resolve(
User source,
UserInfosDto destination,
UserItemPreferencesDto destinationMember,
ResolutionContext context
)
{
UserItemPreferencesDto preferences = /* resolve from _dbContext (and transform) */
return preferences;
}
}
Your create the mapping via
CreateMap<User, UserInfosDto>()
.ForMember(
dest => dest.UserItemPreferences,
opt => opt.MapFrom<UserItemPreferencesResolver>()
);

ServiceStack AutoQuery join use

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);

AutoMapper - Cannot map between IdentityUser subclass and its correspondant DTO

I'm working on a project with asp.net core and Identity,
I am trying to create a mapping configuration between IdentityUser subclasse and its correspondant DTO using Automapper
I have done similar configuration with other classes and it works fine, but with IdentityUser subclass it behaves differently :
Here is my IdentityUser subclasse :
public partial class Collaborateur : IdentityUser
{
public Collaborateur() : base()
{
this.Activites = new HashSet<ActiviteCollaborateur>();
this.ActeursAvantVente = new HashSet<ActeurAvv>();
}
public string Nom { get; set; }
public string Prenom { get; set; }
public string Telephone { get; set; }
public Nullable<long> Matricule { get; set; }
public string Structure { get; set; }
public string Login { get; set; }
public RoleEnum Role { get; set; }
public virtual ICollection<ActiviteCollaborateur> Activites { get; set; }
public virtual ICollection<ActeurAvv> ActeursAvantVente { get; set; }
public virtual Agence Agence { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime LastModified { get; set; }
}
Its corresponding DTO :
public class CollaborateurDTO : BaseDTO
{
public string Nom { get; set; }
public string Prenom { get; set; }
public string Telephone { get; set; }
public Nullable<long> Matricule { get; set; }
public string Structure { get; set; }
public string Login { get; set; }
public RoleEnum Role { get; set; }
}
CollaborateurProfile config class :
public class CollaborateurProfile : Profile
{
CollaborateurProfile()
{
CreateMap<Collaborateur, CollaborateurDTO>().ReverseMap();
CreateMap<Collaborateur, Collaborateur>()
.ForMember(x => x.Id, opt => opt.Ignore())
.ForMember(x => x.CreatedAt, opt => opt.Ignore())
.ForMember(x => x.LastModified, opts => opts.MapFrom(src => DateTime.UtcNow));
}
}
and Startup.cs :
services.AddAutoMapper();
it stops at this line with
MissingMethodException was unhandled by user code
An exception of type 'System.MissingMethodException' occurred in System.Private.CoreLib.ni.dll but was not handled in user code
By mistake i answered this question at the question linked in the comments (https://stackoverflow.com/a/46567611/7131186)
Here is my answer:
In my case (and it seems that this is your case too) it was a copy/paste problem. I somehow ended up with a PRIVATE constructor for my mapping profile:
using AutoMapper;
namespace Your.Namespace
{
public class MappingProfile : Profile
{
MappingProfile()
{
CreateMap<Animal, AnimalDto>();
}
}
}
(take note of the missing "public" in front of the ctor)
which compiled perfectly fine, but when AutoMapper tries to instantiate the profile it can't (of course!) find the constructor!

Foreign key relationship

I'm trying to setup a foreign key using the following two classes.
I want to use pAcqType as an enum and store the names of the types in another table. How should I setup my classes to do this?
public class Property
{
[Key]
public int pID { get; set; }
public string pAddress { get; set; }
public string pCounty { get; set; }
public string pCity { get; set; }
public string pState { get; set; }
public string pzip { get; set; }
public virtual PropertyAcquisitionType pAcqType { get; set; } <-- foreign key
}
public class PropertyAcquisitionType
{
[Key]
public int patID { get; set; }
public string patName { get; set; }
}
UPDATE
Dan got me thinking. And I tried the following and it seems to have worked out.
It setup the foreign key on the table like I wanted. And it didn't even ask for an inverse on the other table.
public int? pAcqType { get; set; }
[ForeignKey("pAcqType")]
public PropertyAcquisitionType patID { get; set; }
Is the foreign key required (NOT NULL in the database)?
public int pAcqTypeId { get; set; }
[ForeignKey("pAcqTypeId")]
public virtual PropertyAcquisitionType pAcqType { get; set; }
Otherwise,
public int? pAcqTypeId { get; set; }
[ForeignKey("pAcqTypeId")]
public virtual PropertyAcquisitionType pAcqType { get; set; }
Then in your other class, add an inverse relationship:
public class PropertyAcquisitionType
{
[Key]
public int patID { get; set; }
public string patName { get; set; }
[InverseProperty("pAcqType")]
public virtual ICollection<Property> pOfThisType { get; set; }
}
Here is one way you could define the relationship using the fluent API (without attributes in the entity classes). Note with this method, you should not need to add a properties property on the PropertyAcquisitionType entity to satisfy the inverse side of the relationship, because the .WithMany() tells EF what it needs to know:
public class MyDbContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Property>()
.HasKey(x => x.pID)
.HasRequired(x => x.pAcqType) // or HasOptional if using int?
.WithMany() // or WithMany(x => x.pOfThisType) if you want to add / keep the inverse property
.HasForeignKey(x => x.pAcqTypeId)
;
}
}

Resources