Slowly migrating an EDMX application to use a DbContext? - entity-framework

I have a legacy EF 4 library with a database-first generated ObjectContext with EntityObjects. I'd like to slowly migrate to using DbContext and am in need of some guidence.
One of the overloads for DbContext takes an existing ObjectContext. I thought this would allow me to wrap my existing ObjectContext in a DbContext and expose my existing EntityObjects through IDbSet properties. Unfortunately, when creating the DbContext, the IDbSet properties are not created and instead an exception is thrown with a message of: "Verify that the type was defined as a class, is not primitive, nested or generic, and does not inherit from EntityObject."
Is there no way to use a DbContext with IDbSet exposing an existing ObjectContext and EntityObjects? It seems strange that I can create a DbContext with an ObjectContext, but not expose the entities themselves.
Here's some sample code:
public class MyDbContext : DbContext
{
public MyDbContext(MyObjectContext objectContext, bool dbContextOwnsObjectContext)
: base(objectContext, dbContextOwnsObjectContext)
{
}
public IDbSet<Person> People { get; set; }
}
The exception is caused by the DbContext trying to create the IDbSet. Person is an EntityObject from my existing EDMX.

As you noted, you cannot use the existing entity objects that the edmx created.
Have you considered using the Reverse Engineer Code First power tools?
I've used them in several projects, and its simple as pointing it at your database, it generates the model in the same project, and then removing the edmx file.

Related

Why can't I use ApplyConfiguration() to add my audit log table (Audit.NET can't find it)?

I have an existing application that is built on Entity Framework Core 2.2.x. It is using modelBuilder.ApplyConfiguration() to associate entities with the data model dynamically. This works for all of the current entities and even my new AuditLog entity as far as the rest of the application is concerned.
However, when I configure Audit.NET's entity framework core provider to log into AuditLog, the data provider cannot write to the database:
The entity type 'AuditLog' was not found. Ensure that the entity type has been added to the model.
I have scoured the internet for solutions to that error, and found that adding this line to my code will cause Audit.NET to find my AuditLog:
modelBuilder.Entity<AuditLog>().ToTable("AuditLog", "Audit");
My code:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
Type[] maps = EntityFrameworkReflectionMapping.Get(EntityTypeConfiguration(), BoundAssemblies);
foreach (object instance in maps.Select(Activator.CreateInstance))
modelBuilder.ApplyConfiguration((dynamic)instance);
modelBuilder.Entity<AuditLog>().ToTable("AuditLog", "Audit");
base.OnModelCreating(modelBuilder);
}
Why do I need to add the entity explicitly, when the rest of the system works as-is?
Additionally, the changes are being detected by Audit.NET through entities which are not explicitly added. So the problem seems to be with Audit.NET's entity framework data provider, or how I'm using it.
I would expect that the data provider would respect the modelBuilder.ApplyConfiguration() approach to associating entities.
There are many things that could be causing the exception, but looks like EF is not being able to detect the entity-table relation for your AuditLog.
Look for a wrong connection string, maybe your AuditLog being defined on a different assembly than other entities.
Also try adding the AuditLog entity class within a db set as a property on your DbContext, for example:
public class MyContext : AuditDbContext
{
//...
public DbSet<ModelName> ModelName { get; set; }
}

Single table accross multiple DbContext classes using EF6 Code First

In my application I have 2 contexts targeting the same SQL database. One context is for my AspNet Identity tables and the other one is for the rest of the application. What I am trying to do is make my 'User' table (which is for the IdentityUser class in my IdentityDbContext) available in my ApplicationContext. I am using Code First Migrations and do not want the ApplicationContext to attempt to create (or modify) that table - I just want it to 'know' about it and be able to query and join it to other tables but want it ignored by the MigrateDatabaseToLatestVersion<ApplicationContext, Configuration>() initializer or when issuing update-database from the Package Manager console.
I have found a few similar questions but no suitable answers.
In this cases I think, you have to create a separate DbContext That contains all DbSets from all your DbContexts and use this master DbContext for migrations purpose.
If you want some entities be read-only in some your contexts(User in your case) you can define entitiy's DbSet as following
public ApplicationContext:DbContext
{
...
public DbSet<User> Users { get; private set; }
}
private setter for a DbSet cause it can set by Entity Framework, but not by users.

Query against Domain Classes should resolve against Entities

I am trying to build a generic repository that allows querying against domain classes.
My Repository interface looks like the following:
public interface IRepository<T>
{
T Get(int id);
IQueryable<T> Query();
void Add(T model);
void Remove(T model);
}
Given I have an UserEntity entity framework class and a User domain class, I want to query against the User. The UserEntity should not be exposed to other services, because it should be internal to the Entity Framework layer.
A query like userRepository.Query().Single(user => user.UserName == "Toni") should return a User domain class. However internally it should query against an IDbSet<UserEntity> returned from my entity framework. The Expression Tree (which contains the Single query operation) should be attached to a query against IDbSet<UserEntity>. After querying against IDbSet<UserEntity> I want to convert the UserEntity to a User domain class. Is this possible?
I have in mind to cerate an IQueryable implementation for my User class that internally queries against UserEntity.
public class MappedEntityQuery<TModel, TEntity> : IQueryable<TModel>
{
}
Code First requires the convention to have all IDbSet properties to access the tables to be in the DbContext
That is not true. You don't need to have any set declared in the context if you provide mapping to the entities in the model builder. In your case you should declare mapping through EntityTypeConfiguration<T> and ComplexTypeConfiguration<T> derived classes. You can create any DbSet instance of mapped entity type by calling Set<T>() on the context.
However for this project I am using a Database First approach, which also does not allow to load compose a DbContext using entities from different projects, because you have to specify the Database metadata in one single metadata file (which will be embedded).
That is true only partially. EDMX metadata must be in the single file embedded in the main project but entity classes do not have to if you use your own instead of auto-generated. So your proposed approach should work.
But if you really want to achieve modularity you should not use EDMX. If you decide to add or change any module in the future it would require changing central project as well but that can affect all other modules - it breaks the idea of modularity, doesn't it?

Automatic properties: DBSet vs ObjectSet

Why are we able to use automatic properties with DBSet, but not with ObjectSet:
public class SomeContext : DbContext
{
public DbSet<Address> Addresses { get; set; }
...
}
Thank you
DbContext is using convention over configuration paradigm. It discovers entity sets based on DbSet properties defined on the DbContext derived class (or in general, it discovers your model based on your code). ObjectContext does not do any discovery and is not convention based. It just reads your model from csdl, ssdl and msl artifacts. As a result ObjectContext requires that the user tells exactly what needs to be exposed.
Simply because DbContext API is newer version created to provide simplified API for better development experience.

EF4 CTP5: DbContext inheritance

I have a hierarchical DbContext structure, where I would like a specialized DbContext with its own DbSets to inherit the DbSets of a BaseDbContext.
While accessing the underlying ObjectContext with ((IObjectContextAdapter)this).ObjectContext it takes too long (several minutes) to receive the ObjectContext.
Is there an issue with DbContext in CT5, that getting an ObjectContext from derived DbContext is not performantly possible?
The structure is: DbContext(EF4) -> myBaseDbContext -> mySpecializedDbContext.
Does anyone have an idea of what´s going on in this scenario?
It´s just POCO (CF) with TPC and a little inheritance.
I didn't have performance issues with following and you don't have so many DbSets:
public class MyContext: DbContext
{
//your DbSets<> and other
public ObjectContext ObjectContext()
{
return (this as IObjectContextAdapter).ObjectContext;
}
}