Eager loading in EntityFramework with DbContext.Database.SqlQuery - tsql

In EF 4, can I do eager loading of navigation properties by writing sql on DbContext.Database.SqlQuery or DbContext.Set<T>().SqlQuery? I don't seem to be getting my navigation properties populated.
Edit
It seems I can do eagerloading with DbContext.Set().SqlQuery, just not DbContext.Database.SqlQuery. Any idea why?

DbSet.SqlQuery works differently than Database.SqlQuery. The method on DbSet applies to the given entity set. It has to return entities of the given type and by default the returned entities will be tracked. Database.SqlQuery can return any object (possibly not an entity) and the returned objects are never tracked by the context. You may also want to take a look at msdn to compare both methods:
Database.SqlQuery - http://msdn.microsoft.com/en-us/library/gg679117(v=vs.103).aspx
DbSet.SqlQuery - http://msdn.microsoft.com/en-us/library/system.data.entity.dbset.sqlquery(v=VS.103).aspx

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.

MVC 5 Edit with ViewModel by reusing EF Generated Code

I have a Business Model and an EditBusinessViewModel.
In MVC 4 I would use code something like this to edit a record:
[HttpPost]
public ActionResult Edit(MainMenu mainmenu)
{
if (ModelState.IsValid)
{
db.MainMenus.Attach(mainmenu);
db.ObjectStateManager.ChangeObjectState(mainmenu, EntityState.Modified);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(mainmenu);
}
Now the auto generated code in MVC 5 looks like this, I've modified this Action to only include fields from my EditBusinessViewModel and named it Edit2:
[HttpPost]
[ValidateAntiForgeryToken]
[ValidateInput(false)]
public ActionResult Edit2([Bind(Include = "ID,BusinessName,BusinessDescription,BusinessAddress,BusinessPhoneOne,BusinessPhoneTwo,BusinessWeb,BusinessEmail,BusinessMelRef")] EditBusinessViewModel business)
{
if (ModelState.IsValid)
{
db.Entry(business).State = EntityState.Modified;
db.SaveChanges();
return Redirect("~/Home/Index/" + business.ID);
}
return View(business);
}
I have the Get part working, my Model and View are working by returning:
return View(new EditBusinessViewModel(business));
But when I post back, I get an error on this line:
db.Entry(business).State = EntityState.Modified;
The entity type EditBusinessViewModel is not part of the model for the current context. Which it is not and the reason for the ViewModel, I guess?
What I would like to know is can I use this code or is there something else I should be doing?
Update
I've been thinking about this too deeply and a ViewModel is just that, a ViewModel so now I have:
[HttpPost]
[ValidateAntiForgeryToken]
[ValidateInput(false)]
public ActionResult Edit2([Bind(Include = "ID,BusinessDescription,BusinessAddress,BusinessPhoneOne,BusinessPhoneTwo,BusinessWeb,BusinessEmail,BusinessMelRef")] EditBusinessViewModel business)
{
if (ModelState.IsValid)
{
business.UserEmail = User.Identity.GetUserName();
Business newbus = db.Businesses.Find(business.ID);
{
newbus.BusinessDescription = business.BusinessDescription;
newbus.BusinessAddress = business.BusinessAddress;
};
db.Entry(newbus).State = EntityState.Modified;
db.SaveChanges();
return Redirect("~/Home/Index/" + business.ID);
}
return View(business);
}
This way I post back the data I need from the view in the View Model, find the entity in the database by its matching ID and update it with the EF scaffold code.
Is there a better way?
Well, you won't be able to use your current code, for reasons I believe you're pointing to in your question itself. You're working with two different types, one that is mapped from a DB table and one that you are using specifically for your Views and is not mapped. Your Entity Model, you don't say which version of EF, but with MVC 5 I assume it's 6 or 6.1.
So you have your entity POCO generated by EF text template, and you have you ViewModel. Even if the properties were identical, EF would not take your ViewModel type, because it has no mapping definition in the edmx, this is the reason it says it's not in the current context as you've already recognized.
There are some decent ways to work in this system though. IF you desire to use separate entities and ViewModels, which I personally do in most of my own code. You could :
It seems like you have an ID, if that ID points to a unique ID on the EF Model, you could do a look up for an entity with that ID and then update the values of the entity with the values from your ViewModel and then save the entity with StateModified instead of the ViewModel.
If the properties are exactly the same or very similar, between your Model and ViewModel, you could look at something like AutoMapper, https://github.com/AutoMapper/AutoMapper, which would enable you to map your ViewModel directly to an instance of your entity Model Type.
If you're Model and ViewModel are vastly different, you could build a static tranform, not sure how many people do this but I like them. Essentially you define two static methods that enable you to convert your Model to your ViewModel and vice versa. The benefit is anywhere you need to do this you can call one method, and if the structure of either type changes you just have to update this in one location.
You say autogenerated code in MVC 5, You could mean just the default example code that comes with EF 5 but I think you're talking about MVC 5 Scaffolding. http://www.asp.net/visual-studio/overview/2013/aspnet-scaffolding-overview; if so, the code for these should not need much alteration at least not in the Controller side, unless you have specialty domain logic which it doesn't appear like you do. If you wanted to use separate ViewModels I suppose you could, in conjunction with one of the recommendations above, but the point of Scaffolding is to remove much of the plumbing that you have to do when exposing DB Models for basic CRUD methods.
If I've missed the mark on what you're looking for please reply in a comment. Also, it's a bit hard to provide code examples for the above recommendations without seeing the class definitions for your two models. I think the descriptions should be enough to go off of, if you think one will fit your use case well? But, if you'd like some simple code examples update your answer with the code for those classes and I can provide some.
From your posted snippet:
return View(new EditBusinessViewModel(business));
Here, business is not your ViewModel, but a variable (presumably your entity from the db) that is used in your ViewModel's constructor. I can only assume it's passed with the intent of storing it in one of your ViewModel's properties.
public ActionResult Edit2([Bind(Include = "...")] EditBusinessViewModel business)
Here, business is your ViewModel. It has the type EditBusinessViewModel, as you can see. But in that method you make the following call:
db.Entry(business).State = EntityState.Modified;
EditBusinessViewModel is not a type known by EF, since it is your viewmodel. You are supposed to pass your entity to the database. The ViewModel should only be used in your MVC project.
I'm pretty sure that one of the properties of your EditBusinessViewModel is the entity you need. This is vaguely confirmed by the fact that you pass your entity in the EditBusinessViewModel constructor.
I don't know what the property is called, since you didn't post the ViewModel's class. Assuming it's called MyEntity, this should do the trick:
db.Entry(business.MyEntity).State = EntityState.Modified;
But for clarity, I'd suggest renaming that parameter to prevent any confusion between separate uses of a business variable. Change it to businessVM or something similar so you're always reminded that you're working with a ViewModel, not an entity.

Entity Framework Code First and Multiple Assemblies

I have a subclass in a different assembly to its base class. The parent is a POCO class used for EF Code First.
When I try to add an instance of inherited class to the database I get InvalidOperationException: "Object mapping could not be found for Type with identity 'Foo.Bar.MyInheritedClass'".
It works fine if subclass is in same assembly as base class.
In regular EF the solution seems to be a call to ObjectContext.MetadataWorkspace.LoadFromAssembly(assembly). But I can't figure out how this relates to Code First.
Any advice?
I'm using Entity Framework 4.1 RC.
I solved this by inheriting from the first assembliy's DbContext, adding a DbSet<> for the derived class, and then adding new instances of derived type to to that.
Posted code on MSDN forum here.
I know this post is a bit old, but I was able to accomplish this using #Dave's recomendation inside the constructor:
public Context() {
((IObjectContextAdapter)this).ObjectContext.MetadataWorkspace.LoadFromAssembly(
System.Reflection.Assembly.GetAssembly(
typeof(--[Inherited DbContext]--)));
}
I'm quite new to EF (Entity Framework 4) and I got the same exception when I made changes in the model.
My problem turned out to be that I did not know EF need all the names on all the navigation properties to agree, not only their type. For example if there is a navigation property named foo, then there needs to be a declared variable in the corresponding class with the very same name.

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

In ADO.Net Data Services how do I check if an entity is already in the context?

I have an ADO.Net Data Service that I am using to do a data import. There are a number of entities that are linked to by most entities. To do that during import I create those entities first, save them and then use .SetLink(EntityImport, "NavigationProperty", CreatedEntity). Now the first issue that I ran into was that the context did not always know about CreatedEntity (this is due to each of the entities being imported independently and a creation of a context as each item is created - I'd like to retain this functionality - i.e. I'm trying to avoid "just use one context" as the answer).
So I have a .AddToCreatedEntityType(CreatedEntity) before attempting to call SetLink. This of course works for the first time, but on the second pass I get the error message "the context is already tracking the entity".
Is there a way to check if the context is already tracking the entity (context.Contains(CreatedEntity) isn't yet implemented)? I was thinking about attempting a try catch and just avoiding the error, but that seems to create a new CreatedEntity each pass. It is looking like I need to use a LINQ to Data Services to get that CreatedEntity each time, but that seems innefficient - any suggestions?
I think you should look at the EntityState property of your entity.
Only if it is of the value EntityState.Detached than you have to add it to your context.
Do not forget the following remark:
This enumeration has a FlagsAttribute
attribute that allows a bitwise
combination of its member values.
I would create a extension method:
public static class EntityObjectExtensions
{
public static Boolean IsTracked(this EntityObject self)
{
return (self.EntityState & EntityState.Detached) != EntityState.Detached;
}
}
When trying to check whether the context was tracking the entity that I wanted to update (or add) I was pretty disapointed when I found that the context.Entites.Contains(currentItem) didn't work.
I got around it using:
if (context.Entities.Where(entities => entities.Entity == currentItem).Any())
{
this.service.UpdateObject(currentItem);
}