entity framework c# eager loading inherited and nested properties - c#

I have a following object as a model in project:
AbstractControll and AntivirusControll which inherits from AbstractControll.
public class AntivirusControll: AbstractControll
AnticvirusControll has property:
public List<Exclusion> Exclusions { get; set; }
And this part of code drives me creazy:
var a = _entities.AntivirusControlls.First(m => m.Id == 1);
var b = _entities.AbstractControlls.First(m => m.Id == 1);
In database AnticvirusControll has one exception,while in result of executing above code I get:
a.Exclusions - has one object ( which is great and ok )
b.Exclusions - has none!
How is it even called inheritance? It's not polymorphic, it's .... a bug I could tell. Lazy loading wont work in this case.
And while asking for AbstractControlls I Can't include any property from derivered class offcourse.
Any ideas how to fix it?
--EDIT
both classes;
public abstract class AbstractControll
{
public int Id { get; set; }
}
public abstract class AbstractControll
{
public int Id { get; set; }
}
public class AntivirusControll: AbstractControll
{
public class Exclusion
{
public int Id { get; set; }
[DataMember]
[Display(Name = "Object Type")]
public String ObjectType { get; set; }
[DataMember]
[Display(Name = "Object Type")]
public String ObjectName { get; set; }
[DataMember]
public ConfigurationItemDescription ExlusionCI { get; set; }
}
[Display(Name= "Is Antyvirus Enabled")]
[DataMember]
public bool? isAntyvirusEnabled { get; set; }
[DataMember]
public ConfigurationItemDescription isAntyvirusEnabledCI { get; set; }
[DataMember]
[Display(Name = "Is real time protection enabled")]
public bool? isRealTimeProtectionEnabled { get; set; }
[DataMember]
public ConfigurationItemDescription isRealTimeProtectionEnabledCI { get; set; }
[DataMember]
[Display(Name = "Virus definition not older than( in days)")]
[Required]
[Range(typeof(int),"1","365")]
public int? VirusDefinitionNotOlderThen { get; set; }
[DataMember]
public ConfigurationItemDescription VirusDefinitionNotOlderThenCI { get; set; }
[DataMember]
public List<Exclusion> Exclusions { get; set; }
public AntivirusControll()
{
isAntyvirusEnabled = true;
isRealTimeProtectionEnabled = true;
VirusDefinitionNotOlderThen = 7;
isAntyvirusEnabledCI = new ConfigurationItemDescription();
isRealTimeProtectionEnabledCI = new ConfigurationItemDescription();
VirusDefinitionNotOlderThenCI = new ConfigurationItemDescription();
Exclusions = new List<Exclusion>();
}
}
--Edit 2
Well... I wanto to clarify, what actualy I'm doing.
I have MVC + angular Application.
There is a document with some Controls( AbstractControll)
Each of them is specific Type for example: Antivirus, Encryption and so on. At this point I have about 20 of them, but I will have more probably. Most of those derived classes have inner classes like Exceptions in Antivirus.
Right.
Basicaly that's it. What is important- After getting "AbstractControll" I want to expose this object over webapi - so lazy loading is not applicable here( or maybe it could be?)
That's it. It runtime I don't know exat type of the controll, so I Can't Include Properties from for example "Antivirus", when operating on DbSet
What I have now is ugly if's block that returns propper object and includes everything, but i'm not happy about his "hack".

Related

.net core data annotation display Name - inherite to viewmodels

I am trying to create a ASP.NET application, and am using DataAnnotations in the Entity Class Models for more readable display names:
In my ApplicationDomain Project
public class Car{
public int Id { get; set; }
[Display(Name = "Make of Car")]
public string Make { get; set; }
[Display(Name = "Year of Purchase")]
public int PurchaseYear { get; set; }
}
When I then use this as the model for my views, everything is displayed as expected.
But when I use a view model, I then have to add the Annotations again as the Display Name I initially added to Car is not 'Inherited' to the View Models based on it.
In my WebMVC Project
public class EditCarViewModel{
[Display(Name = "Make of Car")]
public string Make { get; set; }
[Display(Name = "Year of Purchase")]
public int PurchaseYear { get; set; }
}
The same for the create, index and any other views that use a viewmodel and not the Car Class.
Is there anyway to have the annotations that are in the initial entity class model inherited / propagated up, into the related view models so I'm not having to do this in multiple places?
I think this will be even more of an issue if I then try to add a different UI project. e.g. a desktop application in addition to the WebMVC.
It would be ideal if the labels for both could be based on the definitions in the ApplicationDomain Project.
You can try creating a new metadata class and apply it to your others.
[MetadataType(typeof(CarModelMetaData))]
public class EditCarViewModel{
public string Make { get; set; }.
public int PurchaseYear { get; set; }
}
[MetadataType(typeof(CarModelMetaData))]
public class CreateCarViewModel{
public string Make { get; set; }
public int PurchaseYear { get; set; }
}
public class CarModelMetaData{
[Display(Name = "Make of Car")]
public string Make { get; set; }
[Display(Name = "Year of Purchase")]
public int PurchaseYear { get; set; }
}
There is no way of propagating annotation text from one class to another.
But if you just want to keep the same text in one place, you can create constants and use them this way:
public static class DisplayConstants
{
public const string Make = "Make of Car";
public const string PurchaseYear = "Year of Purchase";
}
public class EditCarViewModel{
[Display(Name = DisplayConstants.Make)]
public string Make { get; set; }
[Display(Name = DisplayConstants.PurchaseYear)]
public int PurchaseYear { get; set; }
}
public class Car
{
public int Id { get; set; }
[Display(Name = DisplayConstants.Make)]
public string Make { get; set; }
[Display(Name = DisplayConstants.PurchaseYear)]
public int PurchaseYear { get; set; }
}
Note that this way you can name properties in EditCarViewModel and Car whatever way you like, no restriction on consistent naming.

Converting infinitely nested objects in .NET Core

EDIT: I originally worded this question very poorly, stating the problem was with JSON serialization. The problem actually happens when I'm converting from my base classes to my returned models using my custom mappings. I apologize for the confusion. :(
I'm using .NET Core 1.1.0, EF Core 1.1.0. I'm querying an interest and want to get its category from my DB. EF is querying the DB properly, no problems there. The issue is that the returned category has a collection with one interest, which has one parent category, which has a collection with one interest, etc. When I attempt to convert this from the base class to my return model, I'm getting a stack overflow because it's attempting to convert the infinite loop of objects. The only way I can get around this is to set that collection to null before I serialize the category.
Interest/category is an example, but this is happening with ALL of the entities I query. Some of them get very messy with the loops to set the relevant properties to null, such as posts/comments.
What is the best way to address this? Right now I'm using custom mappings that I wrote to convert between base classes and the returned models, but I'm open to using any other tools that may be helpful. (I know my custom mappings are the reason for the stack overflow, but surely there must be a more graceful way of handling this than setting everything to null before projecting from base class to model.)
Classes:
public class InterestCategory
{
public long Id { get; set; }
public string Name { get; set; }
public ICollection<Interest> Interests { get; set; }
}
public class Interest
{
public long Id { get; set; }
public string Name { get; set; }
public long InterestCategoryId { get; set; }
public InterestCategory InterestCategory { get; set; }
}
Models:
public class InterestCategoryModel
{
public long Id { get; set; }
public string Name { get; set; }
public List<InterestModel> Interests { get; set; }
}
public class InterestModel
{
public long Id { get; set; }
public string Name { get; set; }
public InterestCategoryModel InterestCategory { get; set; }
public long? InterestCategoryId { get; set; }
}
Mapping functions:
public static InterestCategoryModel ToModel(this InterestCategory category)
{
var m = new InterestCategoryModel
{
Name = category.Name,
Description = category.Description
};
if (category.Interests != null)
m.Interests = category.Interests.Select(i => i.ToModel()).ToList();
return m;
}
public static InterestModel ToModel(this Interest interest)
{
var m = new InterestModel
{
Name = interest.Name,
Description = interest.Description
};
if (interest.InterestCategory != null)
m.InterestCategory = interest.InterestCategory.ToModel();
return m;
}
This is returned by the query. (Sorry, needed to censor some things.)
This is not .NET Core related! JSON.NET is doing the serialization.
To disable it globally, just add this during configuration in Startup
services.AddMvc()
.AddJsonOptions(options =>
{
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
}));
edit:
Is it an option to remove the circular references form the model and have 2 distinct pair of models, depending on whether you want to show categories or interests?
public class InterestCategoryModel
{
public long Id { get; set; }
public string Name { get; set; }
public List<InterestModel> Interests { get; set; }
public class InterestModel
{
public long Id { get; set; }
public string Name { get; set; }
}
}
public class InterestModel
{
public long Id { get; set; }
public string Name { get; set; }
public InterestCategoryModel InterestCategory { get; set; }
public class InterestCategoryModel
{
public long Id { get; set; }
public string Name { get; set; }
}
}
Note that each of the models has a nested class for it's child objects, but they have their back references removed, so there would be no infinite reference during deserialization?

Entity Framework Error: Specified Include Path Is Not Valid for Virtual Entity [duplicate]

This question already has answers here:
A specified Include path is not valid. The EntityType does not declare a navigation property with the name *
(5 answers)
Closed 3 years ago.
I am encountering an error to the effect of "A specified Include path is not valid. The EntityType 'MyCMS.DAL.SiteSettings' does not declare a navigation property with the name 'SiteSettingOptions'."
I have read quite a few posts about this issue but all of the research I have done pertains to ICollections and not for properties that are classes.
Here is where I am and could use your help resolving the issues.
[Table("SiteSettingsBridge")]
[DataContract]
public partial class SiteSettings
{
[Key]
[DataMember]
public int SiteSettingsBridgeID { get; set; }
[DataMember]
public int SiteID { get; set; }
[DataMember]
public int SiteSettingOptionID { get; set; }
[DataMember]
public virtual SiteSettingOptions SiteSettingOption { get; set; }
[DataMember]
[StringLength(500)]
public string Value { get; set; }
public SiteSettings()
{
SiteSettingsBridgeID = 0;
SiteID = 0;
SiteSettingOptionID = 0;
Value = "";
}
}
[DataContract]
public partial class SiteSettingOptions
{
[Key]
[DataMember]
public int SiteSettingOptionID { get; set; }
[DataMember]
public string SettingsGroup { get; set; }
[DataMember]
[StringLength(100)]
public string Name { get; set; }
[DataMember]
[StringLength(500)]
public string DefaultValue { get; set; }
[DataMember]
public SettingType Type { get; set; }
public enum SettingType
{
String = 1,
Bool = 2,
Integer = 3
}
public SiteSettingOptions()
{
SiteSettingOptionID = 0;
SettingsGroup = string.Empty;
Name = string.Empty;
DefaultValue = string.Empty;
Type = SiteSettingOptions.SettingType.String;
}
}
and then in my DAL project, I am attempting to add an Include to the context query like this
public static List<Contracts.Sites.SiteSettings> GetBySiteID(int SiteID)
{
using (CMSContext cntx = new CMSContext())
{
///OMITTED FOR BREVITY
return cntx.SiteSettings.Include("SiteSettingOptions").Where(i => i.SiteID == SiteID).ToList();
}
}
When I compile and run, I am receiving the above error.
To answer a few questions up front, I am not using LazyLoading and I am not doing anything on model create.
Yes, I am brand new to EF. This is my first app.
Thanks Ben...
Your property is called SiteSettingOption (no s) but you are trying to Include("SiteSettingOptions") (notice the final s). Remove the last s. You can avoid this by using the Include extension that uses lambdas rather than strings this would be something like Include(x => x.SiteSettingOption)

working with Entity Framework ntier - convert list to icollection error

Below is what I am trying to do. I am using Entity Framework 6 and I have a DataLayer Object that I am passing up the layers to a BusinessData Object. Basically get the object from the database and pass its values to a new object that mirrors it in the BusinessData layer.
See below for code.
Entity Framework Generated Object
public partial class SolutionArea
{
public SolutionArea()
{
this.Awards = new HashSet<Award>();
this.Competencies = new HashSet<Competency>();
this.KeyWins = new HashSet<KeyWin>();
this.Offerings = new HashSet<Offering>();
this.Products = new HashSet<Product>();
this.Programs = new HashSet<Program>();
}
public int ID { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string Manager { get; set; }
public string PreSalesCount { get; set; }
public virtual ICollection<Award> Awards { get; set; }
public virtual ICollection<Competency> Competencies { get; set; }
public virtual ICollection<KeyWin> KeyWins { get; set; }
public virtual ICollection<Offering> Offerings { get; set; }
public virtual ICollection<Product> Products { get; set; }
public virtual ICollection<Program> Programs { get; set; }
}
Data Layer Object
namespace SolutionsEntities.DataAccessObjects
{
public class SolutionAreaDAO
{
public SolutionAreaBDO GetSolutionArea(int Id)
{
SolutionAreaBDO solutionAreaBDO = null;
using(var context = new SolutionsEntities())
{
SolutionArea solutionAreaDAO = (from s in context.SolutionAreas
where s.ID == Id
select s).FirstOrDefault();
if (solutionAreaDAO != null)
{
solutionAreaBDO = new SolutionAreaBDO();
{
solutionAreaBDO.Title = solutionAreaDAO.Title;
solutionAreaBDO.Programs = solutionAreaDAO.Programs;
}
}
}
}
}
Business Data Object
namespace SolutionsBDO
{
public class SolutionAreaBDO
{
public int ID { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string Manager { get; set; }
public string PreSalesCount { get; set; }
public virtual ICollection<AwardBDO> Awards { get; set; }
public virtual ICollection<CompetencyBDO> Competencies { get; set; }
public virtual ICollection<KeyWinBDO> KeyWins { get; set; }
public virtual ICollection<OfferingBDO> Offerings { get; set; }
public virtual ICollection<ProductBDO> Products { get; set; }
public virtual ICollection<ProgramBDO> Programs { get; set; }
}
}
The problem I have is in the SolutionAreaDAO class above I get an Error saying I cannot convert an ICollection to an ICollection. This is the line of code that causes the issue:
solutionAreaBDO = new SolutionAreaBDO();
{
solutionAreaBDO.Title = solutionAreaDAO.Title;
solutionAreaBDO.Programs = solutionAreaDAO.Programs;
}
Both objects have a property that is a separate entity. SolutionArea object contains a Collection of Program objects. When I try to set the collection from the Data Object to the Business Object I get an explicit cast error.
Error 1 Cannot implicitly convert type 'System.Collections.Generic.ICollection<SolutionsEntities.Program>' to 'System.Collections.Generic.ICollection<SolutionsBDO.ProgramBDO>'. An explicit conversion exists (are you missing a cast?) C:\Projects\SolutionsBackgrounder\SolutionsEntities\DataAccessObjects\SolutionAreaDAO.cs 26 52 SolutionsEntities
I'm sure I'm just not doing tis right but if someone can point me in the right direction it would be much appreciated!
Thanks,
Joe
You're trying to convert a ICollection<SolutionsEntities.Program>> to an ICollection<SolutionsBDO.ProgramBDO>
Those are two collections with a different type parameter (that are unrelated as far as the compiler is concerned). You need to either convert these objects manually, change the collection type on either you BDO or your DAO or use something like AutoMapper to convert them automatically

Request for Creational Design/Pattern Suggestion

I have a number of classes that are all related conceptually, but some more-so at the details level than others. For example, these three classes have nearly identical properties (although member functions will vary):
public class RelatedA : IRelatedType
{
public string Name { get; set; }
public string Value { get; set; }
public DateTime Stamp { get; set; }
}
public class RelatedB : IRelatedType
{
public string Name { get; set; }
public string Value { get; set; }
public DateTime Stamp { get; set; }
}
public class RelatedC : IRelatedType
{
public string Name { get; set; }
public string Value { get; set; }
public DateTime Stamp { get; set; }
public int Special { get; set; }
}
There are a couple of other classes that are conceptually related to the above 3, but can be a bit different implementation-wise:
public class RelatedD : IRelatedType
{
public string Name { get; set; }
public string Statement { get; set; }
}
public class RelatedE : IRelatedType
{
public string Name { get; set; }
public string Statement { get; set; }
public bool IsNew { get; set; }
}
Instances of these can be created by a factory based on some sort of "type" enumerated value. The problem is that later on when these objects are being used (in a business layer, for example), there could be a lot of code like this:
IRelatedType theObject = TheFactory.CreateObject(SomeEnum.SomeValue);
if (theObject is RelatedC)
{
RelatedC cObject = theObject as RelatedC;
specialVal = cObject.Special;
}
else if (theObject is RelatedD)
{
RelatedD dObject = theObject as RelatedD;
statementVal = dObject.Statement;
}
else if (theObject is RelatedE)
{
RelatedE eObject = theObject as RelatedE;
statementVal = eObject.Statement;
isNewVal = eObject.IsNew;
}
This could be repeated in many places. Is there a better approach to the design that I should be using (there's got to be)?
You could try and factor the differences into seperate classes that are then provided so for example:
IRelatedType theObject = TheFactory.CreateObject(SomeEnum.SomeValue);
RelatedTypeHelper theHelper=TheFactory.CreateHelper(theObject);
theHelper.DoSpecialThing(theObject);
Now you won't have to have all the if else blocks, and if you add a new type which requires new handling, you just whip up a new helper implement the required pieces and you should be good to go. The helper should help document this process.
I would also question why a single method would have such a different implementation for specialVal and StatementVal could be your sample, but It makes me curious what your really doing here. can you simplify things back taking a step back and questioning the point of these being included in this specific hierarchy.

Categories