My trivial EDMX has a base entity along with two entities that inherit from it...
<>-------- UserEntity
EntityBase
<>-------- CustomEntity
...this automatically creates three ObjectSet properties on the generated ObjectContext...
public ObjectSet<EntityBase> EntityBases {...}
public ObjectSet<UserEntity> UserEntities {...}
public ObjectSet<CustomEntity> CustomEntities {...}
...which is great. I can then use any of these three in my Linq To Entities. I actually want to use self tracking entities and so set the Code Generation Strategy to None. Then add the self tracking T4 templates. But the newly generaed ObjectContext only has the following...
public ObjectSet<CustomEntity> EntityBases {...}
...but the inherited entities of UserEntities and CustomEntities are nowhere to be seen! Can the self tracking entities T4 handle table per type? Surely it can? What gives?
That is correct behavior. You will get ObjectSet only for base type and this set is used to access all inherited types as well. If you want to get just UserEntity instances you will use .OfType<UserEntity>() extension method, etc.
Related
I have a class called GatewayClaims and a class called GatewayItems. And yes, the project I'm working on is a gateway.
I have several classes derived from GatewayItems: GatewayUser, GatewayCompany, GatewayRole and a few more. Each of these derived classes will hold claims. (Actually, just values. Simplified here.) And these claims gets passed forward to another service as a JWT token. This should work just fine.
But the problem is this:
public class GatewayClaim
{
public GatewayItem Item { get; set; } = new();
}
public abstract class GatewayItem
{
public List<GatewayClaim> Claims { get; set; } = new();
}
The "abstract" is part of the challenge here...
The problem is that I want separate tables for each item/claim pair so I have UserItems/UserClaims, CompanyItems/CompanyClaims, etc. So, preferably I would make the Claims type a generic class GatewayClaim<T> where T:GatewayItem, new() but then List<GatewayClaim> becomes invalid. And I don't weant to create a lot of derived classes just to support the various configurations that would be possible. I could use List<GatewayClaimValue<GatewayItem>> in GatewayItem which seems to work. But then I need to configure the DBSet and IEntityTypeConfiguration class for the various Claims tables and things become really messy by then.
So, I'm looking for an elegant solution to keep the amount of code to a minimum. And keep it readable!
To be clear: GatewayItem is NOT directly mapped to an entity, but a public class GatewayItemConfiguration<T> : IEntityTypeConfiguration<T> where T : GatewayItem is used to allow inheritance of basic configuration for any derived classes. This has an public virtual void Configure(EntityTypeBuilder<T> builder) method that gets overridden in the child configuration classes. Again, I'm trying to stay DRY in my code.
So the GatewayUser class uses a public class GatewayUserConfiguration : GatewayItemConfiguration<GatewayUser> {} class to configure the GatewayUser entity. I do the same way for a GatewayUserClaim which is derived from GatewayClaim at this moment. But the derived Claim types don't differ from their parent class, except the Items list is of a different type. Which is why I want to use GatewayClient<T> instead of GatewayClient.
I have several classes derived from GatewayItems: GatewayUser, GatewayCompany, GatewayRole
These are not closely-enough related to use inheritance in the database. If you want to have a common base class in code, simply don't map GatewayItem to an EF entity.
I want separate tables for each item/claim pair so I have UserItems/UserClaims
Great. Just introduce a UserClaim type, again perhaps inheriting from an unmapped Claim type, and it will map to a separate UserClaim table.
I have existing .net 4 application which is based on object context.
Now I'm adding DbContext in to existing application by inheriting DbContext and call constructor method and pass the existing object context. i.e.
public class DemoModelEntitiesDbContext : DbContext
{
public DemoModelEntitiesDbContext():base(new DemoModelEntities(), dbContextOwnsObjectContext:true)
{
}
public DbSet<ELMAH_Error> ELMAH_Error { get; set; }
}
Than When I call,
using (DemoModelEntitiesDbContext context = new DemoModelEntitiesDbContext())
{
foreach (ELMAH_Error entity in context.ELMAH_Error.ToList())
{
Console.WriteLine(entity.ID);
}
}
I am getting following error message,
"The type 'ObjectContextDemo.ELMAH_Error' 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."
I checked my existing entities are inheriting from EntityObject.
How to add DbContext in to existing application and not changing existing code?
You could cast to object context but then you gain nothing from DbContext if you 100% stick with existing code. but Ive done that with POCOs, not EntityObjects and can't confirm that it would work with entityobjects.
Otherwise it's not possible to do that without changes. DbContext does not support EntityObject. DbContext is designed for POCOs. Plus there are other code differences between DbContext and ObjectContext. You'd have to change even if you were already using POCOs. If you are using the EF designer, you'd have to start by using a different code gen template (ef5 DbContext template). But that will result in very different classes and plenty of changes to your EF calls in your app.
I'm using Entity Framework 5.0 with code-first approach plus inheritance for my business objects represented by Table Per Hierarchy.
I'd like to have the following structure:
//Assembly 'DataAccess'
public class MyDbContext : DbContext
{
DbSet<AbstractClass> CommonObjects.AbstractClasses { get; set; }
}
//Assembly 'CommonObjects'
public abstract class AbstractClass
{
//implementation
}
//Assembly 'DerivedObjects'
public class DerivedClass : AbstractClass
{
//implementation
}
During runtime, when trying to access the DbContext the first time, the compiler throws an InvalidOperationException saying:
The abstract type 'CommonObjects.AbstractClass' has no mapped descendents
and so cannot be mapped. Either remove 'CommonObjects.AbstractClass' from
the model or add one or more types deriving from
'CommonObjects.AbstractClass' to the model.
Is this scenario even possible? If yes, what am I doing wrong?
Thanks for your answers in advance.
Ben
Additional information:
Maybe I should be a bit more specific:
I got one assembly containing my abstract business objects (only abstractions). The concrete implementations (containing the logic) are kept in the responsible assemblies, as their logic depends upon other classes within that assembly. The issue is, I want to be able to store those conrete implementations in the persistance layer as well. But for that purpose, EF had to know those types in order to enable the mapping. But I dont want to make the persistance layer depend on my business logic layer - only the abstractions.
That's why I tried to add the derived objects to the DbContext directly from the Business Object Layer.
Example:
AbstractClass derivedClass = new DerivedClass();
MyDbContext.AbstractClasses.Add(derivedClass);
But then the exception above is being thrown. I just can't figure out a good structure to achieve this.
As it says on the tin, is it possible to create an instance of a DbContext suitable for use as an Entity Framework 5.0 POCO context where the properies that are normally declared as
public DbSet<T> Entities { get; set; }
aren't set/known until runtime?
I'd like to make a repository that has methods like
public TEntity Find<TEntity>(object key) where TEntity : class
{
return _context.Set<TEntity>().Find(key);
}
public void Update<TEntity>(TEntity entity) where TEntity : class
{
if (_context.Entry(entity).State == EntityState.Detached) //entity is detached
_context.Set<TEntity>().Attach(entity);
_context.Entry(entity).State = EntityState.Modified;
}
.... etc
And then use them like:
Widget w = repository.Find<Widget>(123);
repository.SaveChanges();
This is trivial if the repository's context is set to a class that contains a DbSet<Widget> Widgets, but can it be done such that the entity types that I plan to use won't be know until runtime OR possibly not until I actually USE them? So that if I have a new class Foo, I can immediately query my repository to .Find<Foo>(123) without having to first add a DbSet<Foo> Foos to my DbContext class?
I think this should be possible because there's nothing special about the poco classes or the DbContext instance which holds references to them.
You don't need DbSet<Foo> Foos property in your context. That is just one way to tell context about existence of the Foo entity. There are multiple ways how context discovers mapped entities:
By explicit DbSet<T> properties
By navigation properties in already discovered entities
By specifying mapping in DbModelBuilder
Overriding OnModelCreated in your context type
Creating DbModelBuilder manually, building it and compiling it into DbCompiledModel which can be passed to DbContext constructor
By declaring EntityTypeConfiguration<T> for each entity and adding it to DbModelBuilder (either in OnModelCreated or in manually created DbModelBuilder).
The last option can be used to discover all required entities at application startup (for example by searching assemblies for all entity configuration classes and registering them to model builder) but it is still not fully dynamic solution. Compiled model is normally constructed only once per application run when the context is used for the first time. Without replacing the compiled model you cannot add or remove mapped entity types to the context.
I've created a BLL which queries Entity Framework context. EDML file is in the same BLL assembly. It accepts EF entities as parameters from presentation layer and returns EF entities as results. To accomplish this I kept entities public so presentation layer can create them like DLL.TablName newRecord = new DLL.TableName() etc. The problem is with entities my objectcontext is exposed too since it's public. I can manually change it to private in designer generated code but if I make any changes to it it becomes public again. I didn't see any access modifier setting in the designer. How can I make ObjectContext private or internal?
I'm still in the layer design process so I can change my design if it can't be done.
You could probably inherit your Framework class and using the new keyword "hide" the ObjectContext like so:
public class DataContext : YourEFContext {
private new ObjectContext ObjectContext { get; }
}
I'm pretty sure you wanted something like this...