`DbSet<T>.Find` returns "incomplete" `T` - entity-framework

Currently, we are using Where followed by First to fetch items from the DB by key:
DbSet<DerivedResource> dbSet = GetDBSet();
DerivedResource resource = dbSet.Where(r => r.Guid == guid).Include(r => r.Data).First();
This works, this takes a long time. I assume that the call is O(n) and that there is no magic taking place behind the scenes to optimize the call. So, in the attempt to speed things up, I used this instead:
DerivedResource resource = dbSet.Find(guid);
This is quick, but results in resource missing some data: the Data property is null. How can I either fill in this data, or otherwise quickly retrieve an item by key?
Following is a sketch of the classes I'm using. I suspect that the fact that DerivedResource : BaseResource doesn't matter:
[Table("DerivedResource")]
public class DerivedResource : BaseResource<DerivedResource>
{
public BaseData Data { get; set; }
}
[Table("DerivedResource_BaseData")]
public abstract class BaseData
{
[JsonIgnore]
[Required]
[ForeignKey("DerivedResourceGuid")]
public DerivedResource DerivedResource { get; private set; }
[JsonIgnore]
[Key]
[Required, Column("DerivedResourceID")]
public Guid DerivedResourceGuid { get; set; }
}
[Table("DerivedResource_BaseData_DerivedData")]
public class DerivedData : BaseData { }
public abstract class BaseResource<T>
{
[Key]
[Column("ID")]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Guid { get; set; }
public virtual void DoSomething<T>(T t) { }
}
I am new to Entity and would really appreciate the help. I am using EF 6. (If anything is particularly easier with EF Core, I wouldn't mind hearing.)

Related

Multiple Common Fields CreatedOn and CreatedBy in every table of a database. How it can be without repeating for every table

Scenerio:
public class Department
{
public int DepartmentId { get; set; }
public string DepartmentName { get; set; }
public string Description { get; set; }
public DateTime CreatedOn {get; set; }
public string CreatedBy {get; set; }
}
public class TestItem
{
public int TestItemId { get; set; }
public string TestItemName { get; set; }
public Department Department { get; set; }
public int DepartmentId { get; set; }
public DateTime CreatedOn {get; set; }
public string CreatedBy {get; set; }
}
public class Patient
{
public int PatientId { get; set; }
public string PatientName { get; set; }
public DateTime CreatedOn {get; set; }
public string CreatedBy {get; set; }
}
the problem is that, every time I create a table I have to add those two columns repeatedly.
But I want like this-
public class EntryLog
{
public int EntryLogId { get; set; }
public DateTime CreatedOn {get; set; }
public string CreatedBy {get; set; }
}
public class Department
{
public int DepartmentId { get; set; }
public string DepartmentName { get; set; }
public string Description { get; set; }
public EntryLog EntryLog { get; set; }
public int EntryLogId { get; set; }
}
and so on...
class A { .. }
class B { .. }
But its creating problem [showing conflicts error with other table's foreign key] while creating a row for a Department or a Patient.
In EF core, there is Table Per Hierarchy (TPH) but in that case every table will be merged into a single table. But that doesn't give me any solution.
looking forward to expert's suggestion...
The bottom line is: use EntryLog as a base type and don't tell EF about it. It's easy enough to keep EF-core oblivious of the base type: only register the derived types. Doing so, EF-core will map your subtypes to their own tables, just as if they didn't have a common type.
Now EntryLog will no longer need an Id, and it should be abstract:
public abstract class EntryLog
{
public DateTime CreatedOnUtc { get; set; }
public string CreatedBy { get; set; }
}
Whether this is enough depends on your specific requirements. There are several possibilities.
1. No additional configuration
If you're happy with the default conventions EF will apply to the common properties, your done. CreatedOnUtc will be mapped to a DateTime2 column (in Sql Server) and CreatedBy to an nvarchar(max) column in each table for an EntryLog entity.
However, if you do need custom configurations --for example if you want to map CreatedBy to an nvarchar(50) column-- additional mapping instructions should be applied. And of course you still want to do the mapping of the common properties only once --which would also happen if you did map the base type in a TPH scheme. How to do that?
2. Data annotations in the base type
The easiest option is to add data annotations:
public abstract class EntryLog
{
public DateTime CreatedOnUtc { get; set; }
[MaxLength(50)]
public string CreatedBy { get; set; }
}
And that's all.
But there are dev teams that don't want to use data annotations for mapping instructions. Also, EF's fluent mappings offer more options than data annotations do. If data annotations don't fit the bill for whatever reason, fluent configurations must be applied. But still, you only want to configure the common properties only once. A viable way to achieve that is to use IEntityTypeConfigurations for each EntryLog and let each concrete configuration derive from a base class. This offers two more options.
3. The base class contains regular properties
Option 4 will make clear why I talk about "regular properties" here. This is what it looks like:
abstract class EntryLogConfiguration
{
public void ConfigureBase<TEntity>(EntityTypeBuilder<TEntity> builder)
where TEntity : EntryLog
{
// Just an example of how to configure a base property.
builder.Property(e => e.CreatedBy).HasMaxLength(50);
}
}
class DepartmentConfiguration : EntryLogConfiguration,
IEntityTypeConfiguration<Department>
{
public void Configure(EntityTypeBuilder<Department> builder)
{
builder.Property(p => p.DepartmentName).HasMaxLength(100);
ConfigureBase(builder);
}
}
And in the context:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.ApplyConfiguration(new DepartmentConfiguration());
}
4. Using shadow properties
Shadow properties is a new feature of EF-core.
Shadow properties are properties that are not defined in your .NET entity class but are defined for that entity type in the EF Core model. The value and state of these properties is maintained purely in the Change Tracker.
Let's suppose you want to have CreatedBy as a class property (because you want to show it in a UI) but only need CreatedOnUtc as a property that's set in the background and that shouldn't be exposed. Now EntryLog will look like this:
public abstract class EntryLog
{
public string CreatedBy { get; set; }
}
So the property CreatedOnUtc is gone. It has been moved to the base configuration as shadow property:
abstract class EntryLogConfiguration
{
public void ConfigureBase<TEntity>(EntityTypeBuilder<TEntity> builder)
where TEntity : EntryLog
{
builder.Property(e => e.CreatedBy).HasMaxLength(50);
builder.Property<DateTime>("CreatedOnUtc");
}
}
Now you can't set CreatedOnUtc directly, only through EF's change tracker. The best place to do that is in an override of SaveChanges in the context:
public override int SaveChanges()
{
foreach (var entry in ChangeTracker.Entries<EntryLog>())
{
entry.Property<DateTime>("UpdatedOnUtc").CurrentValue = DateTime.UtcNow;
}
return base.SaveChanges();
}
Of course, if UpdatedOnUtc was a regular property, this override would also come in handy, but you could just do
entry.Entity.CreatedOnUtc = DateTime.UtcNow;
I hope this will give you enough food for thought to figure out which option suits you best.

Relate Multiple Tables to Single General Purpose Table in Entity Framework Code First

Many times I have a general purpose entity that other entities contain a collection of. I don't want to have a new collection entity for each parent entity type that needs it but would like to re-use a single general purpose entity. For performance reasons, I also don't want to explicitly define many-to-many relationships as in this answer. The simplest example would be a collection of strings.
public class MyString
{
public Guid Id { get; set; }
public string Value { get; set; }
}
public class MyEntity
{
public Guid Id { get; set; }
public virtual List<MyString> { get; set; }
}
public class MyOtherString
{
public Guid Id { get; set; }
public string Value { get; set; }
}
public class MyOtherEntity
{
public Guid Id { get; set; }
public virtual List<MyOtherString> { get; set; }
}
I'd really like to combine MyString and MyOtherString into a single entity:
public class GeneralPurposeString
{
public Guid Id { get; set; }
public string Value { get; set; }
}
public class MyEntity
{
public Guid Id { get; set; }
public virtual List<GeneralPurposeString> { get; set; }
}
public class MyOtherEntity
{
public Guid Id { get; set; }
public virtual List<GeneralPurposeString> { get; set; }
}
Except now I'm going to have an additional foreign key in GeneralPurposeString for every entity that contains a collection of GeneralPurposeString.
What I would like would be a way to have an additional parent category column on the GeneralPurposeString table (but not the entity) that would specify which entity the item belongs to. I use Guid for primary keys, so the tables could look something like this:
CREATE TABLE [GeneralPurposeString]
(
[Id] uniqueidentifier NOT NULL
CONSTRAINT PK_GeneralPurposeString PRIMARY KEY,
[ParentEntityCategory] uniqueidentifier NOT NULL,
[ParentEntityId] uniqueidentifier NOT NULL,
[Value] nvarchar(MAX)
)
And some how in Code First to specify that MyEntity has a certain category, and that it's collection of GeneralPurposeString uses that category, and MyOtherEntity uses another category (Guid) for it's collections of GeneralPurposeString.
The key would be that GeneralPurposeString could be a collection in any other entity and that loading the parent entity and including the collection would automatically load without having to explicitly specify the category.
The purposes for all of this are
Allow .NET code to have GeneralPurposeString code that wasn't replicated everywhere (actual utility or business logic code). This can probably also be accomplished through inheritance and explicit mapping but that would still leave multiple tables in the database (see #2).
Have only one table in the database for GeneralPurposeString. This is more of a tidiness issue. Performance would possibly be better with multiple tables, but indexing on ParentEntityCategory/ParentEntityId and covering Value should be good performance for lookups.
Not have to explicitly code this relationship and the lookups everywhere it's needed.
I'm thinking if I can get over #2 and be OK with a separate table behind the scenes and implementing a derived class, that will be the simplest route to go.
So just:
public class GeneralPurposeString
{
public Guid Id { get; set; }
public string Value { get; set; }
}
// It's just a GeneralPurposeString with a fancy MyEntity membership pin
public class MyEntityString: GeneralPurposeString {}
public class MyEntity
{
public Guid Id { get; set; }
public virtual List<MyEntityString> Strings { get; set; }
}
// Cool GeneralPurposeStrings belong to MyOtherEntity
public class MyOtherEntityString: GeneralPurposeString {}
public class MyOtherEntity
{
public Guid Id { get; set; }
public virtual List<MyOtherEntityString> Strings { get; set; }
}
public class MyContext: DbContext
{
public DbSet<MyEntity> MyEntities { get; set; }
public DbSet<MyOtherEntity> MyOtherEntities { get; set; }
}
I don't have to add the derived classes to the DbContext and the tables get named with the plural of the derived class by default, so it's actually pretty straight forward.
My previous train of thought with the Parent Category would require additional coding/annotation even if EF supported it. This uses purely convention and nothing extra needed in annotations or in OnModelCreating().
I'm not seeing any harm in extra tables at this point in time. I don't see a need (currently) to have all of the data in one table for reporting, but that really depends on the type of general purpose entity, so I may need to revisit this in the future, or I may just take the many-to-many route if I do need the data in one table.
And I can still have:
public static class GeneralPurposeStringExtensions
{
public static void SassThatHoopyFrood(this GeneralPurposeString s)
{
// do stuff
}
}

Lazy loading in Entity Framework

I have an issue with the lazy loading behavior in EntityFramework 5. Here are my two models
public class Person {
[Key]
[Required]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
public int? OfficeID { get; set; }
[ForeignKey("OfficeID ")]
public virtual Offices OfficeID_Offices { get; set; }
}
public class Offices
{
[Key]
[Required]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
//Navigation Properties
public virtual ICollection<Person> Person_OfficeID { get; set; }
}
Then I have the following function in my Controller
[HttpPost]
public Person Read(int intID)
{
Person objData = (from obj in objDB.Persons
where obj.ID == intID && !obj.Deleted
select obj).FirstOrDefault();
}
This controller method is called through a jquery $.Ajax call, which returns a JSON object. Since my foreign key OfficeID_Offices is virtual, I'd expect to only be loaded when I demand it explicitely. However when I look at my returned JSON Object, I can see that the entire Offices object is returned as well.
Lazy loading seems to be enabled in my DbContext, so I'm wondering how I could avoid having the whole Office object returned.
Thank you!
Serialization of the entity object(s) accesses the property which triggers lazy loading. To disable lazy loading, set the objDB.Configuration.LazyLoadingEnabled property to False

Problems using TPT (Table Per Type) in EF 4.2 and deletion of parent objects

From what I understand on several posts the TPT architecure, with EF, does not create the necessary ON DELETE CASCADE when using a shared primary key.... It was also said that the EF context will handle the proper order of deletion of the sub-classed tables (however I do get an error that it breaks the constraint and that I can fix it with adding the ON DELETE CASCADE on the sub-class table)...
more background info...
I have a Section class, which has a number, title, and a list of pages. The page is designed using a super class which holds basic page properties. I have about 10+ sub-classes of the page class. The Section class holds an ICollection of these pages. The DB is created properly with the exception of no ON DELETE CASCADE on the sub-classed tables.
My code will create the entities and adds to the DB fine. However, if I try to delete a section (or all sections) it fails todelete due to the FK constraint on my sub-class page table...
public abstract BaseContent
{
... common properties which are Ignored in the DB ...
}
public class Course : BaseContent
{
public int Id {get;set;}
public string Name {get;set;}
public string Descripiton {get;set;}
public virtual ICollection<Chapter> Chapters{get;set;}
...
}
public class Chapter : BaseContent
{
public int Id {get;set;}
public int Number {get;set;}
public string Title {get;set;}
public virtual Course MyCourse{get;set;}
public virtual ICollection<Section> Sections{get;set;}
...
}
public class Section : BaseContent
{
public int Id {get;set;}
public int Number {get;set;}
public string Title {get;set;}
public virtual Chapter MyChapter {get;set;}
public virtual ICollection<BasePage> Pages {get;set;}
...
}
public abstract class BasePage : BaseContent, IComparable
{
public int Id { get; set; }
public string Title { get; set; }
public string PageImageRef { get; set; }
public ePageImageLocation ImageLocationOnPage { get; set; }
public int PageNumber { get; set; }
public virtual Section MySection { get; set; }
...
}
public class ChapterPage : BasePage
{
public virtual int ChapterNumber { get; set; }
public virtual string ChapterTitle { get; set; }
public virtual string AudioRef { get; set; }
}
public class SectionPage : BasePage
{
public virtual int SectionNumber { get; set; }
public virtual string SectionTitle { get; set; }
public virtual string SectionIntroduction { get; set; }
}
... plus about 8 other BasePage sub-classes...
public class MyContext: DbContext
{
...
public DbSet<Course> Courses { get; set; }
public DbSet<Chapter> Chapters { get; set; }
public DbSet<Section> Sections { get; set; }
public DbSet<BasePage> Pages { get; set; }
...
}
.. Fluent API ... (note Schema is defined to "" for SqlServer, for Oracle its the schema name)
private EntityTypeConfiguration<T> configureTablePerType<T>(string tableName) where T : BaseContent
{
var config = new EntityTypeConfiguration<T>();
config.ToTable(tableName, Schema);
// This adds the appropriate Ignore calls on config for the base class BaseContent
DataAccessUtilityClass.IgnoreAllBaseContentProperties<T>(config);
return config;
}
public virtual EntityTypeConfiguration<BasePage> ConfigurePageContent()
{
var config = configureTablePerType<BasePage>("PageContent");
config.HasKey(pg => pg.Id);
config.HasRequired(pg => pg.Title);
config.HasOptional(pg => pg.PageImageRef);
config.Ignore(pg => pg.ImageLocationOnPage);
return config;
}
public virtual EntityTypeConfiguration<ChapterPage> ConfigureChapterPage()
{
var config = configureTablePerType<ChapterPage>("ChapterPage");
config.HasOptional(pg => pg.AudioRef);
config.Ignore(pg => pg.ChapterNumber);
config.Ignore(pg => pg.ChapterTitle);
return config;
}
public virtual EntityTypeConfiguration<SectionPage> ConfigureSectionPage()
{
var config = configureTablePerType<SectionPage>("SectionPage");
config.HasOptional(pg => pg.AudioRef);
config.Ignore(pg => pg.SectionNumber);
config.Ignore(pg => pg.SectionTitle);
return config;
}
... other code to model other tables...
So the app is able to populate content and the relationships are properly set up. However, when I try to delete the course, I get the error that the delete failed due to the constraint on the ChapterPage to PageContent table..
Here is the code which deletes the Course (actually I delete all courses)...
using (MyContext ctx = new MyContext())
{
ctx.Courses.ToList().ForEach(crs => ctx.Courses.Remove(crs));
AttachLookupEntities(ctx);
ctx.SaveChanges();
}
If I add the 'ON DELETE CASCADE' in the ChapterPage and SectionPage table for its shared primary with PageContent, the delete goes through.
In summary,
The only solution that I have seen is to manually alter the constraints to add the ON DELETE CASCADE for all of my sub-class page tables. I can implement the change, as I have code which generates the DB script for the EF tables I need (a small subset of our whole DB) since we will not use EF to create or instantiate the DB (since it does not properly support migrations as yet...).
I sincerely hope that I have miscoded something, or forgot some setting in the model builder logic. Because if not, the EF designers have defined an architecure (TPT design approach) which cannot be used in any real world situation without a hack workaround. It's a half finished solution. Do not get me wrong, I like the work that has been done, and like most MSFT solutions its works for 70% of most basic application usages. It just is not ready for more complex situations.
I was trying to keep the DB design all within the EF fluent API and self-contained. It's about 98% there for me, just would be nice if they finished the job, maybe in the next release. At least it saves me all the CRUD operations.
Ciao!
Jim Shaw
I have reproduced the problem with a little bit simpler example:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Data.Entity;
namespace EFTPT
{
public class Parent
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<BasePage> Pages { get; set; }
}
public abstract class BasePage
{
public int Id { get; set; }
public string Name { get; set; }
public Parent Parent { get; set; }
}
public class DerivedPage : BasePage
{
public string DerivedName { get; set; }
}
public class MyContext : DbContext
{
public DbSet<Parent> Parents { get; set; }
public DbSet<BasePage> BasePages { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Parent>()
.HasMany(p => p.Pages)
.WithRequired(p => p.Parent); // creates casc. delete in DB
modelBuilder.Entity<BasePage>()
.ToTable("BasePages");
modelBuilder.Entity<DerivedPage>()
.ToTable("DerivedPages");
}
}
class Program
{
static void Main(string[] args)
{
using (var ctx = new MyContext())
{
var parent = new Parent { Pages = new List<BasePage>() };
var derivedPage = new DerivedPage();
parent.Pages.Add(derivedPage);
ctx.Parents.Add(parent);
ctx.SaveChanges();
}
using (var ctx = new MyContext())
{
var parent = ctx.Parents.FirstOrDefault();
ctx.Parents.Remove(parent);
ctx.SaveChanges(); // exception here
}
}
}
}
This gives the same exception that you had too. Only solutions seem to be:
Either setup cascading delete for the TPT constraint in the DB manually, as you already tested (or put an appropriate SQL command into the Seed method).
Or load the entites which are involved in the TPT inheritance into memory. In my example code:
var parent = ctx.Parents.Include(p => p.Pages).FirstOrDefault();
When the entities are loaded into the context, EF creates actually two DELETE statements - one for the base table and one for the derived table. In your case, this is a terrible solution because you had to load a much more complex object graph before you can get the TPT entities.
Even more problematic is if Parent has an ICollection<DerivedPage> (and the inverse Parent property is in DerivedPage then):
public class Parent
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<DerivedPage> Pages { get; set; }
}
public abstract class BasePage
{
public int Id { get; set; }
public string Name { get; set; }
}
public class DerivedPage : BasePage
{
public string DerivedName { get; set; }
public Parent Parent { get; set; }
}
The example code wouldn't throw an exception but instead delete the row from the derived table but not from the base table, leaving a phantom row which cannot represent an entity anymore because BasePage is abstract. This problem is not solvable by a cascading delete but you were actually forced to load the collection into the context before you can delete the parent to avoid such a nonsense in the database.
A similar question and analysis was here: http://social.msdn.microsoft.com/Forums/en-US/adodotnetentityframework/thread/3c27d761-4d0a-4704-85f3-8566fa37d14e/

Can Fluent NHibernate's AutoMapper handle Interface types?

I typed this simplified example without the benefit of an IDE so forgive any syntax errors. When I try to automap this I get a FluentConfigurationException when I attempt to compile the mappings -
"Association references unmapped class
IEmployee."
I imagine if I were to resolve this I'd get a similar error when it encounters the reference to IEmployer as well. I'm not opposed to creating a ClassMap manually but I prefer AutoMapper doing it instead.
public interface IEmployer
{
int Id{ get; set; }
IList<IEmployee> Employees { get; set; }
}
public class Employer: IEmployer
{
public int Id{ get; set; }
public IList<IEmployer> Employees { get; set; }
public Employer()
{
Employees = new List<IEmployee>();
}
}
public interface IEmployee
{
int Id { get; set; }
IEmployer Employer { get; set; }
}
public class Employee: IEmployee
{
public int Id { get; set;}
public IEmployer Employer { get; set;}
public Employee(IEmployer employer)
{
Employer = employer;
}
}
I've tried using .IncludeBase<IEmployee>() but to no avail. It acts like I never called IncludeBase at all.
Is the only solution to either not use interfaces in my domain entities or fall back on a manually defined ClassMap?
Either option creates a significant problem with the way my application is designed. I ignored persistence until I had finished implementing all the features, a mistake I won't be repeating again :-(
It's not a restriction imposed by Fluent or its AutoMapper, but by NHibernate itself.
I therefore don't think you'd get there with the manual class map. You'll have to lose the interfaces in the property and list definitions. You can keep the interfaces, but mapped properties and collections must use the concrete types of which NHibernate knows.
public class PersonMap : ClassMap<Person>
{
public PersonMap()
{
Id(x => x.Id);
Map<Address>(x => x.Address); // Person.Address is of type IAddress implemented by Address
}
}