I have a parent entity (Catalog) with child entities (Category). I have no need to expose the navigation property of child entities:
public class Catalog
{
string _name;
List<Category> _categorys;
}
public class Category
{
string _name;
}
I want to include Categorys in a query on Catalog:
return await _context.Catalog
.Include(c => EF.Property<List<Category>>(c, "_categorys"));
But I get exception:
The expression 'Property(c, "_categorys")' is invalid inside an
'Include' operation, since it does not represent a property access: 't
=> t.MyProperty'. To target navigations declared on derived types, use casting ('t => ((Derived)t).MyProperty') or the 'as' operator ('t =>
(t as Derived).MyProperty'). Collection navigation access can be
filtered by composing Where, OrderBy(Descending), ThenBy(Descending),
Skip or Take operations.
I have configured the relationship on the Category as follows:
builder.HasOne<Catalog>()
.WithMany("_categorys")
.HasForeignKey("CatalogId")
.IsRequired()
.OnDelete(DeleteBehavior.Restrict);
Is it possible to 'Include' shadow navigation properties?
No, it's not possible because EF Core so far does not support shadow navigation properties.
But in the shown example _children is not shadow property (shadow properties are the ones that do not exist in the objects are maintained purely in the change tracker internal data structures, and are lost when the object goes out of the change tracker), but regular property/field, just not exposed publicly.
I'm not going to comment how many benefits of the ORM are you loosing by not exposing it, so answering your concrete question.
Yes, it's possible to eager load such properties (assuming they were mapped correctly through fluent configuration to the corresponding relationship). But EF.Property method is only for primitive (non navigation) properties. For navigation properties you have to use the Include overload receiving string argument and pass dot separated string containing the path to the navigation property. e.g.
return await _context.Catalog
.Include("_categorys");
Related
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.
I'm trying to migrate my old app to the new EF Core but I cannot find some relationships like:
HasRequired(o => o.Document).WithOptional(o => o.CancelNote);
Is there some extension methods? I cannot find on the docs.
The HasRequired I think that is possible to substitute with HasOne() method, but how about the WithOptional()?
Other thing, according to the docs the entity not uses the virtual keyword to create the navigation properties, how lazy load will work?
You will not find an HasOptional equivalent method in EF7. By convention if your FK property is nullable, you navigation property will be treated as optional
modelBuilder.Entity<Blog>()
.HasOne(p => p.Document)
.WithOne(i => i.CancelNote)
.HasForeignKey<Document>(b => b.CancelNoteForeignKey);
About your second question,EF Core (EF7) doesn't support Lazy Loading. In this link you will find the options you have now to load related entities
I have a code-first model where all entities are derived from a Entity base class. I have a property IsDeleted in base class which I want to ignore in all entities (I cannot remove/comment IsDeleted property since base class is used in many projects). Is there a way to configure modelBuilder to ignore this property form all entities (by conventions, I think), without to specify modelBuilder.Entity<...>().Ignore(l => l.IsDeleted) for all entities from my model?
Thanks,
Ion
You can do this using the new EF 6.1 Custom Code First Conventions:
modelBuilder.Types().Configure(c => c.Ignore("IsDeleted"));
This will ignore any property of the name IsDeleted in any of your types.
If you only want to do this for classes inheriting a certain base class, you can do:
modelBuilder.Types()
.Where(t => t.IsSubclassOf(typeof(MyBaseClass)))
.Configure(c => c.Ignore("IsDeleted"));
You can use the [NotMapped] annotation on the properties, but that will still need to be added for each entity which isn't the same as only specifying it once and having a convention for ignoring it.
I created a generic repository that handles querying my entities.
When I call this:
public IQueryable<TEntity> GetQuery()
{
return _context.Set<TEntity>().AsQueryable();
}
I get back the entire entity including all child entities.
When I call this:
public TEntity GetById(Guid id)
{
return GetQuery().Where(e => e.Id == id).FirstOrDefault();
}
I have to specify what child entities to include.
Is there a way to get back ALL child entities without having to write includes for each entity?
Lazy loading is enabled by default. This means that the collections will be loaded when you access them, not when you retrieve the parent object e.g.
foreach (var parent in repo.GetQuery()) {
foreach (var child in parent.Children) {
// do something
}
}
If you wish to eagerly load your entities you could subclass your generic repository and override the methods where you wish to use the Include lambda. Alternatively there is an Include method that accepts a string list of associations to include, which you could expose on your generic repository.
Update:
Not quite sure why you gave my answer -1 but as further clarification.
You stated regarding your GetQuery() method:
I get back the entire entity including
all child entities.
The child entities are lazily loaded, whether you access the collections in debug or output them on your page.
The single query should work, with lazy loading enabled.
And AFAIK, with lazy loading disabled, this doesn't mean that all the collections are loaded automatically, quite the opposite, you have to explicitly load them by calling Include.
It's one of the parameters supplied to the CreateMetadata method (which you override if extending metadata support).
ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes,
Type containerType,
Func<object> modelAccessor, <<--THIS ONE
Type modelType,
string propertyName)
I had assumed that it allowed you to access the model object itself (e.g. for setting metadata based on model values), however when I try to use it to cast to my model object I just get null.
Entity ent = (Entity)modelAccessor(); // = Null
If I've missunderstood, can anyone explain what it's purpose is? Or alternatively, how to properly use it?
Thanks
We originally had that as "object model", rather than "Func modelAccessor". We had to change it late in MVC 2's ship cycle.
The purpose is to delay retrieving the actual value of the model until such point as you know you're going to need it (that is, until you call ModelMetadata.Model).
The problem it solves is actually a rather esoteric one related to model binding against a LINQ to SQL class that has a foreign key reference in it. The problem is, if you've retrieved the child object which is represented by a foreign key relationship (which usually means a delay load of that object), then you're no longer allowed to choose a new child object by setting the foreign key ID property. It's very common to model bind the foreign key ID (and not the whole foreign key entity) when model binding, but if we'd retrieved the foreign key entity object (for the purposes of populating the ModelMetadata class) then that binding would no longer be legal, and actually throw an exception. Since ModelMetadata is used for both directions of models -- inbound, via model binding, and outbound, via HTML generation -- we needed to introduce the layer of indirection to protect your ability to use it in both scenarios without disrupting LINQ to SQL's rules.
The modelAccessor parameter does not point to an instance of the object, but rather it is a function that will access some attribute of your object. The Func "encapsulates a method that has no parameters and returns a value of the type specified by the TResult parameter." For example, if we have following class:
public class Bar(){
[DisplayName("I am Foo.")]
public string Foo{get;}
}
When the CreateMetaData is called, it will be to create meta data for the Foo property and the modelAccessor will be a function that returns the value of Foo.
I did a little digging and found a way to get to the instance of the object, but it requires using reflection. You can do the following to get the Bar class in my example:
if (modelAccessor != null)
{
//Use reflection to get the private field that holds the Bar object.
FieldInfo container = modelAccessor.Target.GetType().GetField("container");
//Invoke field on the modelAccessor target to get the instance of the Bar object.
Bar myObject = (Bar)container.GetValue(modelAccessor.Target);
}
I've only run this against a simple test case, so your mileage may vary, but hopefully this will help clarify what is going on.