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.
Related
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; }
}
When implementing DDD aggregates I usually have them expose methods that represent business functions and then those functions are responsible for modifying internal state. E.g.
public class Order
{
public int Id { get; private set; }
public IEnumerable<LineItem> LineItems { get; private set; }
public void AddLineItem(...) {...}
public void Ship() {...}
public void Cancel() {...}
}
When using EF6 or NHibernate this works well because the context/session can detect all insertions/deletes/updates of child entities. However, with EF7 I need to explicitly instruct the change tracker about such changes. E.g. when AddLineItem() adds a new line item to the LineItems collection, we must either manually add it to a DBSet on the context or inform the change tracker that the state for that line item was Added.
This means my domain objects must have an explicit dependency on Entity Framework and I must now somehow pass around references to the context (or change tracker) when calling business functions.
Is this something I should simply work around for the moment, on the basis that EF7 will ultimately have the same capabilities as EF6/NHibernate? Or is there a way to have rich domain objects without this dependency?
This can be solved by using dependency injection. This would allow separation of your domain objects from your ORM of choice (plus all the other benefits of dependency injection). There is no single, absolutely correct answer for how exactly you separate concerns in your code.
Related note: the behavior of .Add() will be changing in EF7 - beta 8 (See PR#2979). The change tracker will now include all "children" entities when you call .Add for a parent item.
I am using Entity Framework 5 with Code First approach and using Fluent API for Entity configuration. My project has one particular Product Entity which gets half of its data from the database and the other half from a Data Contract retrieved via a WCF Client (its a 3rd party system used to manage product inventory). The Data Contract is a member of the Product Entity class (property or method I haven't decided yet).
I prefer not to have any WCF Client logic contain within the Entities. I'd prefer to keep this logic in Repository code (DbContext, DbSet, etc.).
So is there a technique to hook into Entity Framework (or intercept) just after a Product Entity is retrieved from the database? I should note the Product Entity appears as a navigation property on other Entities. If a hook or intercept is possible then what that means is I can retrieve the Data Contract from the SOAP service immediately after EF loaded the Product Entity from the database. The benefit for my project is the WCF Client retrieval code does not need to be repeated throughout the application.
One idea I had was to implement IDbSet for the Data Contract and the IDbSet would be responsible for retrieving it. And then somehow trick EF into thinking its a navigation property on the Product Entity. But I wasn't sure if a database DbSet can be mixed with a non-database IDbSet all within the same DbContext. And also the other question - how would EF know to retrieve a navigation property from the IDbSet implantation? I'd prefer to know if this idea is possible before investing time into it. I'd also prefer to know where to start looking.
Please note I've been working with .NET for over 10 years but this EF5 stuff is still relatively new to me.
Thanks in advance.
-Sam
Today I found an event in the Entity Framework that seems to be what I am looking for. ObjectContext.ObjectMaterialized Event. Apparently, DbContext implements IObjectContextAdapter which in-turn exposes the ObjectContext. From there I can subscribe to the ObjectMaterialized event.
MSDN Reads:
Occurs when a new entity object is created from data in the data
source as part of a query or load operation.
The following code demonstrates how I used the ObjectMaterialized event to solve my problem in which one of my preferences was to have a central point to place the WCF client access logic.
// seperate assembly - does not use Domain.Repositories assembly
namespace Domain.Models
{
// the data contract
[DataContract]
public class ProductInventoryState
{
[DataMember]
public int StockStatus { get; set; }
[DataMember]
public IEnumerable<String> SerialNumbers { get; set; }
// etc....
}
// the entity
public class Product
{
public Guid Key { get; set; }
public string ProductCode { get; set; }
public ProductInventoryState InventoryState { get; set; }
// etc....
}
}
// seperate assembly - uses Domain.Models assembly
namespace Domain.Repositories
{
public class MainRepository : DbContext
{
public MainRepository()
{
((IObjectContextAdapter)this).ObjectContext.ObjectMaterialized += ObjectContext_ObjectMaterialized;
}
protected void ObjectContext_ObjectMaterialized(object sender, ObjectMaterializedEventArgs e)
{
if (e.Entity == null)
return;
if (e.Entity is Product)
{
Product product = (Product)e.Entity;
// retrieve ProductInventoryState from 3rd party SOAP API
using (ThirdPartyInventorySystemClient client = new ThirdPartyInventorySystemClient())
{
// use ProductCode to retrieve the data contract
product.InventoryState = client.GetInventoryState(product.ProductCode);
}
}
}
}
}
1.) You can write your own EF Provider (but that is no small task)
2.) You can attach items to the context but not save them.
The entity.State can be set as Not modified after attaching.
You could also remove such entries from Context prior to save changes
3) You can Write a repository fascade that check EF and Checks location 2 and combines the result.
On the question of navigation properties.
You would need to specify these very carefully to avoid issues. Not lazy loaded or not even modelled.
I wouldnt try and mix them personally.
You can tell EF to ignore some properties.
So you can have a Nice original POCO, but only model the bits that are on the DB.
The POCO would then collect the rest.
I use a fascade with events myself to act on KEY methods on a context/DBset.
So I can trigger events on attach , get, save etc.
good luck
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.
Starting from an EF 4 entity diagram and using T4 templates one can create POCO classes that can be used inside the Domain Model. The generated code looks like this:
public partial class Product
{
public virtual int Id
{
get;
set;
}
public virtual string Name
{
get;
set;
}
//and so on
}
Is there any elegant approach to add my own code for implementing the properties? for example, the Name setter I would like to be implemented by lowering all the characters. I would like that my code resist to repeated regeneration of the POCO classes from the EF diagram.
This requirement is somewhat similar to adding validation code to the POCO classes. This issue is already solved by creating a separate validation class and linking it to the POCO through the MetadataType attribute. That separate validation class is not overwritten by repeatedly regenerating POCOs from the EF diagram.
Thanks,
Lucian
No there is no easy way to do that. You must not touch generated code because your changes will be deleted after each regeneration. You options are:
Write entities yourselves and don't use generator - you will get full control over entity code
Modify T4 template in generator to either add your direct code (this can be quite hard to generalize) or simply add call to partial methods (you must also declare these methods in generator) in getter and setter and in your partial part of entity class implement partial methods you need.