Multiples contexts ef-core - c#

I'm working with EF Core and I decided split the DbContexts I'm using.
Everything is right but I notice that when I create the migrations for each context, EF tries to create the tables again even if the other context had created that table previously.
I think EF Core must be trying to update the tables instead of trying to create them but I don't know why EF Core would do that.
AccountsContext :
public class AccountsContext : BaseContext<AccountContext>
{
public AccountContext(DbContextOptions<AccountsContext> options): base (options){}
public DbSet<Account> Accounts { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Account>().HasKey(a => a.id);
modelBuilder.Entity<Account>().Property(a => a.Name).HasField("_name");
modelBuilder.Entity<Account>().Property(a => a.Role).HasField("_role");
modelBuilder.Entity<Account>().OwnsOne(a => a.Password);
modelBuilder.Entity<Account>().OwnsOne(a => a.Email);
}
}
CampaignContext :
public class CampaignContext: BaseContext<CampaignContext>
{
public CampaignContext(DbContextOptions<CampaignContext> options): base (options){}
public DbSet<Campaign> Campaing { get; set; }
private DbSet<Account> Accounts { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Campaing>().ToTable("Campaings");
modelBuilder.Entity<Campaing>().HasKey(a => a.Id);
modelBuilder.Entity<Campaing>().HasOne(c => c.Owner);
modelBuilder.Entity<Account>().ToTable("Accounts");
}
}
Here is the scaffold of the migration in the campaign context. EF Core looks like its trying to create the accounts table even if it already exists in the database.

you should specify the Context you want to use by
dotnet ef Update Database -Context DbContext

each context in Ef core considered as a separated database so you are putting that table in both of the databases I don't know why you are trying to do that but it is normal that you see the table in both migrations

Related

How to map columns in Custom model to Identity model in ASP.NET Core Identity

I am implementing ASP.NET Core Identity in an existing project with an existing Database.
I have specified the Entities to use for IdentityUser and IdentityRole like this:
public partial class MyContext : IdentityDbContext<MyUser, MyRole, int>
In Startup.cs:
services.AddIdentity<MyUser, MyRole>(options =>
{
options.User.RequireUniqueEmail = true;
}).AddEntityFrameworkStores<MyContext>();
I was able to apply the migrations successfully to the database. But, on inspecting the AspNetRole table, I could see that EF created some extra columns.
How do I tell EF Core to use the existing colums (like RoleId for Id in the above image) instead of creating new ones?
Also, I noticed that EF Core changed MyRoles table name to AspNetRoles which was not the case with the MyUsers table. MyUsers table name remained the same even though extra columns were created as I mentioned above. Why is that?
PS: I am using DotNet Core 3.0 with EF Core 3.0.
Add [Column()] annotations to change the default column name:
public class MyRole: IdentityRole<int>
{
[Column("RoleId")]
public override int Id { get; set; }
[Column("RoleName")]
public override string Name { get; set; }
}
To reuse the old table MyRoles, custom the OnModelCreating(builder) method:
public class AppIdentityDbContext : IdentityDbContext<MyUser, MyRole, int>
{
...
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<MyRole>(e =>{
e.ToTable("MyRoles");
});
}
}

Defining many-to-many connection defining ICollection<> only in one class

I have an .NET application with the validation logic outsourced to a .NET Core API. There are some models and logics that both of them use and I want them to use the same classes from a (.net standard) Nuget package. The main problem that I'm using Entity Framework code-first, and some of the common models are in database too but i don't want to include f.e. ApplicationUser in the nuget package.
There is a model with a many-to-many connection to ApplicationUser and I don't want to define the ICollection in it.
So my "local" class looks something like this:
public class ApplicationUser : IdentityUser
{
....
public ICollection<Institute> Institutes { get; set; }
....
}
And my "remote" class looks like this:
public class Institute
{
....
public ICollection<ApplicationUser> Users { get; set; }
....
}
But I don't want Institute to have this public ICollection<ApplicationUser> Users { get; set; } but I want the EF to map the many-to-many connection. If I delete it from the Institute class the next migration will delete the whole ApplicationUserInstitute connection table.
I have thought of some kind of inheritance solution but I think future development would be pain in the ass with it. Every possible solution is welcomed.
You need explicitly override OnModelCreating() in your context and specify Many-to-Many relation there:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<ApplicationUser>()
.HasMany(s => s.Institues)
.WithMany()
.Map(cs =>
{
cs.MapLeftKey("UserRefId");
cs.MapRightKey("InstituteRefId");
cs.ToTable("UserInsitute");
});
}
I used entities from this article to generate schema. The only difference is that I removed public virtual ICollection<Student> Students { get; set; } from Course.

No method to map entity to table in entity framework core

I am using entity framework core in a normal .net project, version 4.7. I know I can do this. The problem is that I can't seem to map an entity to a table because the "ToTable" method doesn't exist. I can't edit the poco or entity classes because they are predefined and generated. So I can't use the attribute. I looked on the internet and everyone seems to use this method to map an entity to a table.
Here is my code:
public class FactsDbContext : DbContext
{
public DbSet<TblIncident> TblIncidents { get; set; }
public DbSet<TblAction> TblActions { get; set; }
public DbSet<TblAddressTypeAlias> TblAddressTypeAliases { get; set; }
public DbSet<TblCountry> TblCountries { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
//these methods don't exist in my case
modelBuilder.Entity<TblIncident>(entity => entity.ToTable("Incident"));
modelBuilder.Entity<TblIncident>().ToTable("Incident");
}
}
I also tried to use IEntityTypeConfiguration with a EntityTypeBuilder but it still don't have access to the map to table method:
public class IncidentConfig : IEntityTypeConfiguration<TblIncident>
{
public void Configure(EntityTypeBuilder<TblIncident> builder)
{
builder.ToTable("Incident");
}
}
I looked into the Entity Framework Core repository on GitHub and searched for the method "Totable" inside the repository. It turns out it is defined as an extension method but it is in separate nuget package and library called Microsoft.EntityFrameworkCore.SqlServer
After I downloaded the package I got the Totable method that I need. Still it doesn't make sense to add that method in a separate package for sql server when you already have the "Table" attribute that you can add on entities directly in the entity framework core package.
You can use the below approach. You have to use Table data annotation.
DBContext:
public virtual DbSet<Article> Article { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Article>(b =>
{
b.Property(e => e.Property1).HasDefaultValue(true);
... //Other properties
}
Model class:
[Table("Article")]
public class Article
{
You can also use to ToTable in DBContext, but you have to make sure that you have included using Microsoft.EntityFrameworkCore;.
Line modelBuilder.Entity<TblIncident>().ToTable("Incident"); looks correct according to the documentation.
https://learn.microsoft.com/en-us/ef/core/modeling/relational/tables#fluent-api
It's very old thread but I got the same issue and I solved it by placing base.OnModelCreating(builder) as a first line of OnModelCreating method.
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Rest of the code
}

EF Core 1.1 Soft (or logical) Delete

I'm trying to implement Soft Delete in a .NET Core 1.1.* Web App, backed by Entity Framework Core 1.1.*. I'm using Sql Server as my DB.
Migrating to .NET core 2.* is not an option at the moment.
After reading books, tuts and 3ds, I've implemented this feature using a Discriminator column. The deletion procedure is apparently working as expected. What's wrong is the data retrieval: deleted entities are still shown within my EF query results.
Current situation
Here's some C# code. I'll keep things as simple as possible
The interfaces:
// Soft deletion interface
public intercace ISoftDeletable
{}
// Another interface for some shadow properties
public interface IEntity
{}
The base class:
public abstract class Entity : IEntity, ISoftDeletable
{
public int MyBaseProp { get; set; }
}
One of my derived classes:
public class MyDerivedEntity: Entity
{
public string Name { get; set; }
public IList<MyChildEntity> Children { get; set; }
}
public class MyChildEntity: Entity
{
public string MyChildProp { get; set; }
}
The Context
public class MyContext: DbContext
{
public MyContext(DbContextOptions<MyContext> options)
: base(options)
{ }
public DbSet<MyDerivedEntity> EntitiesToUse { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
foreach (var entity in builder.Model.GetEntityTypes())
{
if (typeof(IEntity).IsAssignableFrom(entity.ClrType))
{
builder.Entity(entity.ClrType).Property<string>("MyShadowProperty");
}
if (typeof(ISoftDeletable).IsAssignableFrom(entity.ClrType))
{
// Discriminator column
builder.Entity(entity.ClrType).HasDiscriminator("IsDeleted", typeof(bool)).HasValue(false);
// Shadow Property
builder.Entity(entity.ClrType).Property(typeof(bool), "IsDeleted").IsRequired(true).HasDefaultValue(false);
builder.Entity(entity.ClrType).Property(typeof(bool), "IsDeleted").Metadata.IsReadOnlyAfterSave = false;
}
}
// Other model configurations
base.OnModelCreating(builder);
}
// SaveChangesAsync are almost the same
public override int SaveChanges()
{
AuditEntities();
return base.SaveChanges();
}
private void AuditEntities()
{
foreach (EntityEntry<IEntity> entry in ChangeTracker.Entries<IEntity>())
{
// do something with MyShadowProperty...
}
foreach (EntityEntry<ISoftDeletable> entry in changeTracker.Entries<ISoftDeletable>().Where(w => w.State == EntityState.Deleted))
{
// Set the entity as Softly Deleted
entry.Property("IsDeleted").CurrentValue = true;
// Ensure the entity state is modified to prevend hard deletion
entry.State = EntityState.Modified;
}
}
}
The Problem
Everything works as expected, except the data retrieval.
Here's a sample call:
var results = await _context.EntitiesToUse.Include(e => e.SomeChildEntity).AsNoTracking();
I expect the results to include only available myDerivedEntities with .IsDeleted == false. The problem is that my deleted entities are not filtered out. Why?
Please, what's wrong with my code? Am I missing something?
Thank you all so much!
Entity Framework Core 2.0 supports Global Query Filter
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<ISoftDeletable>().HasQueryFilter(e => !e.IsDeleted);
base.OnModelCreating(modelBuilder);
}
You can find more info and examples here
I recommend you the built-in EF Core Global Query Filter but in some situations, Entity Framework Plus could also help.
Disclaimer: I'm the owner of this project
EF+ Query Filter allows you to filter DbSet Globally and by Instance.
// using Z.EntityFramework.Plus; // Don't forget to include this.
var ctx = new EntitiesContext();
ctx.Filter<IUser>(q => q.Where(x => !x.IsSystemUser ));
// SELECT * FROM Customers WHERE IsSystemUser = FALSE
var list = ctx.Customers.ToList();
Wiki: EF+ Query Filter
EDIT: Answer sub-question
Please, Is your library compatible with EF Core 1.1
Yes, it should be compatible with .NET Standard 1.3

Accessing a table created with ModelBuilder by using DbSet of Entity Framework

I have the following code first scenario:
public class crmContext : DbContext
{
public crmContext() : base("crmContext")
{
} // end crmContext()
public DbSet<Pool> Pools { get; set; }
public DbSet<Center> Centers { get; set; }
// Po DbSet PoolAssignments?
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Pool>()
.HasMany(c => c.Centers)
.WithMany(p => p.Pools)
.Map(
m =>
{
m.ToTable("PoolAssignments");
m.MapLeftKey("poolid");
m.MapRightKey("centerid");
});
} // end OnModelCreating()
} // class crmContext
The PoolAssignments table is created by modelBuilder and I would like to access it the same way I access Pools and Centers. For example:
crmContext db = new crmContext();
Pool pool db.Pools.Find(id);
PoolAssignment pa = db.PoolAssignments.Find(id);
The problem with this approach is that I have not defined PoolAssignment as a class and there is no DbSet PoolAssignments in crmContext. I think I am not understanding this part of Entity Framework very well.
If I define a PoolAssignment class (together with its navigation properties) and a PoolAssignments DbSet, then the modelBuilder code becomes unnecessary. Code first will generate the table for me.
I am simply trying to understand the logic behind using modelBuilder in such a scenario. How can I access the table PoolAssignments and how can I access data with the Entity Framework if I do not define classes? I have searched for an answer but I cannot find any. I have read many articles but none seems to cover this scenario.

Categories