InverseProperty with Database Migration doesn't work when property is protected or internal - entity-framework

I have the following Entity Models
public class User
{
public virtual long Id{get; set;}
[InverseProperty("Users")]
public virtual ICollection<Tag> Tags { get; protected set; }
}
public class Tag
{
public virtual long Id{get; set;}
internal protected virtual ICollection<User> Users { get; set; }
}
This is pure simple many-to-many relation User & Tag
I'm using Data Migrations. When I execute the command Add-Migration or Update-Database
I get the following error "The InversePropertyAttribute on property 'Tags' on type 'Kigg.DomainObjects.Entities.User' is not valid. The property 'Users' is not a valid navigation property on the related type 'Kigg.DomainObjects.Entities.Tag'. Ensure that the property exists and is a valid reference or collection navigation property."
When I changed the access modifier of Users property in Tag to public it worked fine and the generation is what I want.
From my design point of view I want to hide the Tag.Users property and make it protected or internal to keep it for internal use as I don't want to expose it to public API.
Note: I'm not discussing the my design here. I'm asking if it's possible to do that while Tag.Users is protected or internal?

I don't know how to make it work with data annotations but with Fluent API you can apply and experiment with the trick from here: http://blog.cincura.net/232731-mapping-private-protected-properties-in-entity-framework-4-x-code-first/
For your model it would look like the following:
public class User
{
public virtual long Id{get; set;}
public virtual ICollection<Tag> Tags { get; protected set; }
}
public class Tag
{
public virtual long Id{get; set;}
internal protected virtual ICollection<User> Users { get; set; }
public class PropertyAccessors
{
public static readonly Expression<Func<Tag, ICollection<User>>> Users
= t => t.Users;
}
}
Mapping in FluentAPI:
modelBuilder.Entity<User>()
.HasMany(u => u.Tags)
.WithMany(Tag.PropertyAccessors.Users);
This works and creates the expected many-to-many relationship.
But I am not sure if you can do anything useful with that navigation property. The fact that you have the property protected and virtual lets me guess that you basically want lazy loading support inside of the entity class or derived classes.
The problem is that apparently (according to my tests at least) lazy loading doesn't work for anything else than a public property. The loaded tag is a proxy but the navigation collection is always null (unless the property is public).
Moreover, even eager and explicit loading don't work:
Outside of the Tag class:
// Eager loading
var tag = context.Tags.Include(Tag.PropertyAccessors.Users).First();
// Explicit loading
var tag2 = context.Tags.First();
context.Entry(tag2).Collection(Tag.PropertyAccessors.Users).Load();
Or inside of the Tag class (some method in Tag which gets the context passed):
public DoSomething(MyContext context)
{
// Eager loading
var tag = context.Tags.Include(t => t.Users).First();
// Explicit loading
context.Entry(this).Collection(t => t.Users).Load();
}
In all cases I get an exception that the property Users on entity Tag is not a valid navigation property. (The exception disappears as soon as I make the property public.)
I don't know if adding/removing/updating relationships would work. (I doubt.)
It looks that you can map a non-public navigation property with this approach to generate the expected database schema. But it seems that there is nothing useful you can do with it.

I don't know much about EF5 but you can use the attribute InternalsVisibleToAttribute to make internal members visible to a specific assembly.

Related

How do you get a web API odatamodelbuilder to work with EF fluent API mappings

I have created a fairly simply domain model using pocos. I have mapped these to an EF DB context using EntityTypeConfiguration<TEnitityType> classes. This all works fine.
I am now trying to create an OData V4 WebAPI controller endpoint using a ODataConventionModelBuilder and this is where things are coming unstuck. It all works fine until it encounters an association that is not convention based. But I cannot find a way to get the ODataBuilder to pick up the mappings from my EntityTypeConfiguration<TEnitityType> classes.
This leaves my with 2 unpalatable options
Decorate my lovely clean pocos with dirty attributes.
Re-map all the non convention based mappings manually using the ODataBuilder
Not sure if code samples will help but here they are anyway, i have simplified the entities for brevity.
var builder = new ODataConventionModelBuilder();
builder.EntitySet<Item>("Items");
config.MapODataServiceRoute(
routeName: "odata",
routePrefix: "odata",
model: builder.GetEdmModel(),
batchHandler: new DefaultODataBatchHandler((GlobalConfiguration.DefaultServer)));
public class Item
{
public Int32 Id { get; set; }
public Int16 ItemTypeId { get; set; }
public virtual ItemType Type { get; set; }
public virtual ICollection<ItemVersion> Versions { get; set; }
public virtual ICollection<ItemTag> Tags { get; set; }
}
The problem comes when it encounters the ItemTags collection, here is an ItemTag:
public class ItemTag
{
public Int32 ItemId { get; set; }
public string Tag { get; set; }
public Item Item { get; set; }
}
Which you can see is not convention based and I have a configuration class for it as follows:
public class ItemTagConfiguration : EntityTypeConfiguration<ItemTag>
{
public ItemTagConfiguration()
{
HasKey(x => new {x.ItemId, x.Tag});
HasRequired(x => x.Item)
.WithMany(y => y.Tags)
.HasForeignKey(x => x.ItemId);
}
}
Does anyone know of a way that I can use these EntityTypeConfiguration files with an ODataBuilder or web API?
EDIT
If found this page which seems to indicate it might be possible with EF 6 which I am using. What I want to do is this
ODataModelBuilder modelBuilder = new ODataConventionModelBuilder();
modelBuilder.EntitySet<Dbf>("Dbfs");
// modelBuilder.Configurations.Add(new DbfMap()); <---- NO GOOD - Needs Class from DBContext we only have a model builder :(
Microsoft.Data.Edm.IEdmModel model = modelBuilder.GetEdmModel();
config.Routes.MapODataRoute("ODataRoute", "odata", model);
but the builder does not have a Configurations property.
Two things:
I have read multiple sources now that ward against using lazy loading and serialization; which is basically what OData is; (It even uses the system.runtime.serialization.datacontract and datamember attributes)
I have had more success in explicitly loading from context, and defining navigation properties in the modelbuilder for dbContext. I understand you are looking at customized nav properties, but I am fairly sure these are overriden methods useful for the ODataModelBuilder class (that does not assume much and needs less Entity Framework to work). Where you mentioned using EF already, I imagine that is the direction you will work, and if you do not need to alias your model names, you add an entry for each Set, using convention naming.
EntitySet("routePrefixName")
in building the EdmModel, and it wires up the relationships you made using Fluent previously. If you do have to add extraneous items to the underlying model, you should define each class as an EntityType<>(), only setting the key. EdmBuilder can use mild properties and key association to attach to the EF model in the ODataConventionModelBuilder.
I have wrestled and sought for some time, and there does not seem to be a wealth of information on .Net OData v4 floating around, probably due to the whole force datetimeoffset issue.
Hope that helps somewhat

Entity Framework table splitting - how to initialize lazy-loaded properties?

Using Entity Framework 6.0, I am attempting to implement table splitting to improve query performance on tables with columns that contain BLOB data. I have followed the recommendations in this tutorial and it does indeed work as described.
Here's a very simplified example of the entity classes that map to one of my split tables ...
public class MyEntity
{
public string Id { get; set; }
public virtual MyEntityContent Content { get; set; }
public string Name { get; set; }
}
public class MyEntityContent
{
public string Id { get; set; }
public virtual MyEntity Entity { get; set; }
public byte[] Blob { get; set; }
}
... and the corresponding configuration code in the associated DbContext implementation ...
modelBuilder.Entity<MyEntity>().HasKey(e => e.Id).ToTable("MyEntities");
modelBuilder.Entity<MyEntityContent>().HasKey(c => c.Id).ToTable("MyEntities");
modelBuilder.Entity<MyEntity>().HasRequired(e => e.Content).WithRequiredPrincipal(d => d.Entity);
Given that the lazy-loaded Content property is Required by Entity Framework, it seems sensible to initialize it to a default value in the constructor of the containing MyEntity class ...
public MyEntity()
{
Content = new MyEntityContent();
}
... which enables a new instance of the class to be created and partially populated, without the risk of an exception being thrown by forgetting to initialize the required property value:
var entity = new MyEntity {Id = "XXX", Name = "something"};
I typically use a similar technique to initialize collection properties on EF entities and it works fine. However, in the above scenario, this initialization in the constructor has an unexpected effect: when retrieving existing entity instances from the database, the database value in the lazy-loaded property is ignored in favor of the empty default value.
This seems illogical to me. Doesn't Entity Framework create an entity object by first calling its default constructor and then applying its own property values to the created instance? If so, this should overwrite my default Content property value with a new instance of MyEntityContent, based on database data. This is how it seems to work with lazy-loaded collection properties.
If it's not possible to do this in the way I am expecting, is there an alternative technique for initializing lazy-loaded properties?
Don't initialize virtual members and perhaps, if you have to, handle any exceptions from uninitialized members.
I just had this issue with an entity with two virtual fields. Originally I had it initialize those two, but after removing them (and initializing the other fields to some default value), it started working for me. Try it out and let me know!
[Edit] I just realized I replied this to a slightly old post, didn't see the date. I guess I'll leave this answer here in case.

asp.net mvc4 entity framework 4: Unable to create a Controller, and I've tried everything

I'm building a website using Visual Studio 2012 with MVC4 and Entity Framework 4.
I created all my model classes, with all their attributes (with some relationships between them). So far, my classes only have a bunch of attributes (no methods yet).
But whenever I try to create a controller for a class that has a collection in it, I get this error: Unable to retrieve metadata for 'model name'. Value cannot be null. Parameter name: key.
I've googled it and found different solutions, but none of them worked: I tried renaming all my primary key attributes to "Id" (they are also all annotated with [Key]), I also tried commenting out the constructor in the Data Context class, I checked my connection is named DefaultConnection...
This is one of the classes for which I'm being unable to create a controller:
public class JobOffer
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int JobOfferId { get; set; }
public Company Company { get; set; }
public OfferState State { get; set; }
public virtual ICollection<OfferApplicant> Applicants { get; set; }
public virtual ICollection<Requirement> Requirements { get; set; }
}
(I already tried without the [DatabaseGeneratedAttribute] annotation and it didn't help)
The OfferState class is an abstract class that has 2 subclasses: Open and Closed (they're there to implement a State pattern).
The OfferApplicant class maps 1 JobOffer with 1 Applicant, since that was a many-to-many relationships, so I created a new table.
The Requirement class is also an abstract class that has a few subclasses, like Age, Education, Area, etc., and also a CompositeRequirement, which has a public virtual ICollection Requirements { get; set; } collection (it's a Composite pattern).
The classes don't even have methods yet, all they have are their properties.
Any clues?
Thanks!!

DbContext and navigation properties in EF 4.4

After switching a project from the EntityObject generator to DbContext, I ran into an issue using some navigation properties on new objects. I've spent a significant amount of time researching the problem, and I'm no closer to a desirable solution.
First, the generated class definitions:
public partial class Category
{
public Category()
{
this.Limits = new HashSet<Limit>();
}
public int CategoryId {get; set;}
public string Name {get; set;}
public virtual ICollection<Limit> Limits { internal get; set; }
}
public partial class Limit
{
public int CategoryId {get; set;}
public string Description {get; set;}
internal virtual Category Category { get; set; }
}
I am creating test data during an integration test using the following code:
using (GFCAMDataContext db = new GFCAMDataContext())
{
limit = new Limit()
{
CategoryId = testData.CategoryId,
Description = "SignerController.Update"
};
db.Limits.Add(limit);
db.SaveChanges();
}
Without any other changes, the Limit.Category property of my newly-created Limit object does not return anything. However, if I query the desired Category from the DbContext before SaveChanges is called the navigation property on my new Limit starts returning the associated Category. With ObjectContext, the Category property is updated without any intervention from me.
I would like to have the same behavior as ObjectContext, but I can't seem to find a way to achieve this goal. I've seen a couple of proposed solutions:
Make the navigation properties public. This had the same behavior, and isn't desirable as the public navigation properties can cause issues during serialization and aren't needed outside of my business layer.
Make all properties public virtual and use DbSet.Create to ensure proxy creation. This resulted in the same behavior, and isn't necessarily desirable as I have code dynamically creating instances (i.e. I don't have access to a DbSet at the time I am creating an entity instance).
Does anyone have any suggestions for a solution to this problem?
One solution would be to explicitly load the nested entity:
db.SaveChanges();
db.Entry(person).Reference(z => z.Category).Load();
The other option, when proxies are enabled, is indeed to call DbSet.Create<T>(). If you don't have access to the DbSet instance at the time you create your entities, you might want to expose a public method in your repository interface that allows that. For example:
public interface IRepository<T>
{
T Add(T entity);
T GetById(...);
void SaveChanges();
...
T CreateInstance(); // Concrete implementation have access to DbSet and uses DbSet.Create<T>()
}

EF code first related entities not loading at all

I haven't been able to find someone else with this issue specifically so here goes.
I have a simple model where one entity simply references another as a parent-child or one-to-many relationship defined like this:
public class Parent
{
public int ID { get; private set; }
public string Name { get; private set; }
}
public class Child
{
public int ID { get; private set; }
public string Name { get; private set; }
public virtual Parent Parent { get; private set; }
}
I am creating speicific mapping files for each, which work great for all the normal properties except for the related entity. It is always coming up null. No matter whether i have the virtual/private accessors on the property it will not load UNLESS i pull a copy of the parent separately from the context first. My mapping looks like this:
HasRequired(t => t.Parent).WithMany().Map(t => t.MapKey("ParentID")).WillCascadeOnDelete();
Is there anything I am doing wrong with this? I cannot for the life of me figure this out. Just so I cover all the bases, I am loading the entity like this:
Context.Set<Child>().FirstOrDefault(x => x.ID == 1);
And lastly here are some constraints I have:
I cannot have the foreign keys in my model as properties.
I cannot have a collection of children from the parent.
I finally figured it out. After much trial and error I noticed that having a parameterless constructor marked as internal, EF cannot create its dynamic proxy class of your type and therefore disables all lazy loading. I have two contructors, one for EF to hydrate objects, and another with parameters requires for callers to create my entity. Once I changed the signature to protected internal it started working. So I changed this:
internal Child() {}
to
protected internal Child() {}
May be you hasn't enable lazy loading .Try this,
Context.Set<Child>().FirstOrDefault(x => x.ID == 1).Include(c=>c.Parent);