In the following classes I am trying to do a code first setup using abstract base classes. Sorry if this is a duplicate question. I did try to search the site and google for an answer but nothing I have tried has worked.
public abstract class IDObject
{
[Key]
public Int32 ID { get; internal set; }
}
public abstract class NamedObject : IDObject
{
public String Name { get; set; }
}
public abstract class HWObject : NamedObject
{
public Double Free { get; set; }
public Double Max { get; set; }
public Double Used { get; set; }
}
public abstract class CPUObject : HWObject { }
public abstract class MemoryObject : HWObject { }
public abstract class StorageObject : HWObject { }
public class Server : NamedObject
{
[JsonConstructor]
internal Server() { }
public String CompanyName { get; set; }
public Boolean IsRunning { get; set; }
public CPUObject CPU { get; set; }
public MemoryObject Memory { get; set; }
public StorageObject Storage { get; set; }
}
I have the following DBContext:
public class DataContext : DbContext
{
public DataContext() : base("name=DataContext") { }
public DbSet<Server> Servers { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
}
}
When I try to view the Read-Only Data Model I get the following error:
System.InvalidOperationException: The abstract type 'MyApp.CPUObject' has no mapped descendants and so cannot be mapped. Either remove 'MyApp.CPUObject' from the model or add one or more types deriving from 'MyApp.CPUObject' to the model.
How do I change the DBContext so the error goes away?
EDIT:
According to the answer below I have removed abstract from the CPUObject, MemoryObject and StorageObject as they should have never been abstract.
I have changed the DBContext to the following: (The mappings create a 1 to 1 relationship)
public class DataContext : DbContext
{
public DataContext() : base("name=DataContext") { }
public DbSet<Server> Servers { get; set; }
public DbSet<CPUObject> CPUObjects { get; set; }
public DbSet<MemoryObject> MemoryObjects { get; set; }
public DbSet<StorageObject> StorageObjects { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Server>().HasRequired(u => u.CPU).WithRequiredDependent().Map(m => m.MapKey("CPUID"));
modelBuilder.Entity<Server>().HasRequired(u => u.Memory).WithRequiredDependent().Map(m => m.MapKey("MemoryID"));
modelBuilder.Entity<Server>().HasRequired(u => u.Storage).WithRequiredDependent().Map(m => m.MapKey("StorageID"));
}
}
CPUObject is currently an abstract class, which cannot be instantiated. Just remove the abstract to make it a concrete object. Then, add public DbSet<CPUObject> CPUs { get; set; } to your DBContext
You are currently getting the error because EF is looking for a concrete class, so the error says you can make another concrete class that inherits (i.e. 'deriving from') CPUObject
Related
I have the following structure of EF entities classes:
public abstract class CalendarItem
{
public Guid CalendarItemId { get; set; }
public Guid? ParentCalendarItemId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual CalendarItem ParentCalendarItem { get; set; }
public virtual ICollection<CalendarItem> ChildCalendarItems { get; set; }
}
public class CalendarSlot : CalendarItem
{
public bool IsAvailable { get; set; }
}
public class CalendarSession : CalendarEvent
{
public DateTimeOffset StartDateTime { get; set; }
public DateTimeOffset EndDateTime { get; set; }
}
Mapping is configured as follows:
public class CalendarItemMap
{
public static void Map(ModelBuilder modelBuilder)
{
modelBuilder.Entity<CalendarItem>().ToTable("CalendarItem");
modelBuilder.Entity<CalendarItem>().HasKey(t => t.CalendarItemId);
modelBuilder.Entity<CalendarItem>().HasOne(ci => ci.ParentCalendarItem)
.WithMany(cs => cs.ChildCalendarItems).HasForeignKey(cs => cs.ParentCalendarItemId);
}
}
With DbSets as:
public DbSet<CalendarItem> CalendarItems { get; set; }
public DbSet<CalendarSession> CalendarSessions { get; set; }
public DbSet<CalendarSlot> CalendarSlots { get; set; }
My question is how to properly organize self-reference (tree) structure of the base class with inheritance provided. All types of entities, namely CalendarSlot and CalendarSession should have references to themselves, CalendarItem cannot be created as an instance.
In the database the tree structure is implemented in the CalendarItem table. CalendarSlot/CalendarSession tables have 1-1 relationship with CalendarItem.
The problem is that in my code I have a place with generic method which converts models to contracts for the API:
internal static TContract ToCalendarItem<TContract, TModel>(TModel calendarItemModel)
where TContract : Contract.CalendarItem, new()
where TModel : Model.CalendarItem
{
return new()
{
CalendarItemId = calendarItemModel.CalendarItemId,
Name = calendarItemModel.Name,
Description = calendarItemModel.Description,
ParentCalendarItem = calendarItemModel.ParentCalendarItem != null ? ToCalendarItem<TContract, TModel>(calendarItemModel.ParentCalendarItem) : null
};
}
and in the line
ParentCalendarItem = calendarItemModel.ParentCalendarItem != null ? ToCalendarItem<TContract, TModel>(calendarItemModel.ParentCalendarItem) : null
I have an error "Argument 1: Cannot convert from 'Model.CalendarItem' to TModel".
How can I tell the system that calendarItemModel.ParentCalendarItem should always be TModel (a class derived from Model.CalendarItem)?
What I am doing wrong here?
Thank you in advance.
I am trying to map two entities with a many to many relationship that inherit from an abstract base class into Dtos that also inherit from their own abstract base class. When I only include the mapping for the Essay class everything works fine except that the Books collection is null, as soon as I add the mapping for that collection I get the following exception:
Inner Exception 1: ArgumentException: Cannot create an instance of
abstract type Dtos.Dto`1[System.Int64].
Consider the following code:
namespace Entities
{
public abstract class Entity<TId> : Entity
where TId : struct, IEquatable<TId>
{
protected Entity()
{
}
public TId Id { get; set; }
}
public class Essay : Entity<long>
{
public string Name { get; set; }
public string Author { get; set; }
public List<EssayBook> EssayBooks { get; set; }
}
public class Book : Entity<long>
{
public string BookName { get; set; }
public string Publisher { get; set; }
public List<EssayBook> EssayBooks { get; set; }
}
public class EssayBook
{
public long EssayId { get; set; }
public long BookId { get; set; }
public Essay Essay { get; set; }
public Book Book { get; set; }
}
}
namespace Dtos
{
public abstract class Dto<TId>
where TId : struct, IEquatable<TId>
{
protected Dto()
{
}
public TId Id { get; set; }
}
public sealed class Essay : Dto<long>
{
public string Name { get; set; }
public string Author { get; set; }
public List<Book> Books { get; set; }
}
public class Book : Dto<long>
{
public string BookName { get; set; }
public string Publisher { get; set; }
}
}
namespace DtoMapping
{
internal sealed class EssayBookProfile : Profile
{
public EssayBookProfile()
{
this.CreateMap<Entities.Essay, Dtos.Essay>()
.IncludeBase<Entities.Entity<long>, Dtos.Dto<long>>()
.ForMember(dto => dto.Books, opt => opt.MapFrom(e => e.EssayBooks.Select(pl => pl.Book)));
}
}
}
I've been looking to see if there is a different way to configure this mapping but I always find this way. I have also tried to specifically add the mappings for the base classes but I get the exact same result.
On my Web API project I have included the AutoMapper.Extensions.Microsoft.DependendyInjection package version 7.0.0.
As I was creating the gist by the recommendation in the post's comments I realized that the mapping for the Book dto was missing. Unfortunately the exception was not clear about the problem and that took me to ask the question here. After I added the mapping everything started working as expected.
Let's say I have nested generic data classes similar to the following:
public class BaseRecordList<TRecord, TUserInfo>
where TRecord : BaseRecord<TUserInfo>
where TUserInfo : BaseUserInfo
{
public virtual IList<TRecord> Records { get; set; }
public virtual int Limit { get; set; }
}
public class BaseRecord<TUserInfo>
where TUserInfo : BaseUserInfo
{
public virtual DateTime CreationTime { get; set; }
public virtual TUserInfo UserInfo { get; set; }
}
public class BaseUserInfo
{
public virtual string Name { get; set; }
public virtual int Age { get; set; }
}
With 2 concrete versions like so:
// Project 1: Requires some extra properties
public class Project1RecordList : BaseRecordList<Project1Record, Project1UserInfo> {}
public class Project1Record : BaseRecord<Project1UserInfo>
{
public Guid Version { get; set; }
}
public class Project1UserInfo : BaseUserInfo
{
public string FavouriteFood { get; set; }
}
and
// Project 2: Some properties need alternate names for JSON serialization
public class Project2RecordList : BaseRecordList<Project2Record, Project2UserInfo>
{
[JsonProperty("allRecords")]
public override IList<Project2Record> Records { get; set; }
}
public class Project2Record : BaseRecord<Project2UserInfo> {}
public class Project2UserInfo : BaseUserInfo
{
[JsonProperty("username")]
public override string Name { get; set; }
}
I'm then happy to have 2 repositories that return Project1RecordList and Project2RecordList respectively, but at some point in my code I find myself needing to be able to handle both of these in one place. I figure that at this point I need to be able to treat both of these types as
BaseRecordList<BaseRecord<BaseUserInfo>, BaseUserInfo>
as this is the minimum required to meet the generic constraints, but trying to cast or use "as" throws up errors about not being able to convert.
Is there any way to do this, or even a more sane way to handle this situation without massive amounts of code duplication? If it makes any difference this is for a web app and there are already a large number of data classes, many of which use these nested generics.
What you are talking about is called covariance and MSDN has a great article on this here: https://msdn.microsoft.com/en-us/library/dd799517(v=vs.110).aspx
First, create a new interface:
interface IBaseRecord<out TUserInfo>
where TUserInfo : BaseUserInfo
{
}
Have BaseRecord inherit from the new interface:
public class BaseRecord<TUserInfo> : IBaseRecord<TUserInfo>
where TUserInfo : BaseUserInfo
{
public virtual DateTime CreationTime { get; set; }
public virtual TUserInfo UserInfo { get; set; }
}
If done right, this should compile:
IBaseRecord<BaseUserInfo> project1 = new Project1Record();
IBaseRecord<BaseUserInfo> project2 = new Project2Record();
To expand this to the BaseRecordList, create IBaseRecordList:
interface IBaseRecordList<out TRecord, out TUserInfo>
where TRecord : IBaseRecord<TUserInfo>
where TUserInfo : BaseUserInfo
{
}
Have BaseRecordList inherit from that:
public class BaseRecordList<TRecord, TUserInfo> : IBaseRecordList<TRecord, TUserInfo>
And then use as such:
IBaseRecordList<IBaseRecord<BaseUserInfo>, BaseUserInfo> project1 = new Project1RecordList();
IBaseRecordList<IBaseRecord<BaseUserInfo>, BaseUserInfo> project2 = new Project2RecordList();
Once you have that setup, just add whatever properties or functions you need to use generically to the interfaces.
I am having an issue using entity framework to map an existing database to a code base. I can't seem to find any code that has done anything similar either. I need to have multi-layer inheritance and I need the layers to use two different mapping types. I am having some strange behaviour though. To reproduce the behaviour I have created a simple model and allowed EF to map it the way it wishes.
My entities are as follows:
public abstract class Root
{
public int RootId { get; set; }
public string RootProperty { get; set; }
}
public abstract class ChildA : Root
{
public string ChildAProperty { get; set; }
}
public class ChildB : Root
{
public string ChildBProperty { get; set; }
}
public class SubChildAa : ChildA
{
public string SubChildAaProperty { get; set; }
}
public class SubChildAb : ChildA
{
public string SubChildAbProperty { get; set; }
}
This results in the following schema:
My context is as follows:
class MyContext : DbContext
{
public DbSet<SubChildAa> ChildAas { get; set; }
public DbSet<SubChildAb> ChildAbs { get; set; }
public DbSet<ChildB> ChildBs { get; set; }
public MyContext()
{
//Database.SetInitializer<MyContext>(null);
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Root>().ToTable("Root");
modelBuilder.Entity<ChildB>().ToTable("ChildB");
modelBuilder.Entity<ChildA>().ToTable("ChildA");
modelBuilder.Entity<ChildA>().Map<SubChildAa>(u => u.Requires("ChildAType").HasValue("SubChildAa"));
modelBuilder.Entity<ChildA>().Map<SubChildAb>(u => u.Requires("ChildAType").HasValue("SubChildAb"));
//base.OnModelCreating(modelBuilder);
}
}
I am trying to use TPT for the Root to Child (A or B) mapping and TPH for ChildA to SubChild. The main problem is that EF is putting the discriminator on the Root object. It is also putting the ChildA properties there, which I don't think is correct, but is less of a problem for now. With the discriminator being on the Root, it causes an error because ChildB does not map to the types in that discriminator.
Can someone suggest a way in which I can have the ChildA table actualLy maintain its own properties and discriminator without pushing them up to the root table.
Something like this
public abstract class Root
{
[Key]
public int RootId { get; set; }
public string RootProperty { get; set; }
}
public class ChildA : Root
{
[Required]
int RootId
ForeignKey["RootId"]
public Root Root {get;set;}
public string ChildAProperty { get; set; }
}
public class ChildB : Root {
[Required]
int RootId
ForeignKey["RootId"]
public Root Root {get;set;}
public string ChildBProperty { get; set; }
}
public class SubChildAa : ChildA
{
public string SubChildAaProperty { get; set; }
}
public class SubChildAb : ChildA
{
public string SubChildAbProperty { get; set; }
}
In your context you want TPT for ChildA and ChildB
So
public DbSet<ChildA> ChildAas { get; set; }
public DbSet<ChildB> ChildBs { get; set; }
TPH for subchilds
public DbSet<SubChild> Childs { get; set; }
This seems like an easy question but I can't seem to find the answer.
I have a Model that looks like this ...
public class Application
{
public int Id { get; set; }
public string Title { get; set; }
public string LeadProgrammer { get; set; }
public string ConnectionStringCode { get; set; }
}
public class ApplicationDBContext : DbContext
{
public DbSet<Application> Applications { get; set; }
}
My actual table name is DBA_APPLICATIONS ... the model is, of course, just looking for dbo.Applications. How can I change this routing to the actual table?
Add this in your ApplcationDBContext class.
public class ApplicationDBContext : DbContext
{
public DbSet<Application> Applications { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Application>().ToTable("DBA_APPLICATIONS");
// otherwise EF assumes the table is called "Applications"
}
}