I use code-first approach and have used SQL Server as database. All the entities are configured like below:
public interface IBaseEntityTypeConfiguration<T>
{
public void Map(EntityTypeBuilder<T> builder);
}
// My entity
public class Student : IBaseEntityTypeConfiguration<Student>
{
public Id { get; set; }
public string Name { get; set; }
public void Map(EntityTypeBuilder<Student> builder)
{
// Configure entity
}
}
In the ApplicationDbContext all the classes which are inherited from IBaseEntityTypeConfiguration are added to the modelBuilder.Configurations.
But I came up with a new requirement. We should switch from SQL Server to PostgreSql!
I don't want to change the Map() method in all entities, but I want to add some kind of provider which says to modelBuilder that use configurations for SQL Server configurations or PostgreSql configs.
Something like below:
public class ApplicationContext : DbContext
{
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// var entities = new MsSqlEntityConfiguration().GetAllTypes();
var entities = new PostgreEntityConfiguration().GetAllTypes();
foreach(entity in entities)
modelBuilder.Configuration.Add(entity); // As an example
}
}
Is there any solution that exists as described?
Related
I use generic repository and I want to use dbcontext without declaration of dbset for each single database model. I tried some solutions but every time I've got this error :
Cannot create a DbSet for 'PointsType' because this type is not
included in the model for the context.
public new DbSet<TEntity> Set<TEntity>() where TEntity : BaseEntity
{
return base.Set<TEntity>();
}
BaseEntity used for generic repository.
Is there anyway to solve this problem ?
You have to register entities in a DbContext. It can infer relations and discover other entities by itself, but it's better to be explicit about it.
You have two options:
1. Adding DbSet<T> properties to DbContext
One way to do this is to add DbSet<TEntity> properties in DbContext class:
class AppDbContext: DbContext {
public DbSet<Product> Products { get; set; }
// ...
}
This is easy, but requires modifying DbContext for every new entity.
2. Implementing IEntityTypeConfiguration<T>
Another way is to implement IEntityTypeConfiguration<TEntity> for all entities, and let DbContext discover configurations using modelBuilder.ApplyConfigurationsFromAssembly method.
Note that there are no DbSet<TEntity> properties in the DbContext class!
class AppDbContext: DbContext
{
// no DbSet properties 🎉
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfigurationsFromAssembly(typeof(AppDbContext).Assembly);
}
}
class Product
{
public int Id { get; set; }
public string Title { get; set; }
}
class ProductEntityConfiguration: IEntityTypeConfiguration<Product>
{
public void Configure(EntityTypeBuilder<Product> builder)
{
builder.HasIndex(e => e.Title).IsUnique();
}
}
Now you can access a DbSet for an entity using:
var productSet = dbContext.Set<Product>();
References
https://learn.microsoft.com/en-us/ef/core/modeling/#grouping-configuration
I am trying to map the results of a complex SQL query to a keyless entity in EF Core. (I basically need to create a view client-side due to having to deal with a preexisting database.)
However, when trying to retrieve my data, I get an InvalidOperationException "Sequence contains no elements". I can map base tables in the DB to normal entities, so I know the connection string is correct. A simplified, but complete example:
using System;
using System.Linq;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore;
class Program
{
static void Main(string[] args)
{
using (var db = new QueryDbContext())
{
List<Model> result = db.Orders.ToList(); // <-- exception occurs here.
Console.WriteLine($"Found: {result.Count}");
}
}
}
public class Model
{
public string OrderId { get; }
}
public class QueryDbContext : DbContext
{
public QueryDbContext()
: base()
{}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
_ = optionsBuilder
.UseSqlServer("myconnectionstring");
}
}
public DbSet<Model> Orders { get; set; }
private const string _OrdersSql =
"select top 5 orderid from tbl_orders order by transacttime desc";
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder
.Entity<Model>(m =>
{
m.HasNoKey();
m.ToQuery(() => Set<Model>().FromSqlRaw(_OrdersSql));
});
}
}
Set<>() is the current version (since EFCore 3.0) of Query<>() as described in the answer to FromSql with Non-Existing Entity.
The problem was that the model properties need both setters and getters, even though the model is read-only. So the model should be
public class Model
{
public string OrderId { get; set; }
}
I made this change, before it was like this
public DbSet<Pessoa> Pessoas { get; set; }
Now it's this way
class ContextData<T> : DbContext where T : class
{
public DbSet<T> Dbset { get; set; }
public ContextData() : base("name=Connection")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Configurations.Add(new PessoaMapping());
}
}
So now I do not know what I do in class migrations
internal sealed class Configuration : DbMigrationsConfiguration<local.blogapi.Context.ContextData
{
public Configuration()
{
AutomaticMigrationsEnabled = true;
}
protected override void Seed(local.blogapi.Context.ContextData context)
{
}
}
I know it's going to make a mistake. I do not know how to get the entities or if there is any specific command at the time of doing the migration, or if I pass some interface, such as IEntity
you're probably wrong.
A context is a group of dbset (dbsets represent sql tables), read this.
I think that you don't need one context for every table in your db.
Use of more contexts may make sense if you want to separate dbsets that have different logic (based on the schema for example, or to separate read / write tables)
so I have 2 different DbContext (ef 6.1.3 code first)
FirstDbContext
SecondDbContext
each context contains a SbSet Users that maps the user table in the corresponding database
NOTE : the data is different, DbFirst User is not DbSecond User!!
I have an abstract repository:
public abstract class Repository<TContext> where TContext : DbContext
{
public Repository(TContext ctx)
{
}
}
and 2 repositories :
public FirstRepo : Repository<FirstDbContext>
{
public FirstRepo(FirstDbContext ctx):base(ctx)
{
}
}
public SecondRepo : Repository<SecondDbContext>
{
public SecondRepo(SecondDbContext ctx):base(ctx)
{
}
}
I Have 2 different MSSQL databases related to the contexes:
DbFirst
DbSecond
I'm using dependency injection to add scoped repository and contexes, 2 database, 2 different connection.
I expected that my .Net Core application would use 2 Models
but once i get data from both the context i get
NotSupportedException: The type 'First.User' and the type
'Second.User' both have the same simple name of
'User' and so cannot be used in the same model.
Why the same model?
I know that in the same model I should have different names because EF does not look for namespaces, but in that case I shouldn't have this kind of issue.
EDIT #1 :
If I use one of the repository alone everything works as expected so i'm sure that there isn't any mispelled namespace
If I use the repositories all together i got this error, for example
var top10 = FirstRepo.GetTop10().ToList();
var sam = SecondRepo.GetByName<Second.User>("sam");
EDIT 2 (#Steve Green):
//Note that I'm not trying to do this :
public class MyTextContext : DbContext
{
public DbSet<Test.Security.Question> Security_Question { get; set; }
public DbSet<Test.Forms.Question> Forms_Question { get; set; }
}
// What I need is something like this :
public class SecurityContext : DbContext
{
public DbSet<Test.Security.Question> Question { get; set; }
}
public class FormsContext : DbContext
{
public DbSet<Test.Forms.Question> Question { get; set; }
}
Important note
If I manually ignore the "other" entity in both of the context everything works
I Remark that the context are not only in different namespaces, but also different assemblies...
// this is working!! .___.
// but i don't want to add a reference to a project just to exclude a class... it's unacceptable
public class FirstDbContext : DbContext
{
public DbSet<First.User> Users {get;set;}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Ignore<Second.User>();
}
}
public class SecondDbContext : DbContext
{
public DbSet<Second.User> Users {get;set;}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Ignore<First.Usere>();
}
}
Any suggestion different from renaming the table will be appreciated
Thanks
I want to get some data of database in C#.
But my LINQ Query was connected invalid database table.
DataContext.cs
namespace BlackBoxSOS.Models
{
public class DataContext : DbContext
{
public DbSet<ServiceIdentification> ServiceIdentifications { get; set; }
public DbSet<ObservationDatum> ObservationData { get; set; }
....
}
}
Function In Service.svc.cs
public GetObservationResponseTypeMessage GetObservation(GetObservationTypeMessage request)
{
DataContext dataContext = new DataContext();
var result = dataContext.ObservationData;
....
return ...;
}
Variable result is "SELECT [Extent1].[Id] AS [Id], [Extent1].[Res_Value] AS [Res_Value]
FROM [dbo].[ObservationDatums] AS [Extent1]" when executing my code. But DataContext has DbSet ObservationData, not ObservationDatums.
Why this program has error? How can i fix it?
You can customise the table name used by Entity Framework code first by applying the table attribute to the ObservationDatum type, or by using the Fluent API (override OnModelCreating in the DbContext).
e.g.:
[Table("ObservationData")]
public class ObservationDatum
{
}
Or:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<ObservationDatum>().ToTable("ObservationData");
}
If all of your table names are singular, you could try disabling table name pluralisation using the following code in your DbContext:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}