Entity Framework 6: target two different database systems from one application - entity-framework

We have an application that needs access to two databases. One is SQL Server and the other one is DB2. We found out that we can change the database provider by creating a custom configuration e.g. for DB2:
public class DB2DBConfiguration : DbConfiguration
{
public DB2DBConfiguration()
{
SetProviderServices(
DB2ProviderServices.ProviderInvariantName,
DB2ProviderServices.Instance);
SetDefaultConnectionFactory(new DB2ConnectionFactory());
}
}
internal class DB2ConnectionFactory : IDbConnectionFactory
{
public DbConnection CreateConnection(string nameOrConnectionString) => new DB2Connection(nameOrConnectionString);
}
And then we add the corresponding attribute on top of the model:
[DbConfigurationType(typeof(SQLServerDBConfiguration))]
public partial class ProductDBModel : DbContext
This works just fine when using only one model with one database. However if we want to use DB2 and SQL Server at the same time (two different DB Models with the specific DbConfigurationType) we get the following error for the second one:
An instance of "SQLServerDBConfiguration" can not be set as there is already an instance of "DB2DBConfiguration" in use.
What can we do about this issue? How can we target two different database systems in one application with Entity Framework?

Related

Table per concrete type in EF Core

I have built the following model of hierarchy for DB:
public abstract class ApplicationUser : IdentityUser
{ }
public class FirstUser : ApplicationUser
{}
public class SecondUser : ApplicationUser
It is noticeable abstract Application class inherits from ASP.NET Core Identity's not abstarct class IdentityUser.
So my purpose is building different tables for UserFirst and UserSecond only, not for IdentityUser and ApplicationUser.
I tried to configure model the following:
builder.Ignore<IdentityUser>();
builder.Entity<FirstUser>().ToTable("FirstUsers");
builder.Entity<SecondUser>().ToTable("SecondUsers");
However it throws exception: Invalid column name 'Discriminator'
What can I do?
Table per Concrete Type (TPC) or Table per Type (TPT) aren't currently supported in EntityFrameework Core 1.0. Only Table per Hierarchy (TPH) is supported.
TPC and TPT are on the high priority list and may come in EntityFramewor Core 1.1 or 1.2, see the EntityFramework Core Roadmap.
Backlog Features
...
High priority features
...
Modelling
More flexible property mapping, such as constructor parameters, get/set methods, property bags, etc.
Visualizing a model to see a graphical representation of the code-based model.
Simple type conversions such as string => xml.
Spatial data types such as SQL Server's geography & geometry.
Many-to-many relationships without join entity. You can already model a many-to-many relationship with a join entity.
Alternate inheritance mapping patterns for relational databases, such as table per type (TPT) and table per concrete type TPC.
As for your question:
You can't do anything about it. If you absoloutely need this feature, you have to fall back to EntityFramework 6.x, but then you can't target .NET Core and have to target .NET Framework 4.x.
It should be noted here, that Microsoft do not feels (or recommends) to use EntityFramework Core 1.0 yet in production environment, if you require the features used from EF6. It will take several versions (at least 2 minor releases) until EntityFramework Core will get anyway close featurewise to EF6.
So if TPC is absolute requirement, go back to EF6.
Technical stuff aside, performance wise it's prefered to use TPH for mapping inheritance to your database as it avoids unnecessary joins during queries. When you use TPT/TPC every query invovling it will have to perform joins and joins are less performant.
So unless you have to map to a legacy DB designed in that way, you should fall back to TPH.
Table-per-concrete-type (TPC) mapping is now available in EFC 7.0.0 nightly builds.
https://github.com/dotnet/efcore/issues/3170
https://github.com/dotnet/efcore/issues/3170#issuecomment-1124607226
https://learn.microsoft.com/en-gb/ef/core/what-is-new/ef-core-7.0/plan#table-per-concrete-type-tpc-mapping
What you need to try it out:
.NET SDK 7.0.100-preview.4
https://dotnet.microsoft.com/en-us/download/dotnet/7.0
Visual Studio 2022 Preview 17.3
https://visualstudio.microsoft.com/vs/preview/
NuGet
Microsoft.EntityFrameworkCore 7.0.0-preview.4.22229.2
Code example:
ApplicationDbContext:
using Microsoft.EntityFrameworkCore;
namespace WebApplicationNet7.Data
{
public class ApplicationDbContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<RssBlog> RssBlogs { get; set; }
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>().UseTpcMappingStrategy();
modelBuilder.Entity<RssBlog>().UseTpcMappingStrategy();
}
}
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
}
public class RssBlog : Blog
{
public string RssUrl { get; set; }
}
}
Migration will look like this:
Note that RssBlog is missing .Annotation("SqlServer:Identity", "1, 1").
You will probably get a warning that looks like this:
Microsoft.EntityFrameworkCore.Model.Validation[20609]
The property 'BlogId' on entity type 'Blog' is configured with a
database-generated default, however the entity type is mapped to the
database using table per concrete class strategy. Make sure that the
generated values are unique across all the tables, duplicated values
could result in errors or data corruption.
I could not get it to work with either setting
modelBuilder.Entity<RssBlog>().Property(u => u.BlogId).UseIdentityColumn(); or using annotation [DatabaseGenerated(DatabaseGeneratedOption.Identity)].

Creating model for existing AspNetUsers table in Entity Framework Code First Approach

I am developing an Asp.Net mvc5 project. I am using Entity Framework code first approach to interact with the database. I am using Identity System for user authentication. But I am having a problem with integrating the AspNetUsers table from identity system to my model. My identity system database context and model database context class are different because I am using built in included identity system.
But connection string are same. Both use default connection string. But model is in different project. Now I am in the middle of the project. I have existing model classes mapped with tables in database. I created an user account from UI, so required tables(AspNetUsers table included) for identity system are auto-migrated. Now I want to create model class to map AspNetUsers table because I want to create relationship with between that AspNetUsers table and one of the existing tables. So I created a model class named "AspNetUser" class in my model project. Then I created properties for that class according to AspNetUsers table in database. Then I add migration and update database for my model. It gave me following error.
There is already an object named 'AspNetUsers' in the database.
What I can think is to delete AspNetUsers table and update database. But if I delete it, I have to delete other tables required for identity system because they have FK relationship. I do not want it. Second way I can think is to delete all required tables for identity system. Then migrate AspNetUser model class. Then delete that class and register new account from UI. So all required tables for identity system will be created again.
Second way also, I have to delete tables and happen data loss. How can I migrate overriding the existing AspNetUsers table? Is it possible? What is the best way to integrate AspNetUsers table to my model?
This is Configuration class in Migration folder
internal sealed class Configuration : DbMigrationsConfiguration<PatheinFashionStore.Domain.Concrete.StoreContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = true;
AutomaticMigrationDataLossAllowed = true;
}
protected override void Seed(PatheinFashionStore.Domain.Concrete.StoreContext context)
{
}
}
This is my context initializer class
public class ContextInitializer : System.Data.Entity.CreateDatabaseIfNotExists<StoreContext>
{
protected override void Seed(StoreContext context)
{
}
}
If it is not possible, what would be the best way?

When using Entity Framework, is Migrating necessary?

Since I already have a DB project in my solution, I was wondering can I leverage Entity Framework so that the Web Application may communicate with the database without having to do any migrations, or is migration necessary in order to use the Context of Entity Framework?
If migrating is not necessary then I also won't need the designer (edmx) file, I could just make my models and link them into my context right?
Not necessary at all.
I often write short solutions towards some databases where I just want to add some functionality on the fly. What I typically do is to just open SQL Server Object Explorer in VS2015, Expand the table I'm interested in, and just write a class on the fly, matching the fields that I need.
I.e for a table named "Classes" in the database, I would end up with a class in C#
public class SomeClass
{
public int Id {get; set;}
public string Title {get; set;}
// +other intersting fields
}
Then I simply create a DbContext class pointing to my database:
public class MyContext : DbContext
{
public DbSet<SomeClass> Classes{get; set;}
public MyContext() : base("myConnectionString"){}
}
That's it. No need for migrations. It then simply just works :)

How do I extend a dbcontext entity class?

I have exposed some EF5.0 entities generated from a database through at WCF data service.
The data service is consumed by a WPF-client which takes the entities (from the data service) and store them locally. I do this by creating a code-first entity database based on the WCF-entities:
public class LocalRaceContext : DbContext
{
public LocalRaceContext() { }
public LocalRaceContext(string connstr) : base(connstr) { }
public DbSet<Participant> Participants { get; set; }
.
.
. more ...
}
I want to extend the Participant with a new property (in the client-side model). I figured I could just do this with a partial class like so:
public partial class Participant
{
public virtual List<Stamp> Stamps { get; set; }
}
This however does not work. Do I need some kind of attribute on the partial class?
I get the following error:
"The type 'RaceEntities+Participant' was not mapped. Check that the type has not been explicitly excluded by using the Ignore method or NotMappedAttribute data annotation. Verify that the type was defined as a class, is not primitive, nested or generic, and does not inherit from EntityObject."
Edit:
#IronMan84: The original model (without the partial class) works because EF code-first takes care of the database and table creation. Actually it works perfectly fine and I am able to save the EF model in a local SQL CE file and retrieve the objects again as EF-classes later.
What I'm trying to achieve is to persist data from the data service locally but in a model which is somewhat extended. I've succeeded so far up until the extending part.
#Matt Whetton: It fails when I create a new instance of LocalRaceContext.
Edit2: I've tried to make an empty partial class (no properties). It still throws the same error.
Thanks in advance
Frederik
Nested classes are not yet supported by EF. Move the Participant class outside of RaceEntities.

add my own property to EF designer

I using vs2010 and the built-in EF designer against SQL server Compact 4.0 local database that in my project. ( design-first)
I tried to add (through the designer) to one of the entities a new property that doesn't exist in the DB (nor planed to) by: add-new scalar property.... , but when i was tring to validate it, i got an error:
property "x" is not map
my question:
can i add my own properties to the schema through the designer. and that properties won't be exist in the underline DB.(I thought i could)?
if I can, how?
You can add non db fields to your entity by creating a partial class of that entity with the required properties. These won't be used for the ORM.
public partial class MyEntity {
public string StringStatusCode { get;set;}
}