Code First - persisting an object without specialized DbContext - entity-framework

Code First is a terrific feature. But I could not figure out how to persist an object without creating a specialized DbContext first. Is it possible at all? I'd like to see something along these lines:
var dbConn = new SqlCeConnection("Data Source=Test.sdf");
using (var db = new DbContext(dbConn, true))
{
var cmp = new Company { CompanyName = "Microsoft" };
db.Set(typeof(Company)).Add(cmp);
var recordsAffected = db.SaveChanges();
Console.WriteLine(
"Saved {0} entities to the database, press any key to exit.",
recordsAffected);
Console.ReadKey();
}
Is it possible to dynamically register a class for model creation? There must be a way!

No, as I know there is not any possibility to create DbContext without any information about mapping. Specialized context with predefined DbSets is necessary to define mappings and database initialization. You can probably use base DbContext only if you provide informations about mapping through its constructor by passing DbCompiledModel created manually before using DbContext but I haven't tried this yet.
The problem is that DbSets and overriden OnModelCreating are used to infer needed mapping. If you don't have DbSets or OnModelCreating defined DbContext can't infer and cache mapping. Mapping metadata are created and compiled only once for each context type (until application restarts). This operation is considered as very slow. If you don't have specific context type EF can't probably infer mapping and even if it can it will probably need to create and compile metadata for each instance of the context (like for anonymous types).
If you use DbCompiledModel created manually it will be your responsibility to reuse it.

Related

EF Core: Automaticall Save Enums as Strings Without Calling HasConversion For Every Single Property

Using EF Core I can tell the modelBuilder to save properties having of enum type as string:
modelBuilder
.Entity<MyEntity>()
.Property(e => e.SomeEnumProperty)
.HasConversion<string>();
This has been asked and answered several times and is also described in the official docs.
However, the list of entitiy types (modelBuilder.Model.GetEntityTypes()) and their subtypes used in my project is rather lengthy and I image it to be error prone to loop over all managed entities, get their properties and their children properties recursivly via reflection and kind of semi-manually add the string conversion.
Is there a builtin way to automatically save all enum property values as strings using the StringEnumConverter?
Currently (EF Core 3.1.7) there is no other way than the one described in EF CORE 2.1 HasConversion on all properties of type datetime.
The difference here is the way of identifying Enum type properties, and due to the lack of easy public way of getting entity type builder (this property builder), the direct usage of SetProviderClrType metadata API instead of more intuitive HasConversion:
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
{
foreach (var property in entityType.GetProperties())
{
var propertyType = Nullable.GetUnderlyingType(property.ClrType) ?? property.ClrType;
if (propertyType.IsEnum)
property.SetProviderClrType(typeof(string));
}
}
This must be at the end of your OnModelCreating override, or more specifically, after all entity types have been discovered.

The operation cannot be completed because the DbContext has been disposed in entity

private void FunctionA(int t)
{
var c=Context.Entities.TableStudent.Where(x => x.StudentBranch ==t).ToList();
using(var db=Context.Entities)
{
foreach (var q in c)
{
var cb=Context.Entities.Student...
.
.
.
}
DbContext.Entities.SaveChanges();
}
}
c.ForEach(a => a.Situation=true);
DbContext.Entities.SaveChanges();
}
I take DbContext.Entities.SaveChanges(); line give error.This error is
The operation cannot be completed because the DbContext has been disposed.
How can I do it this error.
.
Ok, first up, continue reading/learning about IDisposable, using() blocks, and also naming conventions. You aren't doing yourself any favors by making your code more difficult to understand trying to remember what "a", "c", etc. are, just to save a few seconds typing out a meaningful name.
I'm rather surprised that the code you pasted actually would compile, but without knowing what "Context" is vs. "DbContext" (namespaces, static classes??)...
You're going to have a class which extends EF's DbContext, I'm going to call it "MyContext".. I.e.
public class MyContext : DbContext
{
}
Inside of this class you'll have DbSets declared, and likely an overridden method OnModelCreating() to handle any non-trivial configuration for your entities.
public class MyContext : DbContext
{
public DbSet<TableStudent> Students{ get; set; }
}
This class should never be marked as "static".
Then, with your code to manipulate students, related entities and/or other entities where you have DbSets in the DbContext, you will want to scope the lifespan of a DbContext and ensure that all operations against those entities happens within that lifespan. This lifespan is bounded by a using() block. Once the code leaves the using block, the DbContext is disposed. This means any lazy load references made by entities will not work.
using (var myContext = new MyContext())
{
var students= myContext.Students.Where(x => x.StudentBranch == studentBranch).ToList();
foreach (var student in students)
{
// .. logic...
student.Situation = true;
}
myContext.SaveChanges();
}
// After this point, it is unwise/unsafe to "use" any reference to students.
Do what you need to do within the scope of the using block. If you need to pass student data outside, such as to return from a method call, copy the values over to a plain 'ol C# object (ViewModel or DTO) and return that. Accessing Entities outside of the DbContext scope will result in errors because the context that the student was loaded under has been disposed. Even in cases where the scope is kept alive (such as using a Static context [bad!] or scoping the context to the web request with an IoC container, you may avoid errors, but introduce unintended performance problems due to lazy loading.
SaveChanges is something that typically only needs to ever be called once within a lifetime scope of a DbContext. When set up to know the relationships between entities, EF will manage associating things like Foreign Keys between entities, even new entities that you create. One common panic point people reach is a chicken & egg scenario where I want to create an entity with children, but the children need the parent ID which won't exist until SaveChanges gets called. As long as the parent and child relationship is mapped properly, EF will resolve this automatically when SaveChanges is called provided the children were added to the parent's children collection. SaveChanges applies to the whole set of operations against entities the DbContext knows about (and their relations) so it's not applied at an entity by entity basis.
This should hopefully get you started on how to incorporate Entity Framework and working with it's disposable nature. DbContexts are designed to be relatively short-lived, constructed and disposed as needed. Normally these will be scoped to live as long as a unit of work, being a web request / action, or similar. Longer-lived DbContexts will result in performance/resource issues due to their tracking and caching nature. (Plus issues when attempting to scope SaveChanges calls, discarding changes, etc.)

Adding DbContext into existing .net 4 application

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.

Entity Framework v4 POCO templates: repository returns object of incorrect type

I've just implemented a repository based on EFv4 POCO entity templates.
When I do this
public Client Load(Guid firmId,
int prettyId)
{
var client = (from c in _ctx.Clients where c.firm_id == firmId && c.PrettyId == prettyId select c).FirstOrDefault();
return client;
}
the client returned is of type
{System.Data.Entity.DynamicProxies.Client_8E92CA62619EB03F03DF1A1FC60C5B21F87ECC5D85B65759DB3A3949B8A606D3}
What is happening here? I thought I would get rid of any reference to types from System.Data.Entity namespace. The returned instance should be of type Client, which is a simple POCO class.
I can confirm that the solution is to set
context.ProxyCreationEnabled = false;
which disables creation of dynamic proxy typed objects and leaves us with simple POCOs, which is what we were after with EF POCO templates in the first place.
But you lose lazy loading of navigation properties and change tracking on entities. For the first, you either have to use context.LoadProperty() or the Include() method on your ObjectQuery object. For the second, I do not know the solution yet (actually it doesn't really make sense to have change tracking on POCOs).
Also here is a similar question I would like to point out
What are the downsides to turning off ProxyCreationEnabled for CTP5 of EF code first
I agree that Mare's answer is correct. However, I would add a note of caution.
If you run a query without this ProxyCreationEnabled setting set to true, then EF will return DynamicProxies. If you subsequently run a query with the setting set to false, then EF will return the cached DynamicProxies objects, regardless of the ProxyCreationEnabled setting.
This can be configured globally for the EF context in the *Model.Context.tt file in *Model.edmx under
if (!loader.IsLazyLoadingEnabled(container))
...
this.Configuration.LazyLoadingEnabled = false;
this.Configuration.ProxyCreationEnabled = false;
These will be added to the *Model.context.cs generated file, and will persist between updates from the Database.
I prefer this setting as I do not want a child object that matches the parent loaded from the database.
ALT: It can be configured for Json serizialization:
JSON.NET Error Self referencing loop detected for type

RIAServices unsupported types on hand-built DomainService

My EF model was generated from my SQL Server database. I then generated a DomainService for RIAServices against the EF model. One of the entities is called "EntryCategories". The DomainService created this method:
public IQueryable<EntryCategories> GetEntryCategoriesSet()
{
return this.Context.EntryCategoriesSet;
}
Since my user interface display model looks quite different from the physical model, I decided to write my own DomainService for that and related entities. Yes, I know we are meant to modify the generated one but it has so much stuff in there and I wanted to focus on a small thing.
I removed the EnableClientAccess attribute from the generated DomainService and added a new class called ClientDomainService, and encapsulated in it the generated DomainService:
[EnableClientAccess()]
public class ClientDomainService : DomainService
{
// the generated domain service encapsulated in my new one.
private DataDomainService _dcds = new DataDomainService();
// reimplement one of the DataDomainService methods
public IQueryable<EntryCategories> GetEntryCategories()
{
return (from t in _dcds.GetEntryCategoriesSet() where t.EntryCategoriesVersions.EntryCategoriesVersionId == datahead.EntryCategoriesVersions.EntryCategoriesVersionId orderby t.DisplayOrder select t);
}
}
The very fist thing I tried is to reimplement the GetCateogoriesSet method but with the underlying data filtered based on another entity in my class (not shown). But when I build this, an error shows up:
Entity 'DataProject.Web.EntryCategories' has a property 'EntryCategoriesVersionsReference' with an unsupported type
If I comment out my CientDomainService, replace the EnableClientAccess attribute on the generated DomainService, and place the analagous linq filtering in the original GetEntryCategoriesSet method, the project compiles with no errors.
What is so special about the generated DomainService that my new one doesn't have? Is it that metadata.cs file?
What's special about the generated domain service is not the .metadata.cs file (you can keep it, and use it, but it doesn't solve your problem).
The problem appears somehow because RIA services (?) needs a 'domain service description provider' for the exposed Linq to EF entities. The LinqToEntitiesDomainService class has the LinqToEntitiesDomainServiceDescriptionProviderAttribute, already applied, so the generated domain services which inherit from it also inherit the provider.
When you build your own custom domain service, derived from DomainService, and expose entities through it, you need to apply this attribute yourself. Furthermore, since the provider cannot infer the object context type from the domain service base class (which it can and does if the base class is LinqToEntitiesDomainService), you need to specify the object context type in the attribute constructor, like this:
[EnableClientAccess()]
[LinqToEntitiesDomainServiceDescriptionProvider(
typeof(YourObjectContextType))]
public class ClientDomainService : DomainService
{
...
}
That should fix it.
Note that this means if you had hoped to abstract your object context away from your domain service, you'll be disappointed. I had opted for the seemingly popular repository model where all code that operates on the object context goes into a provider used by the domain service. This facilitates unit testing, but evidently doesn't remove the domain service's dependency on the object context. The context is required for RIA Services to make sense of your entites, or at least those referenced by the domain entity (such as EntryCategoriesVersions in your case).
If you want to expose a specific entity on a domain service you will have to provde at least one query method for it. This is also required when the entity is only accessed as a child of another entity.
In this case you need to add the EntryCategoriesVersions entityset to the domain service, to get the scenario working correctly.
What type is EntryCategoriesVersionsReference ? Try adding a [DataContract] annotation against the type, and appropriate [Key] and [DataMember]. It should help with marshalling.
For me, the fix for this error was to add a default constructor to the return type.
In OP's example, the property 'EntryCategories.EntryCategoriesVersionsReference' needs to be of a type with a default constructor.