ASP.Net Core Linking Identity User To Model - c#

I am using ASP.NET Core 6 to make a simple Blog website.
I have the 2 following classes:
AppUser.cs
public class AppUser : IdentityUser
{
public ICollection<Blog>? Blogs { get; set; }
public string? Name { get; set; }
}
Blog.cs
public class Blog
{
public int ID { get; set; }
public string Title { get; set; }
public string UserID { get; set; }
}
}
Below is suppose to get the current users info when creating a blog:
public async Task<IActionResult> OnPostAsync()
{
var user = await _userManager.GetUserAsync(User);
Blog.UserID = user.Id;
if (!ModelState.IsValid)
{
return Page();
}
_context.Blog.Add(Blog);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
For some reason, the ModelState is not valid when attempting to create the new Blog.
When I print to console the Blog.UserID and Blog.Title I get the correct data, but it still does not work.
Potentially unrelated, but the table Entity Framework made for the Blog is:
Which I also don't understand why there is a UserID and AppUserId column.
Any help would be greatly appreciated!
Update
I seem to have fixed it by making the UserID a nullable field.. I'm not sure if that is ideal...

First, for your ModelState problem. I recommend you to check this ModelState.IsValid.Question
And another problem is "Which I also don't understand why there is a UserID and AppUserId column."
I think you would like to use Navigation Property.
So you should change your entities like;
public class AppUser : IdentityUser
{
public string Name { get; set; }
public ICollection<Blog> Blogs { get; set; }
}
public class Blog
{
public int Id { get; set; }
public string Title { get; set; }
public string AppUserId { get; set; }
public AppUser AppUser {get;set;}
}

Related

Error in getting navigation property in a many to many relationship in entity framework core

I have two entities that are connected in a many to many relationship as follows
public class Fee
{
public int Id { get; set; }
[Required]
public string Label { get; set; }
public string Description { get; set; }
----Ignored for brevity----
public virtual ICollection<ClassFee> ClassFees { get; set; }
}
public class StudentClass
{
public int Id { get; set; }
public string Label { get; set; }
---Ignored for brevity---
public virtual ICollection<ClassFee> ClassFees { get; set; }
}
public class ClassFee
{
public int Id { get; set; }
[Display(Name ="Fee")]
public int FeeId { get; set; }
[Display(Name ="Class")]
public int ClassId { get; set; }
---ignored for brevity---
[ForeignKey("FeeId")]
public Fee Fee { get; set; }
[ForeignKey("ClassId")]
public StudentClass Class { get; set; }
}
The following is my FeesController class where I intend to get the details of a given fee and the list of classes the fee is applied to
public async Task<IActionResult> Details(int? id)
{
if (id == null)
{
return NotFound();
}
var fee = await _context.Fees
.Include(cf => cf.ClassFees)
.FirstOrDefaultAsync(m => m.Id == id);
if (fee == null)
{
return NotFound();
}
return View(fee);
}
I was expecting that with this, I should be able to have the class for the fees in the view by calling classFeeEntityInstance.class.label
to get the label for the class but it returns null. Also when I put a breakpoint on the method and run the code, the classfee.class is returned as null
I have also tried to do the following to see if I could eagerly load the class in the query but it does not seen to be possible to find the class from the classfee with the ThenInclude caluse as follows
.Include(cf => cf.ClassFees).ThenInclude(f=>f.Class)
but the f.Class does not exist at that point because the intellisence in Visual Studio does not suggest it and it underlines it immediately I try to add it.
Below is how I wish to use the class under the classFees in my view
#foreach (ClassFee classFee in Model.ClassFees)
{
#classFee.Class.Label
}
But the Class.Label throws up a null reference exception when the code is run
The application is being built on ASP.NET-Core 3.1 with Entity Framework 3.1
I will appreciate any guide to resolve this
Thank you
Go ahead and try to build it using ThenInclude, it should work. There is a known issue with Intellisense and ThenInclude.

JsonException: Cycle detected when using PostMan

When I post my object
{
"Title": "LookingForGroup",
"Description": "Descrptjasag",
"CreatorName":"thelo#mail.bg",
"Price":"4"
}
in postman , I get a json exception that says :
System.Text.Json.JsonException: A possible object cycle was detected
which is not supported. This can either be due to a cycle or if the
object depth is larger than the maximum allowed depth of 32.
My Post Class
public class Post
{
public string Id { get; set; }
public string Title { get; set; }
public ApplicationUser Creator { get; set; }
public string CreatorId { get; set; }
public string Description { get; set; }
public PostType PostType { get; set; }
public decimal Price { get; set; }
public ICollection<Bid> Bids { get; set; }
}
My Model
public class PostInputModel
{
public string Title { get; set; }
public string Description { get; set; }
public string Price { get; set; }
public string CreatorName { get; set; }
}
My Controller
[HttpPost]
public async Task<ActionResult<PostInputModel>> PostPost(PostInputModel input)
{
Post post = new Post()
{
Id = Guid.NewGuid().ToString(),
Title = input.Title,
Creator = _context.Users.Where(x => x.UserName == input.CreatorName).FirstOrDefault(),
Description = input.Description,
PostType = PostType.Help,
Price = 4
};
_context.Posts.Add(post);
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateException)
{
if (PostExists(post.Id))
{
return Conflict();
}
else
{
throw;
}
}
return CreatedAtAction("GetPost", post);
}
My User Class
public class ApplicationUser : IdentityUser
{
public ICollection<Bid> Bids { get; set; }
public ICollection<Post> FreelanceService { get; set; }
}
As I mentioned in comment I am assume your JsonSerializer goes into infinite loops, because of reference between user and posts. Every user contains posts and every post contains user. You can easily check that by using QuickWatch in debug mode.
You will get something like that
User
Post1
Post2
User
Post1
Post2
..
Generally it's not a good practice to return your EntityFramework models as result object. You should create DTO objects that will be returned instead of EF objects:
https://learn.microsoft.com/en-us/aspnet/web-api/overview/data/using-web-api-with-entity-framework/part-5
Your DTO objects shouldn't contains references that leads to this infinite loop.
Other solution that I personally would not use is to inform serializer to dont serialize specific properties:
public class ApplicationUser : IdentityUser
{
[JsonIgnore]
public ICollection<Bid> Bids { get; set; }
[JsonIgnore]
public ICollection<Post> FreelanceService { get; set; }
}
If JsonIgnore attribute will not help, please try to use [IgnoreDataMember].
You have circular reference in your model: Post has an ApplicationUser, that has a collection of Post's. That's your problem! Use foreign keys so you can relate posts with users, without creating circular references. Create and ApplicationUserId, replace in Post model, and use it as a key. 1
JsonSerializer in .NET Core 3.0 does not support circular references. A proposal to support this is being worked on in #41002.
If you believe this is not a cycle and instead you just have a very deep heirarchy, you can set JsonSerializerOptions.MaxDepth to something larger than the default.

Why doesn't EF Core automatically generate id field?

I'm new to programming and development and I'm learning, and this is one of my learning projects. I've been trying to get around this in various ways, but when I try to add a new Vehicle Model to a specific Vehicle Make, the Id column doesn't automatically increment, but tries to overwrite the first Id.
I tried working around Data annotations, which I think are correct, I tried manually adding values to the database via queries, and it works perfectly. Tried deleting the db and migrations, changing the annotations again and nothing works. The only thing I can be doing wrong is the code itself, probably somewhere in the controller or service layer.
VehicleMake Class:
public class VehicleMake
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
[Display(Name = "Make Name")]
public string Name { get; set; }
[Required]
[Display(Name = "Abbreviation")]
public string Abrv { get; set; }
[Display(Name = "Models")]
public virtual IEnumerable<VehicleModel> VehicleModels { get; set; }
}
VehicleModel Class:
public class VehicleModel
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
public int MakeId { get; set; }
public virtual VehicleMake Make { get; set; }
[Required]
[Display(Name = "Model Name")]
public string Name { get; set; }
[Required]
[Display(Name="Abbreviation")]
public string Abrv { get; set; }
}
Controller for Vehicle Model:
[HttpPost]
public IActionResult Create(int Id, VehicleModel newModel)
{
if (ModelState.IsValid)
{
newModel.MakeId = Id;
_model.Add(newModel);
return RedirectToAction("Index");
}
return View(newModel);
}
Service for adding new model:
public void Add(VehicleModel newModel)
{
_context.Add(newModel);
_context.SaveChanges();
}
Here is the value it is trying to add to the db and of course gives an error
https://imgur.com/pL9EruF
What am I doing wrong?
Why are you passing an id to a create action in the first place? You should just have:
[HttpPost]
public async Task<IActionResult> Create(VehicleModel newModel)
{
if (!ModelState.IsValid)
return View(newModel);
_context.Add(newModel);
await _context.SaveChangesAsync();
return RedirectToAction("Index");
}
Note:
I'm showing the minimal code here. If you want to factor out the call to Add and SaveChangesAsync to a service, that's fine.
Use the async methods when working with EF Core. ASP.NET Core and EF Core are both async all the way. The sync methods only exist for serving rare edge-case scenarios where you can't use async for some reason, and all they do is block on the async methods.

Changing Users Context

I'm using the MVC4 Internet Application and I am trying to change the account model to use my context. I cannot seem to get it to work. I tried to delete the context and include the user profile in my context and I could log on etc but when I check for profiles by user id it returns a null value.
public DbSet<FightCard> FightCards { get; set; }
public DbSet<Fight> Fights { get; set; }
public DbSet<Fighter> Fighters { get; set; }
public DbSet<UserProfile> UserProfiles { get; set; }
[Table("UserProfile")]
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
}
When I try to do this:
public ActionResult Profile(int id)
{
UserProfile profile = uc.UserProfiles.Find(id);
ViewBag.Name = profile.UserName;
}
I get a null value return. Anyone know how to successfully use the simple membership with a different context?
Solved it there. But to be honest I'm not exactly sure what fixed it. Here is the link I used and one part of it solved my problem.
http://dansgreenshoes.com/2013/03/10/mvc4usertable/

Entity Framework 4.1 RC (Code First) - Entity not updating over association

What I'm trying to do is fairly simple. I have two classes:
public class TownRecord
{
public int Id { get; set; }
public string ShortName { get; set; }
public string FileName { get; set; }
public string tags { get; set; }
public virtual TownRecordType RecordType { get; set; }
public DateTime? DateScanned { get; set; }
public DateTime? RecordDate { get; set; }
[StringLength(4000)]
public string Comments { get; set; }
public string UploadedBy { get; set; }
}
public class TownRecordType
{
public int Id { get; set; }
public string RecordType { get; set; }
public virtual ICollection<TownRecord> TownRecords {get; set; }
}
When I want to update the RecordType property on the TownRecord class, I find that the association fails to update. No exception is thrown but the update is not performed:
[HttpPost]
public ActionResult Edit(int id, TownRecord tr, FormCollection collection)
{
TownRecordType newRecType = _ctx.TownRecordTypes.Find(Int32.Parse(collection["RecordType"]));
tr.RecordType = newRecType;
_ctx.Entry(tr).State = EntityState.Modified;
_ctx.SaveChanges();
return RedirectToAction("List");
}
NOTE: I removed my error handling for clarity...
I've seen a question similar to this here but I'm not getting it. This is probably a really foolish rookie mistake but I've StackOverflowing and Googling for several hours and getting nowhere. Any help is greatly appreciated.
This doesn't work because you are using independent association. Relation between TownRecord and TownRecordType is not part of town record's entry so changing state to modified doesn't say anything about state of relation. That is the real meaning of "independent" - it has its own entry but for unknown reason it is hard to get it in DbContext API (EF 4.1). Proposed way is using Foreign key association instead of independent association. To change your association to foreign key you must do this:
public class TownRecord
{
public int Id { get; set; }
...
[ForeignKey("RecordType")]
public int RecordTypeId { get; set; }
public virtual TownRecordType RecordType { get; set; }
...
}
You will change your code to:
[HttpPost]
public ActionResult Edit(int id, TownRecord tr, FormCollection collection)
{
tr.RecordTypeId = Int32.Parse(collection["RecordType"]);
_ctx.TownRecords.Attach(tr);
_ctx.Entry(tr).State = EntityState.Modified;
_ctx.SaveChanges();
return RedirectToAction("List");
}
Actually the question with the same problem was asked 2 hours before you asked the question. I also tried to provide solution which works with independent association but I don't like it. The problem is that for independent association you need to have attached TownRecord loaded its actual TownRecordType and replace it with new TownRecordType.

Categories

Resources