EF6 - get all related children - c#

So, we've created a interview. It is built something like this:
class interview
{
public virtual ICollection<WebQuestionPage> WebQuestionPage { get; set; }
}
Then, in the WebQuestionPage class we have
public virtual ICollection<WebQuestionSection> WebQuestionSection { get; set; }
sections!
And each section might contain another, nested, section and/or questions
public virtual ICollection<WebQuestionSection> WebQuestionSection1 { get; set; }
public virtual ICollection<WebQuestion> WebQuestion { get; set; }
The WebQuestion class is the same, it might contain another, nested, question and/or options for the question.
Now, I want to load this entire interview with as low of a loading time as possible.
I did this:
interview = _dbWeb.WebInterview.Include("WebQuestionPage")
.Include("WebQuestionPage.WebQuestionSection.WebQuestion")
.Include("WebQuestionPage.WebQuestionSection.WebQuestion.WebQuestionOption")
.Include("WebQuestionPage.WebQuestionSection.WebQuestion.WebQuestionType")
.Include("WebQuestionPage.WebQuestionSection.WebQuestion.WebQuestion1")
.Include("WebQuestionPage.WebQuestionSection.WebQuestion.WebQuestion1.WebQuestionOption")
.Include("WebQuestionPage.WebQuestionSection.WebQuestion.WebQuestion1.WebQuestionType")
.Include("WebQuestionPage.WebQuestionSection.WebQuestion.WebQuestion1.WebQuestion1")
.Include("WebQuestionPage.WebQuestionSection.WebQuestion.WebQuestion1.WebQuestion1.WebQuestionOption")
.Include("WebQuestionPage.WebQuestionSection.WebQuestion.WebQuestion1.WebQuestion1.WebQuestionType")
.Include("WebQuestionPage.WebQuestionSection.WebQuestion.WebQuestion1.WebQuestion1.WebQuestion1")
.Include("WebQuestionPage.WebQuestionSection.WebQuestion.WebQuestion1.WebQuestion1.WebQuestion1.WebQuestionOption")
.Include("WebQuestionPage.WebQuestionSection.WebQuestion.WebQuestion1.WebQuestion1.WebQuestion1.WebQuestionType")
.Include("WebQuestionPage.WebQuestionSection.WebQuestion.WebQuestion1.WebQuestion1.WebQuestion1.WebQuestion1")
.Include("WebQuestionPage.WebQuestionSection.WebQuestion.WebQuestion1.WebQuestion1.WebQuestion1.WebQuestion1.WebQuestionOption")
.Include("WebQuestionPage.WebQuestionSection.WebQuestion.WebQuestion1.WebQuestion1.WebQuestion1.WebQuestion1.WebQuestionType")
.Include("WebQuestionPage.WebQuestionSection.WebQuestionSection1.WebQuestion")
.Include("WebQuestionPage.WebQuestionSection.WebQuestionSection1.WebQuestion.WebQuestionOption")
.Include("WebQuestionPage.WebQuestionSection.WebQuestionSection1.WebQuestion.WebQuestionType")
.Include("WebQuestionPage.WebQuestionSection.WebQuestionSection1.WebQuestion.WebQuestion1")
.Include("WebQuestionPage.WebQuestionSection.WebQuestionSection1.WebQuestion.WebQuestion1.WebQuestionOption")
.Include("WebQuestionPage.WebQuestionSection.WebQuestionSection1.WebQuestion.WebQuestion1.WebQuestionType")
.Include("WebQuestionPage.WebQuestionSection.WebQuestionSection1.WebQuestion.WebQuestion1.WebQuestion1")
.Include("WebQuestionPage.WebQuestionSection.WebQuestionSection1.WebQuestion.WebQuestion1.WebQuestion1.WebQuestionOption")
.Include("WebQuestionPage.WebQuestionSection.WebQuestionSection1.WebQuestion.WebQuestion1.WebQuestion1.WebQuestionType")
.Include("WebQuestionPage.WebQuestionSection.WebQuestionSection1.WebQuestion.WebQuestion1.WebQuestion1.WebQuestion1")
.Include("WebQuestionPage.WebQuestionSection.WebQuestionSection1.WebQuestion.WebQuestion1.WebQuestion1.WebQuestion1.WebQuestionOption")
.Include("WebQuestionPage.WebQuestionSection.WebQuestionSection1.WebQuestion.WebQuestion1.WebQuestion1.WebQuestion1.WebQuestionType")
.Include("WebQuestionPage.WebQuestionSection.WebQuestionSection1.WebQuestion.WebQuestion1.WebQuestion1.WebQuestion1.WebQuestion1")
.Include("WebQuestionPage.WebQuestionSection.WebQuestionSection1.WebQuestion.WebQuestion1.WebQuestion1.WebQuestion1.WebQuestion1.WebQuestionOption")
.Include("WebQuestionPage.WebQuestionSection.WebQuestionSection1.WebQuestion.WebQuestion1.WebQuestion1.WebQuestion1.WebQuestion1.WebQuestionType")
.Include("WebQuestionPage.WebQuestionSection.WebQuestionSection1.WebQuestionSection1.WebQuestion")
.Include("WebQuestionPage.WebQuestionSection.WebQuestionSection1.WebQuestionSection1.WebQuestion.WebQuestionOption")
.Include("WebQuestionPage.WebQuestionSection.WebQuestionSection1.WebQuestionSection1.WebQuestion.WebQuestionType")
.Include("WebQuestionPage.WebQuestionSection.WebQuestionSection1.WebQuestionSection1.WebQuestion.WebQuestion1")
.Include("WebQuestionPage.WebQuestionSection.WebQuestionSection1.WebQuestionSection1.WebQuestion.WebQuestion1.WebQuestionOption")
.Include("WebQuestionPage.WebQuestionSection.WebQuestionSection1.WebQuestionSection1.WebQuestion.WebQuestion1.WebQuestionType")
... and so on
which, really, isn't that beautiful, easily read, or fast because it will join even if there is no nested children.
This is written is ASP.NET MVC using EF6.

Related

Entity Framework include only returning part of the database data

I am very new to asp.net and C# so bear with me. I am trying to return data from a database using the entity framework .include() method so that I can get the foreign key information from another table. However, what is being returned is only part of the data. It seems to be cut off before everything is returned.
"[{"id":11,"name":"Mr. Not-so-Nice","heroType":3,"heroTypeNavigation":{"id":3,"type":"Villian","heroes":["
Which gives me the error: SyntaxError: Unexpected end of JSON input.
Please seem below for the model classes and the GET section of the controller where this is being returned. If I remove the "include()" method it returns all the heroes from the main table just fine.
public partial class Hero
{
public int Id { get; set; }
public string Name { get; set; }
public int? HeroType { get; set; }
public virtual HeroTypes HeroTypeNavigation { get; set; }
}
{
public partial class HeroTypes
{
public HeroTypes()
{
Heroes = new HashSet<Hero>();
}
public int Id { get; set; }
public string Type { get; set; }
public virtual ICollection<Hero> Heroes { get; set; }
}
// GET: api/Heroes
[HttpGet]
public async Task<ActionResult<IEnumerable<Hero>>> GetHeroesTable()
{
return await _context.HeroesTable.Include(hero => hero.HeroTypeNavigation).ToListAsync();
}
Serializer recursion rules will be tripping this up. Basically as jonsca mentions, you have a circular reference between hero, and hero type. The serializer will start with the hero, then go to serialize the hero type which it will find the Hero's collection and expect to serialize, which each would reference a hero type, with collections of Heros.. The serializer bails when it sees this.
I would recommend avoiding passing back Entity classes to your view to avoid issues with EF and lazy loading. Serialization will iterate over properties, and this will trigger lazy loads. To avoid this, construct a view model for the details your view needs, flatten as necessary.
For example if you want to display a list of Heroes with their Type:
public class HeroViewModel
{
public int HeroId { get; set; }
public string Name { get; set; }
public string HeroType { get; set; }
}
to load:
var heroes = await _context.HeroesTable.Select(x => new HeroViewModel
{
HeroId = x.HeroId,
Name = x.Name,
HeroType = x.HeroType.Type
}).ToListAsync();
You can utilize Automapper for example to help translate entities to view models without that explicit code using ProjectTo<TEntity> which can work with EF's IQueryable implementation.
With larger realistic domains your client likely won't need everything in the object graph.
You won't expose more information than you need to. (I.e. visible via debugging tools)
You'll get a performance boost from not loading the entire graph or triggering
lazy load calls, and it's less data across the wire.
The last point is a rather important one as with complex object graphs, SQL can do a lot of the lifting resulting in a much more efficient query than loading "everything". Lazy hits to the database can easily add several seconds to each and every call from a client, and loading large graphs has a memory implication on the servers as well.

windows wpf MVVC pattern, how to fit code logic in that pattern

I'm working now with converting existing winform app to WPF. I've never coded anything using MVVC pattern before and I have a hard time trying to figure out how to correctly set models, viewmodels and views in my specific case.
App that I want to convert is used for communicate with physical devices using serial port.
Let's first start with existing winform app. Almost any of the logic is in separate .dll file that expose interface which winform consumes. There are really no "code behind" while displaying data because dll already expose everything that is needed. There are only some additional functions in GUI for manipulation of the data, saving current data etc.
The thing is that I really don't understand how to fit this nicely in MVVM. As model as I understand I will need to create some let's say device that will have all the properties that are changeable and readable. And first question, if model needs to be concrete class or it may be some interface (which I already have) or maybe abstract device class (that implements this interface)?
Second question about ViewModels. I understand that ViewModel is somehow used for "glue" model data to view. And from that perspective in ViewModel I can put all the code that as a result format data from device to output it in some nice formats that will be easily databinded to View. But, here is a quirk. I think that this inteface that I'm now using in that .dll file fits nicely to be databinded in gui (or maybe not?).
Another question is about View itself. I understand that View wouldn't be aware of model etc. But could I put in view that code that I already have in winform GUI? (for saving data to csv files, for doing some measurements etc) or maybe I will need to create another model (I have no idea what it may be looks like).
And the last question is where to put all the logic for using serial port, for concrete implementation of all functions etc. I believe that models should be as easy as possible (like in mvc pattern) without any logic and so on (but again, if it should be in that way, maybe model should be only interface?). And ViewModels should have only code for data manipulation to expose data in friendly format to view and convert it back to model. So where rest of the logic should exist? Thank you all in advance for explaining this thing. And in order to put some code to my post, below is interface that I'm using in that .dll file
public interface IScope
{
event EventHandler NewDataInBuffer;
bool Destroy();
bool Connect();
bool Disconnect();
bool StartCapture();
bool StopCapture();
string ScopeName { get; }
IParameter<float> CurrentVoltageLimit { get; }
IParameter<int> DataSamplesPerDiv { get; }
List<IParameter<Config.Timebase>> AvailableTimebaseSettings { get; }
List<IParameter<Config.Coupling>> AvailableCoupleSettings { get; }
List<IParameter<Config.Slope>> AvailableTriggerSlopeSettings { get; }
List<IParameter<Config.VerticalSensitivity>> AvailableSenitivitySettings { get; }
List<IParameter<Config.TriggerMode>> AvailableTriggerModeSettings { get; }
List<IParameter<Config.RecordLength>> AvailableRecordLength { get; }
IParameter<Config.Timebase> TimeBase { get; set; }
IParameter<Config.Coupling> Couple { get; set; }
IParameter<Config.Slope> TriggerSlope { get; set; }
IParameter<Config.VerticalSensitivity> Sensitivity { get; set; }
IParameter<Config.TriggerMode> TriggerMode { get; set; }
IParameter<Config.RecordLength> RecordLength { get; set; }
int TriggerPosition { get; set; }
float TriggerLevel { get; set; }
}
public interface IParameter<T>
{
string ParameterName { get; }
string ParameterValue { get; }
string ParameterUnit { get; }
bool IsReadOnly { get; }
T GetParameter { get; }
}

Issue with lambda expressions in c# data retrieval

i'm writing a system to track observation values from sensors (e.g. temperature, wind direction and speed) at different sites. I'm writing it in C# (within VS2015) using a code-first approach. Although i've a reasonable amount of programming experience, I'm relatively new to C# and the code-first approach.
I've defined my classes as below. I've built a REST api to accept observation reading through Post, which has driven my desire to have Sensor keyed by a string rather than an integer - Some sensors have their own unique identifier built in. Otherwise, i'm trying to follow the Microsoft Contoso university example (instructors - courses- enrolments).
What I am trying to achieve is a page for a specific site with a list of the sensors at the site, and their readings. Eventually this page will present the data in graphical form. But for now, i'm just after the raw data.
public class Site
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Sensor> Sensors { get; set; }
}
public class Sensor
{
[Key]
public string SensorName { get; set; }
public int SensorTypeId { get; set; }
public int SiteId { get; set; }
public ICollection<Observation> Observations { get; set; }
}
public class Observation
{
public int Id { get; set; }
public string SensorName { get; set; }
public float ObsValue { get; set; }
public DateTime ObsDateTime { get; set; }
}
and I've created a View Model for the page I'm going to use...
public class SiteDataViewModel
{
public Site Site { get; set; }
public IEnumerable<Sensor> Sensors { get; set;}
public IEnumerable<Observation> Observations { get; set; }
}
and then i try to join up the 3 classes into that View Model in the SiteController.cs...
public actionresult Details()
var viewModel.Site = _context.Sites
.Include(i => i.Sensors.select(c => c.Observations));
i used to get an error about "cannot convert lambda expression to type string", but then I included "using System.Data.Entity;" and the error has changed to two errors... on the 'include', I get "cannot resolve method 'include(lambda expression)'...". And on the 'select' i get "Icollection does not include a definition for select..."
There's probably all sorts of nastiness going on, but if someone could explain where the errors are (and more importantly why they are errors), then I'd be extremely grateful.
Simply you can you use like
viewModel.Site = _context.Sites
.Include("Sensors).Include("Sensors.Observations");
Hope this helps.
The way your ViewModel is setup, you're going to have 3 unrelated sets of data. Sites, sensors, and observations. Sites will have no inherent relation to sensors -- you'll have to manually match them on the foreign key. Realistically, your ViewModel should just be a list of Sites. You want to do
#Model.Sites[0].Sensors[0].Observations[0]
not something convoluted like
var site = #Model.Sites[0]; var sensor = #Model.Sensors.Where(s => SiteId == site.Id).Single(); etc...
Try doing
viewModel.Site = _context.Sites.Include("Sensors.Observations").ToList();
Eager-loading multiple levels of EF Relations can be accomplished in just one line.
One of the errors you reported receiving, by the way, is because you're using 'select' instead of 'Select'
And lastly, be aware that eager-loading like this can produce a huge amount of in-memory data. Consider splitting up your calls for each relation, such that you display a list of Sensors, and clicking, say, a dropdown will call an API that retrieves a list of Sites, etc. This is a bit more streamlined, and it prevents you from getting held up because your page is loading so much information.
Update
I've created a sample application for you that you can browse and look through. Data is populated in the Startup.Configure method, and retrieved in the About.cshtml.cs file and the About.cshtml page.. This produces this page, which is what you're looking for I believe.

How do I design a specific super class

I am trying to refactor a solution to bring on board another project.
I have a Core project where common classes across projects reside.
I've tried to simpify my question by using 2 imaginary projects: Holidays and Weather...
I have a file load process setup for the Holidays project which has the following 2 classes:
public class Job
{
public virtual string CreatedBy { get; set; }
public virtual DateTime? CreatedDate { get; set; }
public virtual Security Security { get; set; }
protected IList<File> _files = new List<File>();
public virtual IEnumerable<File> Files
{
get { return _files; }
}
}
public class File
{
public virtual string FileName { get; set; }
public virtual FileType FileType { get; set; }
public virtual FileStatusType FileStatusType { get; set; }
public virtual Job Job { get; set; }
}
The file load process for the Weather project has exactly the same structure as Holidays, except that the Jobs class does not have a Security property.
My question is, is it possible to somehow move both classes into the Core project to allow both projects to use them?
Obviously Weather does not need the Security property, so I was thinking I would have a Core.Job class without Security, and then extend the Core.Job in Holidays.Job.
But once I do that, in the Core.File class, what Job is it referring to? As it sits in the Core project it must be the Core.Job.
So would I then need to have Job and File sit in Holidays, and Weather (and any other future projects) use the Core.Job and Core.File?
I don't want the Core project to have any references to sub projects.
I am using NHibernate, and so have mapping files - adding to the complexity.
Hope this is clear enough
Thanks
You can certainly do this, but I am not sure whether it brings you true benefit:
Does the Core itself work with the base Job in any way? If it does not, implementing Job separately in each project may help you keep coupling loose, even though I'd a little redundant. In code I wrote, I have sometimes introduced unnecessary dependencies by extracting interfaces without adding true benefit. This is why I am a bit precautious.
In case Core does acutal work with it, the part to refactor into the common base Job is perhaps the interface it works with.
You may think of an interface instead of a base class. Security may semantically belong to another interface. Moreover, you hand over a lot of control over your classes to the Core.
Do you ever hand a job from one project to another (or are they mapped to the same DB table via NHibernate?)? If you don't, an internal redundant class may be fine too.
Not very clear why confuse on the soluton offered by you (assuming that I right understood you)
//Core DLL
public class Job
{
public virtual string CreatedBy { get; set; }
public virtual DateTime? CreatedDate { get; set; }
protected IList<File> _files = new List<File>();
public virtual IEnumerable<File> Files
{
get { return _files; }
}
}
in the Hollidays you have
public class HollidayJob : Job
{
public virtual Security Security { get; set; }
}
in Weather simply use a type Job, if it selfsufficient.
In this case you refer CoreDLL from Holliday project and Weather. When you serialize it via NHibernate it for HollidayJob save one field more, but when Weather reads the same table it skips that field, as don't know anything, and don't actually care abotu it.
Hope this helps.

ASP.NET MVC 2 and AutoMapper - dissecting and mapping uploaded file info with AutoMapper

This is the 3rd major edit to this question, so I'm going to write a quick little summary first, then ask the question.
I have an input/edit model I'm planning on using with an EF4-backed MVC 2 site. The model is as follows:
public class AdminGameEditModel
{
[Required]
public int GameID { get; set; }
[Required(ErrorMessage="A game must have a title")]
public string GameTitle { get; set; }
[Required(ErrorMessage="A short URL must be supplied")]
public string ShortURL { get; set; }
[Required(ErrorMessage="A box art image must be supplied")]
public HttpPostedFileBase BoxArt { get; set; }
[Required(ErrorMessage="A large image for the index page is required")]
public HttpPostedFileBase IndexImage { get; set; }
[Required(ErrorMessage="A game must have a review")]
public string ReviewText { get; set; }
[Required(ErrorMessage="A game must have a score")]
public int ReviewScore { get; set; }
[Required(ErrorMessage="A game must have at least one Pro listed")]
public string[] Pros { get; set; }
[Required(ErrorMessage="A game must have at least one Con listed")]
public string[] Cons { get; set; }
[Required(ErrorMessage="A game must belong to a genre")]
public int GenreID { get; set; }
[Required(ErrorMessage="A game must be associated with at least one platform")]
public int[] PlatformIDs { get; set; }
}
I'd like to map it to a Game entity for creation/updating. There's a snag, though - I need to save the images in a particular folder, and then take their paths and save those as properties in my entity. So, an example for clarity's sake: rather than my Game entity having a actual BoxArt image, it would instead have the path to the correct BoxArt image. I hope this makes sense. Let me know if I need to clarify.
Can I do this with AutoMapper? If so, can anyone provide some code guidance?
EDIT:
Part of the problem is that my model is fairly complex, as it contains a many-to-many relationship. The PlatformIDs are ultimately used to build/rebuild (depending whether I'm creating or updating an entity) linked Platform entities in the Game/Platform map. I'm not sure if AutoMapper can do something that complex without needing to go through my repository.
Then there's the problem of the image paths. The paths aren't a property of HttpPostedFileBase, but must be constructed by hand like so:
if (BoxArt.ContentLength > 0) {
var fileName = Path.GetFileName(BoxArt.FileName);
var path = Path.Combine(Server.MapPath("~/Content/Images/BoxArt"), fileName);
BoxArt.SaveAs(path);
}
So, what I'm looking for is more complex than just trying to map simple properties across objects. I'd like to keep a reference to my edit model out of my repository. Separation of concerns, and all that. Because of that, I need to map to an entity before I attempt to pass it to my repo for saving. I'm just not sure how to do it without blending app layers.
If I understand you, all you need to do is update your properties first prior to the call to AutoMapper.
Make sure your object has the correct values prior to calling AutoMapper.
After the call to do the mapping, your destination object will have all the matching properties copied over.
Post some more code if this doesn't answer the questoin.

Categories