Entity Framework : Handling childern with DBSet<Parent> - entity-framework

I have a Parent class that has a Child, I am using TPH, and exposing only DBSet<Parent> in the DBContext . Now I would like to use the Where(x = x.PropertyInChild) on the DBSet<Parent> where x is a Child, casting x during the process reveals the following error.
LINQ to Entities only supports casting EDM primitive or enumeration
types.

You have to implicitly include the derived entities in the OnModelCreating method.
public class SampleContext : DbContext
{
public DbSet<Parent> Parents { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Child>();
}
}
To query child entities you need to use OfType< TEntity > method.
var parents = context.Parents.OfType<Child>().ToList();

Related

EF Core Fluent API, set IsRequired on all entities to generate a non-null db column

I'm working on a Razor pages web app which works directly with a db context...yes this is not ideal but is what I'm stuck with for the time being.
In the data model, each object inherits from a base entity class containing audit data, e.g.:
public class BaseEntity
{
[Key]
public int Id { get; set; }
public DateTime CreatedOn { get; set; }
public string CreatedBy { get; set; }
...etc.
public class Table1 : BaseEntity
{
public string TestItemName { get; set; }
}
In the database, I want CreatedBy to be required (not null), but I don't want to use the [Required] attribute since this will trigger the UI to validate the CreatedBy column. I don't want to expose this column in the UI and instead have service code which updates all of the audit properties based on Add/Insert.
What I'm looking for is a way via Fluent API which will give me the column type in the db that I need, e.g. NVARCHAR(MAX) NOT NULL.
I can accomplish this in the OnModelCreating method in the dbcontext:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Table1>()
.Property(o => o.CreatedBy)
.IsRequired();
However this would require me to create a similar entry for every table in the model.
Is there code I can use in OnModelCreating which could accomplish this for all entities? Something like this (this is just pseudo-code, but looking to give an idea):
var entityTypes = modelBuilder.Model.GetEntityTypes().Select(o => o.GetType()).ToList();
entityTypes.ForEach(e =>
{
e.Property("CreatedBy").IsRequired();
});
Implement your entity configurations in discrete classes that implement IEntityTypeConfiguration. Your implementations should inherit from a base implementation that configures BaseEntity and the Configure method should be virtual with overriding implementations calling the base class' method:
public abstract class BaseEntityConfiguration<TEntity>
: IEntityTypeConfiguration<TEntity>
where TEntity : BaseEntity
{
public virtual void Configure(EntityTypeBuilder<TEntity> builder)
{
builder.Property(be => be.CreatedBy)
.IsRequired();
// etc
}
}
public class SomeEntityConfiguration : BaseEntityConfiguration<SomeEntity>
{
public override void Configure(EntityTypeBuilder<SomeEntity> builder)
{
// call base class method to configure BaseEntity properties
base.Configure(builder);
// configure remaining SomeEntity-specific properties/etc
builder.TestItemName.IsRequired();
}
}
You'll need to inform the model builder to use your configuration classes. For example, if your config classes are in the same assembly as your DbContext:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfigurationsFromAssembly(
typeof(YourDbContext).Assembly);
}

Entity Types cannot share table | Not actually the same table though

I have two databases that so happen to have the same table name. Entity is giving me an InvalidOperationException stating "The entity types 'PmsUser' and 'AcctUser' cannot share table 'NGUSERS'...
I created these using a code first approach.
My models for both look something like
[Table("NGUSERS")]
public partial class AcctUser
{
...
and
[Table("NGUSERS")]
public partial class PmsUser
{
...
They each have their own respective connection string to differing databases. What do I need to do to get it to allow the same table name (modifying the Database isn't an option)?
Use two ModelBuilder instead of attributes. Then pass the created DbModel to the DbConnection. Or set the database name in the DbContext. See Entity Framework Connections and Models.
This base class helps to register the model builders with the DbContext:
public abstract class ModelBuilderBase {
protected readonly DbModelBuilder ModelBuilder;
protected ModelBuilderBase(DbModelBuilder modelBuilder) {
ModelBuilder = modelBuilder;
}
public abstract void BuildModel();
}
ModelBuilder implementation:
public class ModelBuilder1 : ModelBuilderBase {
public ModelBuilder1 (DbModelBuilder modelBuilder) : base(modelBuilder)
{ }
public override void BuildModel() {
ModelBuilder.Entity<AcctUser>().ToTable("NGUSERS");
}
}
DbContext:
public class DbContext1 : DbContext {
public DbContext1() : base("Database1") /* set DB name */ {
}
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
base.OnModelCreating(modelBuilder);
var modelBuilders = new List<ModelBuilderBase> {
new ModelBuilder1(modelBuilder)
};
modelBuilders.ToList().ForEach(x => x.BuildModel());
}
}
Create ModelBuilder2 and DbContext2 for the second DB/Table.

How to create Table per type inheritance in Entity Framework Core 2.0 code first?

Following code generates only single table "CertificateEvent".
How do I achieve TPT inheritance in EF Core 2.0?
public abstract class CertificateEvent {
public int CertificateEventId { get; set; }
}
public class Assignment : CertificateEvent {...}
public class Assessment : CertificateEvent {...}
public class MyDbContext : DbContext
{
public MyDbContext(DbContextOptions<MyDbContext> options) : base(options)
{
}
public DbSet<Assessment> AssessorAssessments { get; set; }
public DbSet<Assignment> AssessorAssignments { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<CertificateEvent>().ToTable(nameof(CertificateEvent));
modelBuilder.Entity<Assessment>().ToTable(nameof(Assessment));
modelBuilder.Entity<Assignment>().ToTable(nameof(Assignment));
}
}
class MyDesignTimeDbContextFactory : IDesignTimeDbContextFactory<MyDbContext>
{
public MyDbContext CreateDbContext(string[] args)
{
var builder = new DbContextOptionsBuilder<MyDbContext>();
builder.UseSqlServer("Server=(local);Database=Test;Trusted_Connection=True;MultipleActiveResultSets=true");
return new MyDbContext(builder.Options);
}
}
I've also tried dotnet ef migrations add Inheritance, but it did not created TPT inheritance in the database
TPT is not in EF Core (yet). See
The feeling from our team is that TPT is generally an anti-pattern and
results in significant performance issues later on. While enabling it
may make some folks "happier" to start with it ultimately just leads
to issues. We are willing to consider it though, so we're leaving this
open and will consider it based on the feedback we get.
https://github.com/aspnet/EntityFrameworkCore/issues/2266

How to use DI in DbSet

I want to implement business logic in DbSet derived classes. I like the idea of not having services and DAL abstractions and think this could be a good way. For this to work I need to inject objects into my DbSet but I don't know how. Here some sample code which does not work, because the EF Framework can't create an object of the DbSet. Maybe someone can point me in the right direction?
public class LongTermBookingDbSet : DbSet<LongTermBooking>
{
DbContext _dbContext { get; set; }
public LongTermBookingDbSet(DbContext dbContext )
{
this._dbContext = _bContext ;
}
public override LongTermBooking Add(LongTermBooking entity)
{
return this.Add(entity, false);
}
public LongTermBooking Add(LongTermBooking entity, bool SendMails)
{
var dbSet = base.Add(entity);
//do something with the _dbContext
return dbSet;
}
}
One of the options is to aggregate real DbSet, not derive it:
public class PersonSet : IDbSet<Person>
{
private readonly DbSet<Person> _dbSet;
public PersonSet(DbSet<Person> dbSet)
{
_dbSet = dbSet;
}
}
public class MyDbContext: DbContext
{
public PersonSet PersonSet {...}
}
Inherits from DbSet<T> with the purposes to add property

Entity State for entity framework with repositories

I am using Julie Lerman's EF Repository techniques.
All my entities implement the following interface
public interface IEntity
{
EntityState State { get; set; }
}
All my repositories call the following GetList function
public virtual IList<T> GetList(Func<T, bool> where, params Expression<Func<T, object>>[] navigationProperties)
{
List<T> list;
IQueryable<T> dbQuery = ((DbContext)context).Set<T>();
//Apply eager loading
foreach (var navigationProperty in navigationProperties)
{
dbQuery = dbQuery.Include(navigationProperty);
}
list = dbQuery.AsNoTracking().Where(where).ToList();
return list;
}
I am finding that the initial state property for my entities is zero, but I want to set it to
I want to set State property to be EntityState.Unchanged
How should I do this?
Julie Lerman described it in her book Programming Entity Framework: DbContext At page 93
(Example 4-15).
You can use following code in your DbContext constractor to set the object states to UnChanged:
public YourContext()
{
((IObjectContextAdapter)this).ObjectContext .ObjectMaterialized +=
(sender, args) =>
{
var entity = args.Entity as IEntity;
if (entity != null)
{
entity.State = State.Unchanged;
}
}
}
Here's another easier approach to this issue. I am using it and it works!!
public abstract class Entity<TId> : BaseEntity, IEntity<TId>, IModelState
{
public virtual TId Id { get; private set; }
public byte[] RowVersion { get; protected set; }
private readonly IDictionary<Type, IEvent> events = new Dictionary<Type, IEvent>();
public IEnumerable<IEvent> Events => events.Values;
public ModelState ModelState { get; protected set; } = ModelState.Unchanged;
protected Entity()
{
ModelState = ModelState.Added;
}
... removed for brevity
make sure you're using C# 7 and Roslyn Compiler with your .NET 4.6.x
I think this is safer because only your own Entity Object will have the power to set it to unchanged when it gets initialized by EF. In my opinion DbContext should not have the right to set the "state" property of any entity.