.Net Core Foreign Key Field always null - .net-core

We have a .net core api project. Foreign Keyed model always returning null from select queries.
DBContext is initialized with UseLazyLoadingProxies option.
Foreign key relation is defined in the table ContentTopic.
Foreign key is defined as ContentTopic->TopicId = Topic->Id
In the sample below Topic always return null.
services.AddDbContext<VaultContext>(options =>options.UseLazyLoadingProxies().UseSqlServer(Configuration.GetConnectionString("DBContext")));
[Table("ContentTopic")]
public class ContentTopic
{
[Key]
public long Id { get; set; }
public long TopicId { get; set; }
public long ContentId { get; set; }
public DateTime CreateDate { get; set; }
public bool IsInBody { get; set; }
[ForeignKey("TopicId")]
public virtual Topic Topic { get; set; }
}

UseLazyLoadingProxies extension must be called from DBContext in OnConfiguring method not from Startup.cs

Related

Foreign key created in shadow and appending random 1 to column name ASP:NET EF Core

When I migrate my new models and data I get the following error for multiple foreign keys:
The foreign key property 'FavoriteList.SongId1' was created in shadow
state because a conflicting property with the simple name 'SongId'
exists in the entity type, but is either not mapped, is already used
for another relationship, or is incompatible with the associated
primary key type. See https://aka.ms/efcore-relationships for
information on mapping relationships in EF Core.
Example of my models:
public class FavoriteList
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public int MovieId { get; set; }
[ForeignKey("MovieId")]
public int SongId { get; set; }
[ForeignKey("SongId")]
public int CustomerId { get; set; }
[ForeignKey("CustomerId")]
public virtual Movie Movie { get; set; }
public virtual Song Song { get; set; }
public virtual Customer Customer { get; set; }
}

EF Core InMemory using SQLite DB - Foreign Key on delete behavior set null Issue

My .NET Core 3.1 project uses both a primary DBContext with SQL Server and also I have a secondary DBContext that is using an in-memory SQLite DB. The reason for in-memory DB is providing faster read access. My in-memory DB is setup so when ever a CRUD operation happens in SQL, the in-memory DB is updated as well.
Issue is I cannot get the ".OnDelete(DeleteBehavior.SetNull)" working on the in-memory SQLite DB. Both DBContexts and their associated Fluent API are both setup the same. I went for SQLite in-memory option on the basis that it should provide a relational DB i.e cascade delete and set null features.
In this example, I have two tables: InMemTcpServer & InMemStrings
Each InMemStrings entity has a foreign key reference to an InMemTcpServer enity (one to many relationship) therefore when an InMemTcpServer is deleted, I want the foreign key field in the InMemStrings to be set to null.
When testing this all using the DBContext from my SQL Server, this all works OK, there's something not right in the in-memory environment.
InMemStrings Model:
public class InMemStrings
{
public int Id { get; set; } // Primary Key
public string Name { get; set; }
public string ConnectorType { get; set; }
public int? TcpServerId { get; set; }
public InMemTcpServer InMemTcpServer { get; set; }
}
InTcpServer Model:
public class InMemTcpServer
{
public int Id { get; set; } // Primary Key
public string Name { get; set; }
public string DataFlow { get; set; }
public string Encoding { get; set; }
public string IpAddress { get; set; }
public int PortNumber { get; set; }
public string Location { get; set; }
public bool AutoStart { get; set; }
public ICollection<InMemStrings> InMemStrings { get; set; }
}
InMemoryDbContext:
public class InMemDbAContext : DbContext
{
public InMemDbAContext()
{
}
public InMemDbAContext(DbContextOptions<InMemDbAContext> options) : base(options)
{
}
public DbSet<InMemTcpServer> InMemTcpServers { get; set; }
public DbSet<InMemImage> InMemImages { get; set; }
public DbSet<InMemSound> InMemSounds { get; set; }
public DbSet<InMemSpeech> InMemSpeech { get; set; }
public DbSet<InMemStrings> InMemStrings { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// If a tcpserver is deleted, set foreign key field in related Strings DB Table to null
modelBuilder.Entity<InMemStrings>()
.HasOne(p => p.InMemTcpServer)
.WithMany(b => b.InMemStrings)
.OnDelete(DeleteBehavior.SetNull);
}
}
Method that deletes the InMemTcpServer:
var inMemTcpServer = _inMemDbAContext.InMemTcpServers.Where(i => i.Id == job.Id).FirstOrDefault();
if (inMemTcpServer != null)
{
_inMemDbAContext.InMemTcpServers.Remove(inMemTcpServer);
_inMemDbAContext.SaveChanges();
}
Once the above InMemTcpServer has been deleted, I would expect its corresponding foreign key in the other table to be set to null, just like it does in the native DBContext that is using SQL Server.
From what i've read so far, it does appear that using SQLite for an in-memory should support the use of relational tables in EF Core?

ef core one-to-one property is always null

I'm trying to define (using data annotations, that's the project pattern, not using the fluent api) a one-to-one relationship but the navigation property (public virtual HolidayType HolidayType) is always null. The db table is creating the ForeignKey correctly using the annotation [ForeignKey("HolidayTypeId")].
Holiday Model
[Table("Holidays", Schema = "PTO")]
public class Holiday : EntityBase
{
...
public long HolidayTypeId { get; set; }
[ForeignKey("HolidayTypeId")]
public virtual HolidayType HolidayType { get; set; }
...
}
db FK constraint
ALTER TABLE [PTO].[Holidays] WITH CHECK ADD CONSTRAINT [FK_Holidays_HolidayTypes_HolidayTypeId] FOREIGN KEY([HolidayTypeId])
REFERENCES [PTO].[HolidayTypes] ([Id])
ON DELETE CASCADE
GO
Holiday Types Table
[Table("HolidayTypes", Schema = "PTO")]
public class HolidayType : EntityBase
{
[Required]
[MaxLength(200)]
public string Description { get; set; }
public bool IsActive { get; set; }
}
EntityBase
public class EntityBase
{
[Key]
public long Id { get; set; }
[Required]
[MaxLength(200)]
public string Name { get; set; }
public Guid CreatedByGuidId { get; set; }
public DateTime DateCreated { get; set; }
public Guid UpdatedByGuidId { get; set; }
public DateTime? DateUpdated { get; set; }
[Timestamp]
public byte[] RowVersion { get; set; }
}
Here's the repository call, the holidays are retrieved but they are missing the HolidayType property - always null. My understanding, using the ForeignKey annotation, the query does not need to have an .include statement. My understanding is obviously flawed so my next step is to try the include statement to verify. I'm hoping that an include statement doesn't require an addition db call if that is the answer to my issue.
public async Task<IEnumerable<T>> ListAsync(Expression<Func<T, bool>> predicate)
{
return await ApplicationDbContext.Set<T>().Where(predicate).ToListAsync();
}
Looks like the .Include statement is needed but it breaks my repository implementation so I need to rethink that.
This now hydrates the HolidayType:
public async Task<List<Holiday>> GetHolidaysByEmployerId(long employerId, int? year = null)
{
Expression<Func<Holiday, bool>> predicate = holiday => holiday.EmployerId == employerId;
var result = await ApplicationDbContext.Set<Holiday>().Where(predicate)
.Include(holiday => holiday.HolidayType)
.ToListAsync();
return result.ToList();
}

EF-Code First navigation property foreign key in complex type

I have complex type for Audit fields
My complex type:
[ComplexType]
public class AuditData {
[Column("CreatorUserId")]
public int? CreatorUserId { get; set; }
public DateTime? CreationTime { get; set; }
[Column("ModifierUserId")]
public int? ModifierUserId { get; set; }
public DateTime? ModificationTime { get; set; }
}
My base Entity (all other inherti this one) has AuditData property:
public abstract class Entity : IEntity, IAuditedEntity, INotifiedDbContextBeforeSave
{
// Summary:
// Unique identifier for this entity.
public int Id { get; set; }
public bool IsActive { get; set; }
public int Old_Id { get; set; }
public string Old_TableName { get; set; }
[Timestamp]
public byte[] RowVersion { get; set; }
public AuditData AuditData { get; set; }
// can this 2 lines below work as navigation property with foreign key in complex type
public virtual User CreatorUser { get; set; }
public virtual User ModifierUser { get; set; }
//... other fields
}
I have 2 navigation properties CreatorUser and ModifierUser.
I know you cant have navigation property in ComplexType but can my navigation property on entity be mapped with foreign key in complexType
something like:
[ForeignKey("CreatorUserId")] // --> should point to AuditData.CreatorUserId
public virtual User CreatorUser { get; set; }
becouse CreatorUserId will be property in every entity but EF is not aware of it.
Mybe there is solution in fluent API ?
The official documentation says:
Complex types are non-scalar properties of entity types that enable scalar properties to be organized within entities. Like entities, complex types consist of scalar properties or other complex type properties. Because complex types do not have keys, complex type objects cannot be managed by the Entity Framework apart from the parent object.
It follows that that complex types can not participate in any relations among entities, so they can't contain foreign keys

Exclude underlying objects when storing data using EF6

I have a class Ticket which has some properties. Three of these (View, Task and Key) properties are navigation properties. Those properties already exist in database even before a ticket has been stored. In my application I load those properties from the database first and then create a Ticket object. I need to save only the ticket (not the underlying objects ) to the database with the id to Key, View and Task (these are primery keys in the Ticket table)
[Table("Tickets")]
public class Ticket
{
[Key]
public int Id { get; set; }
public DateTime? Created { get; set; }
[Required]
public View View{ get; set; }
[Required]
public Key Key { get; set; }
public Task Task { get; set; }
}
I try to save the Ticket object like this:
db.Tickets.Add(ticket);
db.Entry(ticket.Key).State = System.Data.Entity.EntityState.Unchanged;
db.Entry(ticket.View).State = System.Data.Entity.EntityState.Unchanged;
db.Entry(ticket.Task).State = System.Data.Entity.EntityState.Unchanged;
db.SaveChanges();
When I try this approach I get the error:
{"Message":"An error has occurred.","ExceptionMessage":"Saving or accepting changes failed because more than one entity of type 'Key' have the same primary key value. Ensure that explicitly set primary key values are unique. Ensure that database-generated primary keys are configured correctly in the database and in the Entity Framework model. Use the Entity Designer for Database First/Model First configuration. Use the 'HasDatabaseGeneratedOption\" fluent API or 'DatabaseGeneratedAttribute' for Code First configuration.","
Is it even possible to work with Entity Framework this way? Having pre defined data which is loaded to it's objects (Key, View, Task) first and later assign these objects to an object having these properties but then in the entity framework context only adding the parent object, in this case the ticket?
I have also tried to set the underlying objects to null but then I will loose the data for those underlying objects, data I need later on in the application.
This is how the underlying objects look like:
[Table("Views")]
public class View
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Version { get; set; }
[Required]
public DateTime? Created { get; set; }
}
[Table("Keys")]
public class Key
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Version { get; set; }
[Required]
public DateTime? Created { get; set; }
}
[Table("Tasks")]
public class Task
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Version { get; set; }
[Required]
public DateTime? Created { get; set; }
}
Try adding Foreign Keys to your object and making those required instead of making the navigation property required. Like so:
[Table("Tickets")]
public class Ticket
{
[Key]
public int Id { get; set; }
public DateTime? Created { get; set; }
[Required]
public int ViewId {get; set; }
[ForeignKey("ViewId")]
public View View{ get; set; }
[Required]
public int KeyId {get; set; }
[ForeignKey("KeyId")]
public Key Key { get; set; }
public Task Task { get; set; }
}

Resources