Loosing Foreign Key when querying In different Controllers/Methods - c#

I have a Model Rental in my ASP.Net 4.5 MVC Project. Rental has 2 foreign keys ProfileId(Profile Model) & ItemId(Item Model). Both are set properly when the Rental record is created.
In Controller #1 if i run:
var rentals = GetRentals(6);
public IQueryable<Rental> GetRentals(int parent)
{
return db.Rentals.Where(t => t.ProfileId.Equals(parent));
}
var rentalArray = rentals.ToArray();
rentalArray has the proper item id and FK index, allowing me to run rentalArray[i].Item.Name Good Response Pic from Code Above
Now In controller #2 if i run
var rentals = GetRentals(6);
public IQueryable<Rental> GetRentals(int parent)
{
return db.Rentals.Where(t => t.ProfileId.Equals(parent));
}
var rentalArray = rentals.ToArray();
In this query i loose the item FK property of the Rental, though the ItemId is still coming in properly. I am unable to run rentalArray[i].Item.Name as Item is null.
Both are the same database record, Im just querying in a different Controller/Method. Picture of bad response
I cannot determine why Rental.Item property comes in null on the second query? Database Diagram
Rental Model:
public class Rental
{
public int RentalId { get; set; }
public int ItemId { get; set; }
[IgnoreDataMember]
public virtual Item Item { get; set; }
public int ProfileId { get; set; }
[IgnoreDataMember]
public virtual Profile Profile { get; set; }
public DateTime RequestDate { get; set; }
public bool? Accepted { get; set; }
public bool? Complete { get; set; }
public DateTime BeginRental { get; set; }
public DateTime EndRental { get; set; }
public int ChargeAmount { get; set; }
public bool Canceled { get; set; }
public string ChargeId { get; set; }
}

You can use include method
using System.Data.Entity;
and you can use it like this:-
return db.Rentals.include(tt => tt.Profile).include(ttt => ttt.Item).Where(t => t.ProfileId.Equals(parent));

Thanks to bash.d for finding issue.
Have you noticed that in your bad picture you have
LibLob.Models.Profile instead of a reference to an System.Data.Entity
object? You said your DBContext was the same?
Controller #1 was using a ApplicationDBContext while controller#2 was using ProxylessApplicationDbContext, resulting in Rental.Item appearing but being null in controller #2. Fixed by adding a second dbcontext and using that only for method to retrieve rentals.

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.

'Invalid Column Name' Error After Upgrading from Asp.net core 2.0 to 2.1

I've revisited my web site recently and had to upgrade from ASP.net MVC (DBF) core 2.0 to 2.1.
Since doing so I'm getting the following error...
SqlException: Invalid column name 'MovieTitleId'. Invalid column name 'MovieTitleId'.
Yet there is no such field 'MovieTitleId' in any part of my code or db.
The error occurs only when the site is accessing the 'many table' Scenes
(there is a one-to-many relationship set up in the db with FKs.. Movie > Scenes)
This is the Scene class..
public partial class Scene
{
[Key]
public int SceneId { get; set; }
[ForeignKey("TitleId")]
public int? TitleId { get; set; } // foreign key from Movie
[ForeignKey("LocationSiteId")]
public int? LocationSiteId { get; set; }
[ForeignKey("LocationAliasId")]
public int? LocationAliasId { get; set; }
public string Notes { get; set; }
public int? SceneOrder { get; set; }
public string TitleList { get; set; }
public LocationAlias LocationAlias { get; set; }
public LocationSite LocationSite { get; set; }
public Movie Movie { get; set; }
}
And this is the Movie class which on the 'one side' and call Scenes on a typical 'Master/Detail' type web page...
public partial class Movie
{
public Movie()
{
Scenes = new HashSet<Scene>();
}
[Key]
public int TitleId { get; set; }
[Required]
public string Title { get; set; }
[DisplayName("Title")]
public string ParsedTitle { get; set; }
[DisplayName("Year")]
public int? TitleYear { get; set; }
public string ImdbUrl { get; set; }
public string Summary { get; set; }
public bool? ExcludeTitle { get; set; }
public bool? Widescreen { get; set; }
[DisplayName("Title")]
public override string ToString()
{
return Title + " (" + TitleYear + ")";
}
public ICollection<Scene> Scenes { get; set; }
}
The error occurs in the MoviesController.cs...
Movie movie = _context.Movies.Find(id);
ViewBag.Scenes = _context.Scenes
.Where(s => s.TitleId == id)
.Include(s => s.LocationSite)
.Include(s => s.LocationSite.LocationPlace)
.OrderBy(s => s.SceneOrder).ToList();
Everything used to work fine until i upgraded to core 2.1.
I can't even recall there ever being a field called 'MovietitleId' which is actually 'TitleId'.
Is the error msg concatenating the model 'Movie' and column 'TitleId' somehow?
Try adding virtual keyword for your foreign key. Also the ForeignKey Data Annotation should be on that property where you have declared your virtual property just like below. So it should be something like this:
Scene.cs
public int TitleId { get; set; }
[ForeignKey("TitleId")
public virtual Movie Movie { get; set; }
Why virtual?
If you declare your property virtual your virtual property (by default) won't be loaded right away when querying the main object. It will be retrieved from the database ONLY if you try to access it. This is called lazy loading.
If you want to know why to use virtual in detail, you may visit this link: Why are foreign keys in EF Code First marked as virtual?
Hope this helps.

How to add multiple entity with childs at once

I'm having trouble adding multiple entities with multiple children at once in foreach loop.
Its obvious ef cannot track ids in foreach loop. But maybe there are other solutions that you can guide me.
The error when I tried to add multiple entities with a child is:
The instance of entity type cannot be tracked because of another instance
with the same key value for {'Id'} is already being tracked. When
attaching existing entities, ensure that only one entity instance with
a given key value is attached.
For example:
public class Order
{
public Order()
{
OrderDetails = new HashSet<OrderDetail>();
}
[Key]
public int Id { get; set; }
[StringLength(50)]
public string Code { get; set; }
public int? CompanyId { get; set; }
public int? PartnerId { get; set; }
public decimal TotalNetPrice { get; set; }
public decimal TotalPrice { get; set; }
public bool IsActive { get; set; } = true;
public bool IsDeleted { get; set; } = false;
[ForeignKey("CompanyId")]
public virtual Company Company { get; set; }
[ForeignKey("PartnerId")]
public virtual Partner Partner { get; set; }
public virtual ICollection<OrderDetail> OrderDetails { get; set; }
}
public class OrderDetail
{
[Key]
public int Id { get; set; }
public int OrderId { get; set; }
[StringLength(50)]
public string Code { get; set; }
public int LineNumber { get; set; }
public int ProductId { get; set; }
public int Quantity { get; set; }
public bool IsActive { get; set; } = true;
public bool IsDeleted { get; set; } = false;
[ForeignKey("OrderId")]
public virtual Order Order { get; set; }
[ForeignKey("ProductId")]
public virtual Product Product { get; set; }
}
Here is my code in the method:
foreach (var order in orderList)
{
// consider we create/cast object to Order class.
_orderService.Add(order);
// in here we don't have id, because we didn't call savechanges.
foreach(var orderDetail in order.orderDetailList)
{
// consider we create/cast object to OrderDetail class.
orderDetail.orderId = order.Id;
// in here, order id is always 0 as expected.
_order.OrderDetails.Add(orderDetail);
}
}
try
{
await uow.SaveChangesAsync();
}
catch(Exception exception)
{
var msg = exception.Message;
}
I tried to use [DatabaseGenerated(DatabaseGeneratedOption.Identity)] attribute for the identity columns.
According to documentation (https://learn.microsoft.com/en-us/ef/core/modeling/generated-properties?tabs=data-annotations):
Depending on the database provider being used, values may be generated
client side by EF or in the database. If the value is generated by the
database, then EF may assign a temporary value when you add the entity
to the context. This temporary value will then be replaced by the
database generated value during SaveChanges().
So it should give at least temp id to track it. But it didn't work with my case.
I also tried the same approach on model creating a part in context. But again the same result. No success.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Entity>()
.Property(x => x.Id)
.ValueGeneratedOnAdd();
}
It seems like the best solution is to make a transaction and save order before details and get real id and then add details. But it has performance issues as you know.
I'm wondering if there is any other best practice for that issue?
Thank you.
Try this:
foreach (var order in orderList)
{
_orderService.Add(order);
foreach(var orderDetail in order.orderDetailList)
{
// Add reference Of Order to OrderDetails, not an id
orderDetail.Order = order;
_order.OrderDetails.Add(orderDetail);
}
}
In this case EF will know how to connect Order and OrderDetail on SaveChangesAsync

Entity Framework .Include throwing NullReferenceException

I have a very basic EF setup that is throwing an odd error when trying to populate a navigation property by using .Include. Here are the entity Models:
public class LineGroup
{
public int ID { get; set; }
public string Name { get; set; }
public bool IsActive { get; set; }
public ICollection<LineGroupMember> LineGroupMembers { get; set; }
}
public class LineGroupMember
{
public int ID { get; set; }
public int Extension { get; set; }
public string Name { get; set; }
public int Permissions { get; set; }
public bool IsLoggedIn { get; set; }
public int LineGroupID { get; set; }
internal LineGroup LineGroup { get; set; }
}
I am using these through an injected DB context, and can query each just fine without using navigation properties. I can also query the LineGroups and include the LineGroupMembers property just fine, like so:
var LineGroups = _context.LineGroups.Include(l => l.LineGroupMembers).ToList();
This load all of the line groups into a list that has a correctly working "LineGroupMembers" collection for each Line Group. However, if I try
var lineGroupMembers = _context.LineGroupMembers.Include(m => m.LineGroup).ToList();
I get "NullReferenceException" with no helpful details. Any ideas why the navigation property will work one way and not the other? There are no null values in either database table...
Make your navigation property public
public LineGroup LineGroup { get; set; }
If it is internal it won't be picked up by default by EF. You could also add explicit fluent mapping to force EF to recognize it as well.

EF6.1 Code First Mapping or Annotations for Tables

I have the following two models that I'm trying to setup, but cannot figure out how to do it using data annotations or fluent API. Can anyone advise either the proper annotations or Fluent API code?
public class Vehicle
{
public int ID { get; set; }
public int JobID { get; set; }
public int StatusID { get; set; }
public string Name { get; set; }
public virtual Job Job { get; set }
public virtual Status Status { get; set; }
}
public class Job
{
public int ID { get; set; }
public int VehicleID { get; set; }
public int StatusID { get; set; }
public string Description { get; set; }
public virtual Vehicle Vehicle{ get; set; }
public virtual Status Status { get; set; }
}
The problem I'm having is the reference to the Job model from Vehicle, and from Vehicle to Job. In the database, the Jobs tables holds all jobs pending or completed. There may or may not be a vehicle associated with it (when a job is in progress or complete, a vehicle will be associated with it). For the vehicles table, the JobID represents the current job assigned to the vehicle (if it's assigned a job) and will constantly change through the day, but should have no effect on the Jobs table.
The following snippet outlines a fluent code-first example of the relationship you describe:
public class Vehicle
{
public int ID { get; set; }
public string Name { get; set; }
public virtual Job Job { get; set; }
}
public class Job
{
public int ID { get; set; }
public string Description { get; set; }
}
public class AppDataContext : DbContext
{
public DbSet<Vehicle> Vehicles { get; set; }
public DbSet<Job> Jobs { get; set; }
protected override void OnModelCreating(DbModelBuilder mb)
{
mb.Entity<Job>().HasKey(x => x.ID);
mb.Entity<Vehicle>().HasKey(x => x.ID);
mb.Entity<Vehicle>().HasOptional(x => x.Job).WithOptionalDependent();
// ... other config, constraints, etc
}
}
And to get Jobs associated with vehicles:
using (var context = new AppDataContext())
{
var query = context.Vehicles.Where(x => x.Job != null).Select(x => x.Job);
// ...
}
IMHO, the explicit foreign key properties should be kept out of the model unless strictly needed. This tends to make life easier and keeps the code clean.
Hope this is along the lines of what you're looking for...
P.S. - At the risk of 'just posting links' - If you already have a database (as implied in your question), it might be worth considering a Database First approach: http://msdn.microsoft.com/en-gb/data/jj206878.aspx ..or Code First with an existing database: http://msdn.microsoft.com/en-us/data/jj200620

Categories