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.
Related
I'm currently working on an MVC-project that should be highly modular. For example I want to have a user-module, a menu-module and a page module.
Because the modules need to be highly re-usable in different visual studio solutions I create separate projects for each module.
For the database mapping I would like to make use of the entity framework. I've created a separate DbContext in each module-project. Each DbContext contains the entities associated with the module.
Unfortunately I'm not able to let EF create foreign keys between entities in different modules/dbContexts.
For example:
Core module contains User-Entity
Page module contains Page-Entity which has an author that links to the User-entity defined in the core-module dbContext.
Has anyone an idea how I can create foreign keys across modules/dbContexts?
Are all of your entities in the same database? I would suggest separating your assemblies like this:
Data - project containing your Entity Framework model and/or class/entity definitions (depending on which type of EF approach you are using).
Service - project containing interfaces and classes that manipulate your data. Example, for your User entity (and related items), you might have this:
public interface IUser : IDisposable
{
Data.User Get(int userId);
IQueryable<Data.User> GetAll();
//other method definitions for User entity CRUD
}
Then, you implementation:
public class User : IUser
{
private readonly DataEntities _dataContext = new DataEntities(); //this is from your EF Data assembly
public Data.User Get(int userId)
{
return _dataContext.Users.FirstOrDefault(u => u.UserId == userId);
}
public IQueryable<Data.User> GetAll()
{
return _dataContext.Users;
}
//other method implementations
public void Dispose()
{
_dataContext.Dispose();
}
}
Then, reference both your Service and Data assemblies in your module projects.
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.
For example, I have following DbContext classes.
public class AppDbContext : DbContext {
...
}
public class LogDbContext : DbContext {
...
}
public class FooDbContext : DbContext {
...
}
If a connection string named AppDbContext is on the App.Config and I want other DbContext classes to share the same connection string as AppDbContext, could I just pass the string "AppDbContext" as the parameter for the ctor of LogDbContext and FooDbContext. For example,
public class FooDbContext : DbContext {
public FooDbContext : base("AppDbContext") { }
}
Does it have any side effects ?
Update 2013/1/9
After trying #ShinH2S's suggestion and somethings, I give up this way, and decide give different Dbcontext derived classes with different connectionStrings and database. I have try a test project and put it on GitHub. It will throw a runtime exception when the entityframework detects the database scheme is changed because the AppDbContext and FooDbContext have different schemas. If I assign a DropCreateDatabaseIfModelChanges strategy to both DbContext derived classes, one of them will be dropped because the models is different to another.
Update 2017/10
This is an old problem. In my memory, EF6 and above versions can have different migration history for multiple context in the same migration table.
I prefer this answer at SO. I had not been coding with C# about 2 years.
IMHO, there is no side effects. But if it was me I will just create a base class that inherits from DbContext class BaseDbContextthen all the contexts (AppDbContext, LogDbContext and FooDbContext ) will derive from BaseDbContext.
I am just starting out on Entity Framework 4.1 Code-First. I have created my classes and DbContext, and they work perfectly fine. Right now I want to bind my ListView to my Entities, with the help of an EntityDataSource, but unfortunately it does not recognise any available connection strings! I think the providerName must be System.Data.EntityClient for it to work, but I have no concrete entity model to reference to...
I have read that an ObjectContext can be "adapted" from the DbContext, which in turn can be used to create an ObjectDataSource. I want to use my DbContext to bind to my ListView, however. Is there any way I can do this?
I have a hard time understanding your question... You want to specify the connection string when you instanciate your Context class, is that it?
You can create an overload of your constructor of your DbContext class, like
public MyContext(string connString) : base (connString)
{
Database.SetInitializer(...
...
}
Then, in a Code-First approach, you don't really need the ObjectContext except for super-advanced scenarios, and databinding isn't one of them I guess. To get to bind to a collection in your Context class, just put a property for it in a ViewModel class designed for your screen, like
public class MyViewModel
{
private MyContext _context;
public ObservableCollection<MyObject> MyObjects { get; set; }
public MyViewModel()
{
_context = new MyContext();
MyObjects = new ObservableCollection<MyObject>(_context.MyObjects.ToList());
}
}
Then you can bind your ListView against that property, given that it's referenced.
Hope it helps, otherwise please give more details.
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...