Relationships in MVC - c#

I'm trying to access a variable that i have done true a relationship in my model:
// Threads
public class Thread
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public int NumberOfMessages { get; set; }
public int ViewCount { get; set; }
public string[] Tags { get; set; }
public DateTime LastEdited { get; set; }
public int Vote { get; set; }
// Other model relations
public virtual ICollection<Message> Messages { get; set; }
public virtual Group Group { get; set; }
public virtual ForumUser User { get; set; }
}
I'm trying to add a message to the database and also reference it in the list of messages
thread.Group.Id = groupId;
It gave me a NULL reference exception. I do really now know how this works.
I'm going to show a other example that might explain a little bit more
` // Saving the message in the database
forumDb.Messages.Add(block);
forumDb.Threads.Find(threadId).Messages.Add(block);
forumDb.SaveChanges();
}`
It would be nice if some one example what I'm doing wrong the model for that last example is the same

From the information you gave, it looks like you didn't initialize the Group property of the Thread.
When creating an object, it's properties are not initialized. Since you try to access the id property of an uninitialized Group object, you get a null reference exception.
To solve your problem you could do this:
thread.Group = new Group(); // First initialize the group
thread.Group.Id = groupId; // Set the id of the newly created Group object.

Related

How to update child list with another object in Entity Framework Core

I need to update a child list from a parent adding records to it or updating one of its attributes. I receive the updated model from the Controller but when I try to replace the actual list with the new and save the changes to DB I get the error:
The instance of entity type 'WorkflowReferenciaExecucoes' cannot be tracked because another instance with the same key value for {'ReferenciaExecucoesId'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting key values.
I don't have access to the dbContext directly because we are using the repository pattern. What I have tried to update the child in the service is:
private void Update(Workflow entity)
{
// entity is my updated model received by controller
// Getting the actual parent in the database
var workflow = GetById(entity.WorkflowId);
workflow.NomeWorkflow = entity.NomeWorkflow;
workflow.DescricaoWorkflow = entity.DescricaoWorkflow;
workflow.FgAtivo = entity.FgAtivo;
// Updating child list
workflow.WorkflowReferenciaExecucoes = entity.WorkflowReferenciaExecucoes;
// Trying to save the update gives error
_uow.WorkflowRepository.Update(entity);
}
My parent class is:
public class Workflow
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int WorkflowId { get; set; }
public int ProjetoId { get; set; }
public int WorkflowTipoId { get; set; }
public string NomeWorkflow { get; set; }
public string DescricaoWorkflow { get; set; }
public DateTime DataInclusao { get; set; }
public bool FgAtivo { get; set; }
public Projeto Projeto { get; set; }
public WorkflowTipo WorkflowTipo { get; set; }
public IEnumerable<WorkflowReferenciaExecucao> WorkflowReferenciaExecucoes { get; set; }
public IEnumerable<WorkflowCondicaoExecucao> WorkflowCondicaoExecucoes { get; set; }
}
And child class:
public class WorkflowReferenciaExecucao
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int ReferenciaExecucaoId { get; set; }
public int WorkflowId { get; set; }
public int? ExecucaoWorkflowId { get; set; }
public int ValorReferenciaExecucao { get; set; }
public bool FgProcessar { get; set; }
public bool FgAtivo { get; set; }
}
What do I have to do to update the actual list to the new one?
Thank you!
Could it be that the passed in entity has duplicates in the WorkflowReferenciaExecucoes property - meaning the same WorkflowReferenciaExecucao exists twice in that IEnumerable?
you can not update like that you have wrong relationship you class should be like that
public class WorkflowReferenciaExecucao
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int ReferenciaExecucaoId { get; set; }
public Workflow Workflow { get; set; }
public int? ExecucaoWorkflowId { get; set; }
public int ValorReferenciaExecucao { get; set; }
public bool FgProcessar { get; set; }
public bool Fugitive { get; set; }
}
WorkflowReferenciaExecucao is one and it has only one workflow so when you update Workflow then you have to update only workflow id in WorkflowReferenciaExecucao don't pass whole object just pass id to change it one to many relationship so on one side you update anything it don't relate to many relationship because it only point to id that it
I can reproduce your problem when there are multiple child records with the same ReferenciaExecucoesId in the update entity.
You can check if this is the case.

EF Optional Relationships / Nullable / Navigation Table

I solved this, answer below.
I am new to EF and having a lot of difficulty trying to get an optional relationship. I am looking to have a relationship where I have ApiLogItem Model with an UserId property which can be null / anonymous user or a logged in user to track all Api calls. The goal is to have Existing Users who do some create a new object to be linked to that object. I do not want to create new Users every time a new ApiLogItem is created.
I have tried a dozen variations with virtual / foreign key attributes and I am stumped. It works great for null / anonymous user but once I attach an actual user to the ApiLogItem it will not insert. I get this error:
{"Violation of PRIMARY KEY constraint 'PK_AspNetUsers'. Cannot insert
duplicate key in object 'dbo.AspNetUsers'. The duplicate key value is
(09c0d2e2-b003-4be8-a62a-08d7268af58e).\r\nThe statement has been
terminated."}
I have tried following this tutorial but alas no luck.
https://www.learnentityframeworkcore.com/conventions/one-to-many-relationship#targetText=EF%20Core%20will%20create%20a,public%20class%20Author
public class ApiLogItem
{
[Key]
public long Id { get; set; }
[Required]
public int StatusCode { get; set; }
[Required]
public string Method { get; set; }
[MaxLength(45)]
public string IPAddress { get; set; }
public Guid? UserId { get; set; }
public ApplicationUser User { get; set; }
}
public class ApplicationUser : IdentityUser<Guid>
{
[MaxLength(64)]
public string FirstName { get; set; }
[MaxLength(64)]
public string LastName { get; set; }
public List<ApiLogItem> ApiLogItems { get; set; }
}
Error happens when I want to create a new ApiLogItem:
using (ApplicationDbContext _dbContext = new ApplicationDbContext(_optionsBuilder.Options))
{
_dbContext.ApiLogs.Add(apiLogItem);
await _dbContext.SaveChangesAsync();
}
I have reviewed several other stackoverflow issues and none seem to fix. You can find the repository here:
https://github.com/enkodellc/blazorboilerplate
You are calling applicationDbContextSeed.SeedDb(); in your Startup class each time you run, and in your SeedDb method, you are adding a user with a static id 09C0D2E2-B003-4BE8-A62A-08D7268AF58E.
The first time you run, it will create that user; the second time, it will fail because that user (with that id) already exists.
I figured it out. It needs a virtual in the parent and just the id in the child. I need to learn more about EF as it is not intuitive to me. Will post a better answer later today after testing.
public class ApplicationUser : IdentityUser<Guid>
{
[MaxLength(64)]
public string FirstName { get; set; }
[MaxLength(64)]
public string LastName { get; set; }
public ICollection<ApiLogItem> ApiLogItems { get; set; }
}
public class ApiLogItem
{
[Key]
public long Id { get; set; }
[Required]
public int StatusCode { get; set; }
[Required]
public string Method { get; set; }
[MaxLength(45)]
public string IPAddress { get; set; }
public Guid? UserId { get; set; }
}

How to map to destination with less properties than source in Automapper 6?

I am trying to write mapping configuration for next case. I have domain object:
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
public string ImagePath { get; set; }
public virtual ICollection<Service> Services { get; set; }
public int? SubcategoryId { get; set; }
[ForeignKey("SubcategoryId")]
public virtual Category Subcategory { get; set; }
}
And Dto to map:
public class CategoryDto
{
public int Id { get; set; }
public string Name { get; set; }
public string ImagePath { get; set; }
}
The problem is, target class have less properties, than source. If I use simple map, I get an exception.
Mapper.Initialize(n => n.CreateMap<Service, ServiceDto>());
I can't use Ignore(), because it will be applied to target class, not source one. Method ForSourceMember() also didn't help for some reason. I read this question, it's fine for most cases, but property Services is not null, it's Count = 0, when it's empty. I also read some similar questions from the right, but they didn't help, maybe they worked in previous versions.
Hope someone can help me to find solution, or explain what I missed.
Mapper.Initialize can only be called once, when your app initializes itself, not per request as you're doing now.

Entity framework add's new record in one-to-one relation while object has an ID

I'm new to c# and I have a basic problem with saving/updating data.
With two classes :
public class User
{
public int UserId { get; set; }
public string Email { get; set; }
public string Login { get; set; }
public string Password { get; set; }
public DateTime RegisteredDate { get; set; }
public class Task
{
public int TaskId { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string Comment { get; set; }
public User DeclaredBy { get; set; }
}
I'm trying to save a task object (new or updated) with DeclaredBy field set
dbContext.Tasks.AddOrUpdate(task);
dbContext.SaveChanges();
User set in DeclaredBy field has an ID, but after executing SaveChanges() a new record of User appears in DB.
This is how EF works, if you add entity with other related entity then both will be stored in DB. If you want to store only one then you need to set other navigation properties to null and only set foreign key id, which currently you don't have, you should add property:
public int DeclatedById { get; set; }
Btw. this is very common problem and a lot of projects has duplicate values in db.
Maybe I should add that this will happen only if entity wasn't traced by EF context and was attached to it.

While Including Primary Keys as a property of my classes in C# using ASP.NET MVC5

If I supply a primary key property in my classes does Entity Framework implicitly iterate the value of newly created objects or do I have to supply code to register this information? This may seem obvious to some, and to be fair I researched this question to no avail before asking it.
Reason I need to know:
I need to retrieve a SelectList of items from a class, of which is a property of another class. The classes share a composition relationship in other words.
// GET: Fora/Create
public ActionResult Create()
{
ViewBag.Categories = new SelectList(db.Fora.OrderBy(m => m.MessageItem.TopicItem.Category), "TopicID", "Category");
ViewBag.UserNames = new SelectList(db.Fora.OrderBy(m => m.MessageItem.From.UserName), "MemberID", "UserName");
return View();
}
Classes:
public class ForumViewModel
{
public int MessageID { get; set; }
public Member User{ get; set; }
public Topic Category { get; set; }
public Message Message { get; set; }
}
public class Message
{
private List<Topic> categories = new List<Topic>();
public virtual int MessageID { get; set; }
public virtual int MemberID { get; set; }
public virtual int ForumID { get; set; }
public virtual int TopicID { get; set; }
public virtual string Subject { get; set; }
public virtual string Body { get; set; }
public virtual DateTime Date { get; set; }
public virtual Member From { get; set; }
public virtual Topic TopicItem { get; set; }
public virtual List<Topic> Categories
{
get { return categories; }
set { categories = value; }
}
}
Before anyone rips me apart for the class structure, I would just like to say I am a college student still trying to learn this stuff and out of desperation to get the action create() method of my controller to work I have hacked at this class trying to force it to work, also to no avail...
Any help or clarification is appreciated.
If what you mean by "implicitly iterate the value of newly created objects" and "register this information" is actually adding the new objects to the database and getting EF updated about these, you need to take the steps to first create a new object, add it to the dbcontext and then save the changes. Assuming the new object is of type Message,
first create a new Message:
var newMessage = new Message{ ... };
then add this to your dbcontext:
db.Message.Add(newMessage);
(where Message is the dbContext class that represents your Message table)
and finally save your changes:
db.SaveChanges();
The new newMessage object will get a new id after saving your changes if your db is configured to auto-increment the primary key value of your Message table.
Hope this helps.

Categories