If I have the following objects:
public class Application
{
public int ApplicationId { get; set; }
public string Name { get; set; }
public virtual ICollection<TestAccount> TestAccounts { get; set; }
}
public class TestAccount
{
public int TestAccountId { get; set; }
public int ApplicationId { get; set; }
public string Name { get; set; }
public virtual Application Application { get; set; }
}
EF Mapping looks like this:
modelBuilder.Entity<Application>()
.HasMany(a => a.TestAccounts)
.WithRequired(t => t.Application)
.WillCascadeOnDelete(false);
In one part of my code I want to retrieve data for Application and have
it return TestAccount data.
In another part of my code I want to retrieve data for Application and
have it NOT return TestAccount data.
Is there a way I can make this happen with LINQ or some other way?
This question has already been answered here: Disable lazy loading by default in Entity Framework 4.
Basically, in the constructor of your DbContext, just add this:
this.Configuration.LazyLoadingEnabled = false;
I hope this helps.
EDIT
Also, if you want to know how to load it manually later, it should be a simple matter of using Include() like this:
var query = context.Application.Include(x => x.TestAccounts).ToList()
Related
I am developing an ASP.Net Core Web API using EF Core Code First (C#) and SQL Server.
I have a fairly simple scenario which I just cannot figure out. I have a Form entity and a Site entity. Each Form can have many Sites and each Site can be in many Forms. To enable this I have a SiteForm joining table. For each Site associated with a Form there is a Leaving Date field. So my SiteForm class looks like this:
public class SiteForm
{
public Guid SiteId { get; set; }
public Site Site{ get; set; }
public Guid FormId { get; set; }
public Form Form{ get; set; }
public DateTime? LeavingDate { get; set; }
}
My Form's Data Transfer Object (FormDto) is as follows:
public class FormDto
{
public Guid Id { get; set; }
...
public ICollection<LinkedSiteDto> LinkedSites { get; set; }
= new List<LinkedSiteDto>();
}
And my LinkedSiteDto is like this:
public class LinkedSiteDto
{
public Guid Id { get; set; }
public string Name { get; set; }
public string Ident { get; set; }
public DateTime? LeavingDate { get; set; }
}
Having populated the database I can get the Sites for each Form using the following mapping:
CreateMap<Form, FormDto>()
.ForMember(dto => dto.LinkedSites, opt => opt.MapFrom(
form => form.SiteForms.Select(sf => sf.Site).ToList()));
I just cannot figure out how I would include the LeavingDate value from each joining table entry? Any suggestions would be very gratefully received.
Note: Technoligies in use are ASP.Net MVC 3, Entity, SQL Server Management Studio
Problem?
It seems that when I run, the context as: public class DatabaseInit : DropCreateDatabaseAlways<LocationAppContext>
That it creates the database, but my service assignments table has an extra foreign key called
ServiceAssignment_Service when it shouldn't.
My service assignment model is as such:
namespace LocationApp.Models
{
public class ServiceAssignment
{
public int id { get; set; }
public int locationID { get; set; }
public int ServiceID { get; set; }
public virtual Location Location { get; set; }
public virtual ServiceAssignment Service { get; set;}
}
}
and the service model is as such:
namespace LocationApp.Models
{
public class Service
{
public Service()
{
this.ServiceAssignments = new HashSet<ServiceAssignment>();
}
public int id { get; set; }
public string name { get; set; }
public string description { get; set; }
public bool active { get; set; }
public string icon { get; set; }
public virtual ICollection<ServiceAssignment> ServiceAssignments { get; set; }
}
}
with that said, the relation ship is simple:
service assignments have many location id's and service id's.
why is this extra foriegn key being generated? the curent keys, that should e there is:
PK: Main PK for the table
FK 1: Location_ServiceAssignment
FK 2: Service_ServiceAssignment
Those are their, how ever this third one is baffling....
The second part is: If a location of id 2 has a service id of 2,3,6,7 How do I get all service id's returned, such that I can pass the object to a service query to get all information on the service based on the ID?
Update:
Context Class:
namespace LocationApp.DAL
{
public class LocationAppContext : DbContext
{
public DbSet<Content> Contents { get; set; }
public DbSet<Location> Locations { get; set; }
public DbSet<ServiceAssignment> ServiceAssignments { get; set; }
public DbSet<Service> Services { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Location>().HasMany(sa => sa.ServiceAssignments);
modelBuilder.Entity<Service>().HasMany(sa => sa.ServiceAssignments);
}
}
}
I think you have to tell EF that Service.ServiceAssignments is the inverse navigation property of ServiceAssignment.Service and that Location.ServiceAssignments is the inverse of ServiceAssignment.Location. Right now with your mapping you only specify that Location or Service has many ServiceAssignments. EF will consider the navigation properties in ServiceAssignment as the ends of separate relationships.
Try instead the mapping:
modelBuilder.Entity<Location>()
.HasMany(l => l.ServiceAssignments)
.WithRequired(sa => sa.Location)
.HasForeignKey(sa => sa.LocationID);
modelBuilder.Entity<Service>()
.HasMany(s => s.ServiceAssignments)
.WithRequired(sa => sa.Service)
.HasForeignKey(sa => sa.ServiceID);
You can probably remove this mapping altogether as an alternative because EF should detect the right relationships by convention.
So, use either no mapping (=mapping by convention) or the full mapping (=specifying both ends of the relationships). Just the 50%-mapping you have used is likely the problem.
I'm using EF Code First. Now, I'm having a hard time figuring out how to write the LINQ to retrieve the data into my models in my Controller, to display them in a view. Basically, I am receiving a feed of HolterTest data, and I am trying to create a worklist for the people who do a bunch of specific tasks to process the HolterTest, allowing them to flag the tasks as they are completed, and provide status of where the individual tests are in the process The basic Task class is so they can add or alter steps in the process, with the displayOrder being the order in which tasks are done. A WorkTask is a specific instance of a task, allowing us to mark who completed it, and when. A WorkItem is the complex type that includes the HolterTest, the list of WorkTasks, and status information, including when the WorkTasks were all completed.
Model Classes:
public class HolterTest
{
public Int32 HolterTestID { get; set; }
public string PatientNumber { get; set; }
public string LastName { get; set; }
public DateTime RecordingStartDateTime { get; set; }
public System.Nullable<DateTime> AppointmentDateTime { get; set; }
}
public class Task
{
public Int32 TaskID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public Int32 DisplayOrder { get; set; }
public bool IsActive { get; set; }
}
public class WorkTask
{
public Int32 WorkTaskID { get; set; }
public Task Task { get; set; }
public bool IsCompleted { get; set; }
public System.Nullable<DateTime> CompletedDateTime { get; set; }
public string CompletedBy { get; set; }
}
public class WorkItem
{
public Int32 WorkItemID { get; set; }
public HolterTest HolterTest { get; set; }
public string Status { get; set; }
public List<WorkTask> WorkTasks { get; set; }
public bool IsStarted { get; set; }
public bool IsCompleted { get; set; }
public System.Nullable<DateTime> CompletedDateTime { get; set; }
}
Currently I have a business logic function that takes the list of HolterTests, finds the ones that don't have a WorkItem, and creates the WorkItems, associates the HolterTests including the WorkTasks, based on the current active list of Tasks.
My problem is how to write the LINQ to get all of the WorkItems (with their child items) for my WorkItemController so I can display the work to do in a View (WorkItems where IsCompleted = false) by PatientNumber, and make it possible to update WorkTasks for a particular WorkItem.
You want to access the related using it's navigation properties. Note that in your example, you haven't setup the navigation properties to be virtual. You should should update your model like this:
public class WorkItem
{
public Int32 WorkItemID { get; set; }
public virtual HolterTest HolterTest { get; set; }
public string Status { get; set; }
public virtual List<WorkTask> WorkTasks { get; set; }
public bool IsStarted { get; set; }
public bool IsCompleted { get; set; }
public System.Nullable<DateTime> CompletedDateTime { get; set; }
}
A simple function for accessing the work items by patient number is this:
IEnumerable<WorkItem> GetWorkIncompleteWorkItemsByPatient(string patientNumber)
{
var db = new YourContext();
return db.WorkItems.Where(wi => wi.IsCompleted == false && wi.HolterTest.PatientNumber == patientNumber);
}
Then to work on the related tasks, you would access it through the task, in this example if you knew the task ID:
var workTask = YourWorkItem.WorkTasks.FirstOrDefault(wt => wt.WorkTaskID == worktaskId);
You could look through all the tasks in the work item like this:
foreach (var workTask in YourWorkItem.WorkTasks)
{
//your logic here...
}
Linq to entities explained
http://msdn.microsoft.com/en-us/library/bb399367.aspx
But starting here may suit better: The EF Main Site http://msdn.microsoft.com/en-us/data/ee712907
if you really want to dive straight intry this video and sample code. http://msdn.microsoft.com/en-us/data/jj193542
then see this http://msdn.microsoft.com/en-us/data/jj573936
Essentially based on the POCO you have you could read per POCO and get the data that way.
However EF does a lot of heavy lifting if the POCOS have navigational properties and foreign keys defined. Worth revisiting the POCO definitions and Code-First patterns.
I'm having a trouble do the following mapping:
Domain (simplified version):
public class Ad
{
// Primary properties
public int Kms { get; set; }
// Navigation properties
public virtual Model Model { get; set; }
}
DTO:
public class CreateAdDto
{
// Primary properties
public int Kms { get; set; }
// Navigation properties
public virtual ModelDto Model { get; set; }
}
ViewModel:
public class CreateAdViewModel
{
// Primary properties
public int Kms { get; set; }
public int Make_Id { get; set; }
public int Model_Id { get; set; }
// Navigation properties
public IEnumerable<SelectListItem> MakeList { get; set; }
public IEnumerable<SelectListItem> ModelList { get; set; }
}
In the Controller, when I do the Mapping I'm loosing the Make_ID from the Dropdownlist of the View:
public virtual ActionResult Create(CreateAdViewModel adViewModel)
{
if (ModelState.IsValid)
{
var adDto = Mapper.Map<CreateAdViewModel, CreateAdDto>(adViewModel);
_adService.CreateAd(adDto);
}
return RedirectToAction(MVC.Home.Index());
}
The mapping is:
Mapper.CreateMap<CreateAdViewModel, CreateAdDto>()
Thanks.
As you have mentionned, the Ad need to know the Model_Id and to set it into the Model
Mapper.CreateMap<CreateAdDto, Ad>()
.ForMember(dest => dest.Model, opt => opt.MapFrom(src => new Model { Id = src.Model_Id }));
You also need from the other mapping side to let the Dto know where to get the Model id.
Mapper.CreateMap<Ad, CreateAdDto>()
.ForMember(dest => dest.Model_Id, opt => opt.MapFrom(src => src.Model.Id}));
The code above is not secure because a validation to see if Model is null should be added.
For the rest of your code, you seem to do it right. The section with Entity Framework requires you to attach because the entity Model already exist, otherwise, EF would insert this entity to the database.
CreateAdDto doesn't have a Make navigation property or a Make_Id property.
Solution found after some research:
ViewDomain:
public class CreateAdViewModel
{
// Primary properties
public int Kms { get; set; }
public int Make_Id { get; set; }
public int Model_Id { get; set; }
// Navigation properties
public IEnumerable<SelectListItem> MakeList { get; set; }
public IEnumerable<SelectListItem> ModelList { get; set; }
}
DTO:
public class CreateAdDto
{
// Primary properties
public int Kms { get; set; }
public int Model_Id { get; set; }
// Navigation properties
//public virtual ModelDto Model { get; set; }
}
Domain:
public class Ad
{
// Primary properties
public int Kms { get; set; }
// Navigation properties
public virtual Model Model { get; set; }
}
Viewmodel -> Dto Mapping:
Mapper.CreateMap<CreateAdViewModel, CreateAdDto>();
Dto -> Domain Mapping:
Mapper.CreateMap<CreateAdDto, Ad>()
.ForMember(dest => dest.Model, opt => opt.MapFrom(src => new Model { Id = src.Model_Id }));
Atention:
To achieve this with Entity Framework, I had to attach first the Model Entity to the Context and then Ad the new Ad:
public void CreateAd(CreateAdDto adDto)
{
var adDomain = Mapper.Map<CreateAdDto, Ad>(adDto);
_modelRepository.Attach(adDomain.Model);
_adRepository.Add(adDomain);
_adRepository.Save();
}
Hope this is the best practice.
BTW I would like to have some opinions regarding this aproach.
Thanks.
Based on what is I see in your question, I suggest a simple approach. Your application is medium scale. You should very carefully about maintainability,my experience say.So try to create a simple an strain forward approach for yourself like below approach:
I can describe all layer in detail but with notice to title of your question I prefer describe only Model(bussiness Ojbect) layer:
Good! As you can see PM.Model include:
Tow sub folders contain our ViewModels and in root of Library we have a .tt file contain Entity framework Objects (POCO classes) and we have a Mapper folder(Since that i dont like use autoMapper or third party like this :) ).
You can see IListBox interface in Domain layer. I put all ListBox container to this interface.
I hope current approach useful for you but finally I suggest remove one of this layers DTO or ViewModel, because in the future will be very complex.
Good luck
Do you aware about cost of these mapping?! You have 2 layers mapping (before arrived to Entity framework) for an simple insert.We can do more complex CRUD(s) in less than 2 layers mapping.
How to think about maintainability of this code?
Please keep DRY,KISS,SOLID conventions in your mind and top of your everyday work.
Good luck
I'm having trouble passing view information from my Get/Create action to my view. Here are my three model classes;
public class Competition
{
public int Id { get; set; }
public int CompetitionId { get; set; }
public string Name { get; set; }
public string Prize { get; set; }
}
public class CompetitionEntry
{
public int Id { get; set; }
public int CompetitionEntryId { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public int CompetitionId { get; set; }
}
public class CompetitionEntryViewModel
{
public int Id { get; set; }
public Competition Competitions { get; set; }
public int CompetitionId { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
Here is my Get/Create action in CompetitionEntry Controller;
public ActionResult Create(int id)
{
CompetitionEntryViewModel competitionentryviewmodel = db.CompetitionEntriesView.Find(id);
return View(competitionentryviewmodel);
}
I know this doesn't work. The id parameter goes into the URL fine. How to I get access to my Competition class in th Get action? I need to be able to show the competion name on my Create Competition entry view.
Thanks in advance!
public ActionResult Create(int id)
{
var data = db.CompetitionEntriesView.Find(id);
CompetitionEntryViewModel competitionentryviewmodel = new CompetitionEntryViewModel();
competitionentryviewmodel.CompetitionName = data.Name;
return View(competitionentryviewmodel);
}
What you are trying to do is build an object graph and display it through a view model. In order to do this, you need to map your domain model(s) to your view model.
You can do the mapping yourself by writing a lot of code (re-inventing the wheel), or, you could consider using third party tools to do this for you. I recommend you use an AutoMapper as it is very simple to use imo.
The other problem is that your view model contains a domain model. This is likely to cause you a lot of headache in near future. If I were you, I would replace Competition with CompetitionViewModel.
I would also consider creating a view model for a list of competitions, i.e. CompetitionsViewModel. Look into partial views to see how you can display a list of competitions.
Good luck