Relational Database SQL Query in Asp.NET Core - c#

public async Task<List<Note>>ShowAssigned()
{
return await _context.Notes
.Where(x => x.List.OwnerId != x.OwnerId)
.ToListAsync()
}
I get no syntax εrrors, but it seems you can't access attributes from related Data in this way.
Basically the goal is: A user creates a List, then some Notes for this List. Then he should be able to assign one of that Notes to another User. When that other User logs on, he should be able to see that new Note that was assigned to him.
Can anyone help me out with this?
public class List
{
public Guid ListId { get; set; }
public string OwnerId { get; set; }
public List<Note> Notes { get; set; }
}
public class Note
{
public Guid ID { get; set; }
public string OwnerId { get; set; }
[ForeignKey("ListId")]
public Guid ListId { get; set; }
public List List { get; set; }
}
And the context class:
public DbSet<Note> Notes { get; set; }
public DbSet<List> Lists { get; set; }
When i try to access Data the same way in a view like that:
#model List<Project.Models.Note>
#foreach (var item in Model)
{
if (item.List.OwnerId == item.OwnerId)
i get this error when running the web app (no syntax errors):
NullReferenceException: Object reference not set to an instance of an object

First write your model classes as follows:
public class List
{
public Guid ListId { get; set; }
public string OwnerId { get; set; }
public virtual List<Note> Notes { get; set; }
}
public class Note
{
public Guid ID { get; set; }
public string OwnerId { get; set; }
[ForeignKey("List")] // Not ListId, its List
public Guid ListId { get; set; }
public virtual List List { get; set; }
}
If your project is on ASP.NET Core < 2.1
Then write your query as follows:
await _context.Notes.Include(n => n.List).ToListAsync()
If your project is on ASP.NET Core >= 2.1
Then in the ConfigureServices() method in Startup class:
services.AddDbContext<ApplicationDbContext>(options =>
options.UseLazyLoadingProxies().UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
Don't forget to install appropriate version of Microsoft.EntityFrameworkCore.Proxies nuget package because UseLazyLoadingProxies() resides in this package.
Then write your query as follows:
await _context.Notes.ToListAsync()

So I found the answer to my problem, in some parts with the help of TanvirArjel (but i basically did it differently)
public async Task<List<Note>> GetAssignedItemsAsync(ApplicationUser user)
{
var lists = await _context.Lists.Include(l => l.Notes).Where(x => x.OwnerId != user.Id).ToListAsync();
var notesListe = new List<Note>();
foreach (List l in lists)
{
foreach (Note n in l.Notes)
{
if (n.OwnerId == user.Id)
{
notesListe.Add(n);
}
}
}
return notesListe;
}

Related

Handling Nested Objects in Entity Framework

I am struggling a bit to wrap my head around Entity Framework and It's driving me crazy. I have an target object that I'd like to populate:
public class ApiInvitationModel
{
public int Id { get; set; }
public EventModel Event { get; set; }
public UserModel InvitationSentTo { get; set; }
public UserModel AttendingUser { get; set; }
}
The schemas of the above models are:
public class EventModel {
public int Id? { get; set; }
public string Name { get; set; }
public DateTime? StartDate { get; set; }
public DateTime? EndDate { get; set }
public OrganizationModel HostingOrganization { get; set; }
public Venue Venue { get; set; }
public string Price { get; set; }
}
public class UserModel {
public int Id? { get; set; }
public string Name { get; set; }
public string PhoneNumber { get; set; }
public string MobileNumber { get; set; }
public List<OrganizationModel> Organizations { get; set; }
}
public class OrganizationModel {
public int Id? { get; set; }
public stirng Name { get; set; }
public string Address { get; set; }
public UserModel PrimaryContact { get; set; }
}
The above schemas are simplified for the purpose of the question and are the models we intend to return via API.
The problem is the origin schemas in the database is very different and I'm trying to map the database objects to these objects via Entity Framework 6.
My attempted solution was to try and nest the models via a query but that didn't work and I'm not sure where to go from here besides making numerous calls to the database.
public List<ApiInvitationModel> GetInvitations(int userId) {
using (var entities = new Entities()) {
return entities.EventInvitations
.Join(entities.Users, invitation => invitiation.userId, user => user.id, (invitation, user) => new {invitation, user})
.Join(entities.Events, model => model.invitation.eventId, ev => ev.id, (model, ev) => new {model.invitation, model.user, ev})
.Join(entities.organization, model => model.user.organizationId, organization => organization.id, (model, organization) => new ApiInvitationModel
{
Id = model.invitation.id,
Event = new EventModel {
Id = model.event.id,
Name = model.event.name,
StartDate = model.event.startDate,
EndDate = model.event.endDate,
HostingOrganization = new OrganizationModel {
Id = model.invitation.hostingId,
Name = model.event.venueName,
Address = model.event.address,
PrimaryContact = new UserModel {
Name = model.event.contactName,
PhoneNumber = model.event.contactNumber,
}
}
...
},
InvitedUser = {
}
}
).ToList();
}
}
As you can see above, there's quite a bit of nesting going on but this doesn't work in Entity Framework 6 as far as I am aware. I keep getting the following errors:
"The type 'Entities.Models.API.UserModel' appears in two structurally incompatible initializations within a single LINQ to Entities query. A type can be initialized in two places in the same query, but only if the same properties are set in both places and those properties are set in the same order.",
Based on the above error, I assumed that each of the model initiatilizations would need to be the same (i.e. initializing the values as the same ApiInvitationModel in each join in the same order) but that produces the same error.
What would be the best approach to handling this, keepign in mind the source database doesn't have foreign keys implemented?

c# asp.net make a get request on relational table with entity framework database

I am trying to get data from a table with a get request with a controller. When I make the request with a normal table (TestTable) it is ok, but if I make the request with a relational table I get the fail message:
"The 'ObjectContent`1' type failed to serialize the response body for
content type 'application/xml; charset=utf-8'."
My controller (Mdata):
namespace ScThAsp.Controllers
{
public class MDataController : ApiController
{
public IEnumerable<Måledata> Get()
{
using (var e = new SCTHDBEntities())
{
return e.Måledata.ToList();
}
}
public TestTable Get(int id)
{
using (SCTHDBEntities entities = new SCTHDBEntities())
{
return entities.TestTable.FirstOrDefault(e => e.Id == 1);
}
}
}
}
My Table for måledata is:
public partial class Måledata
{
public int MDid { get; set; }
public Nullable<int> BBid { get; set; }
public Nullable<decimal> Måling1 { get; set; }
public Nullable<decimal> Måling2 { get; set; }
public Nullable<decimal> Måling3 { get; set; }
public Nullable<decimal> Måling4 { get; set; }
public Nullable<System.DateTime> RegTid { get; set; }
public virtual BuildBoard BuildBoard { get; set; }
}
My database looks like:
Database
See link..
I think I mayby should make a inner join with the other table connected to Måledata table - I am not sure how to do that in a EF environment.
I have really tried a lot now - hope for an answer. Thanks
Your class Måledata contains more data that you presented (it is marked as partial) and probably contains stuff related to EF. This magic stuff is not serializable. To avoid problem rewrite results to a plain object with properties you need. This object must be serializable (if contains plain properties and classes it will).
Building upon Piotr Stapp's answer you need to create a DTO (Data Transfer Object) for your Måledata which contains properties as your model, Måledata other than the EF properties. Use some sort of Mapper, maybe AutoMapper to map the required properties in your final response.
public class MaledataDTO
{
public int MDid { get; set; }
public int? BBid { get; set; }
public decimal? Måling1 { get; set; }
public decimal? Måling2 { get; set; }
public decimal? Måling3 { get; set; }
public decimal? Måling4 { get; set; }
public DateTime? RegTid { get; set; }
//... other properties
}
public IEnumerable<MaledataDTO> Get()
{
using (var e = new SCTHDBEntities())
{
var result = e.Måledata.ToList();
return Mapper.Map<List<MaledataDTO>>(result);
}
}
I found 2 solutions.
1) Solution was with Automapper (thanks Abdul). Installing automapper and a Using Automapper. Added a class called MåledataDTO : ` public class MåledataDTO
{
public int MDid { get; set; }
public int? BBid { get; set; }
public decimal? Måling1 { get; set; }
public decimal? Måling2 { get; set; }
public decimal? Måling3 { get; set; }
public decimal? Måling4 { get; set; }
public DateTime? RegTid { get; set; }
//... other properties
}
`
In my controller I used the following code
public IEnumerable<MåledataDTO> Get()
{
using (var e = new SCTHDBEntities())
{
Mapper.Initialize(config =>
{
config.CreateMap<Måledata, MåledataDTO>();
});
var result = e.Måledata.ToList();
return Mapper.Map<List<MåledataDTO>>(result);
2: In the second solution: In the picture you see the relations bewtween the tables - made in VS - but that creates a problem in the tables Get SET classes. The relation creates a Virtual object in the class - like mentioned before
public virtual BuildBoard BuildBoard { get; set; }
If you delete the relations and make the public partial class Måledata like
in this video
https://www.youtube.com/watch?v=4Ir4EIqxYXQ
the controller should then have one of two solutions:
using (SCTHDBEntities e = new SCTHDBEntities()) {
//this works
//var knud = new List<Måledata>();
//knud = (e.BuildBoard.Join(e.Måledata, c => c.BBid, o => o.BBid,
// (c, o) => o)).ToList();
//return knud;
//this works too
return (from p in e.BuildBoard
where p.BBid == 1
from r in e.Måledata
where r.BBid == p.BBid
select p).ToList();
That was that
Gorm

Entity Framework materializes collections inside IQueryable methods before their execution

On my previous work EF keeps query as IQueryable until I used some materialized methods, for example, ToList() or FirstOrDefault(), and I could create big, flexible and fast queries. But on my new work I noted, that sequences inside IQueryable methods have ICollection type and, of course, they have IEnumerable methods (not IQueriable). I can't understand what wrong and how I can change it. I haven't find solution in google. The version of EF is the same as on my previous work (6.1.3).
For Example, I have 2 entity classes and my own class:
public class Client // Entity
{
public int ID { get; set; }
public string FullName { get; set; }
public string Address { get; set; }
...
public virtual ICollection<Parcel> ParcelsSender { get; set; }
public virtual ICollection<Parcel> ParcelsReceiver { get; set; }
}
public class Parcel // Entity
{
public int ID { get; set; }
public string ParcelNumber { get; set; }
...
public int ClientSenderID { get; set; }
public int ClientReceiverID { get; set; }
public virtual Client ClientSender { get; set; }
public virtual Client ClientReceiver { get; set; }
}
public class ClientCustom // My class
{
public int ID { get; set; }
public string FullName { get; set; }
public bool IsInexperienced { get; set; }
}
and I created this EF query:
var clients = context.Clients
.OrderBy(x => x.FullName)
.Select(x => new ClientCustom()
{
ID = x.ID,
FullName = x.FullName,
IsInexperienced = x.ParcelsSender.Select(y => y.ID).FirstOrDefault() == 0
&& x.ParcelsReceiver.Select(y => y.ID).FirstOrDefault() == 0
})
.ToList();
In this case, the problem is that x.ParcelsSender and x.ParcelsReceiver in query are ICollection<Parcel> type; In turn, x.ParcelsSender.Select() and x.ParcelsReceiver.Select() methods returns IEnumerable<Parcel> instead IQueriable<Parcel>. As I know, that means, queries become very slowly with big amount of data.

EF Core returns only first record of a list unless FK entities are reset

I am facing the same issue as described in this question. Problem: my method GetAllConferences() returns correctly all the conferences from the DB, but when I return the result to the View from the controller return Ok(tripListVm) inly the first collection item is returned to the client. On the otehr side, by setting to null all the FK references (as pointed out in the SO question above) I can return correctly all the entities to the client, however this does not seem to me the proper way of proceeding.
EDIT: the solution was much simpler than I though. In the code below (I leave it in its original form for others to see it) I was not mapping the FK entities inside the ViewModel to Dto objects, but returning the model entity itself. That was the reason why I needed to null those inner references to make it work. By returning all Dtos objects, it works properly.
I have three entities involved with 1-many relationships:
public class Conference
{
public int Id { get; set; }
[Required]
[MaxLength(50)]
public string Name { get; set; }
public ICollection<Venue> Venues { get; set; }
public int? LocationId { get; set; }
public Location Location { get; set; }
}
public class Venue
{
public int Id { get; set; }
[Required]
[MaxLength(50)]
public string Name { get; set; }
public int? ConferenceId { get; set; }
public Trip Conference { get; set; }
public int? LocationId { get; set; }
public City City { get; set; }
}
public class City
{
public int Id { get; set; }
[Required]
[MaxLength(50)]
public string Name { get; set; }
public ICollection<Conference> Conferences { get; set; }
public ICollection<Venue> Venues { get; set; }
}
In the repository, I have a method that returns the conferences and the related entities (City and Venues):
public IEnumerable<Conference> GetAllConferences()
{
return _context.Conferences
.Include(t => t.Venues)
.Include(t => t.City)
.ToList();
}
In the controller I need to use the following code to return all the results:
var conferences = _repository.GetAllConferences();
if (conferences.Any())
{
var conferenceListVm = trips.ToConferenceVmList();
//Without setting the FK references to null, I can return only the first result of the collection
foreach (var vm in conferenceListVm)
{
foreach (var pm in vm.PoinOfInterests)
{
pm.Trip = null;
}
vm.Location.Conferences = null;
vm.Location.Venues = null;
}
return Ok(conferenceListVm);
}
public static ConferenceViewModel ToConferenceVm(this Conference conference)
{
var confVm = new ConferenceViewModel();
confVm.Name = conference.Name;
confVm.City = conference.City;
confVm.Venues = conference.Venues;
return tripVm;
}
public static IEnumerable<ConferenceViewModel> ToConferenceVmList(this IEnumerable<Conference> conferences)
{
return conferences.Select(c => c.ToConferenceVm()).ToList();
}

What's the proper way to get data using Entity Framework so you can navigate through objects?

I have a .Net 4.5 MVC 5 database first project that I'm playing around with. There's a data access layer (Entity Framework 6), a business logic layer and the MVC layer.
If I have an object with relationships in the data layer:
namespace DataAccess
{
public class Course
{
public int CourseID { get; set; }
public string Title { get; set; }
public ICollection<Lecture> Lectures { get; set; }
public ICollection<Tutor> Tutors { get; set; }
}
public class Lecture
{
public int LectureID { get; set; }
public string Title { get; set; }
public DateTime Date { get; set; }
public ICollection<Student> Students { get; set; }
}
public class Tutor
{
public int TutorID { get; set; }
public string Name { get; set; }
}
public class Student
{
public int StudentID { get; set; }
public string Name { get; set; }
}
}
And in my business logic layer I have a method that gets courses:
namespace BusinessLogic
{
public static IEnumerable<Course> GetCourses()
{
using (var db = new MyEntities())
{
return db.Courses.Include("Lectures").Include("Lectures.Students").Include("Tutors").ToList();
}
}
}
And I get the data using my controller like this:
public class HomeController : Controller
{
public ActionResult Index()
{
var courses = BusinessLogic.GetCourses();
return View(courses);
}
}
Why is it, when I query my data in the Razor view like this:
var numLectures = courses.Lectures.Count;
var numStudents = courses.Lectures.Students.Count;
var tutorName = courses.Tutors.LastOrDefault().Name;
I get the application error System.ObjectDisposedException: The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.
I know the connection is disposed after the using statement has finished and that .ToList() will let me navigate the courses object, but how do I navigate the objects inside each course (i.e. lectures, students, tutors etc.)?
Your navigation properties need to be declared as virtual:
namespace DataAccess
{
public class Course
{
public int CourseID { get; set; }
public string Title { get; set; }
public virtual ICollection<Lecture> Lectures { get; set; }
public virtual ICollection<Tutor> Tutors { get; set; }
}
public class Lecture
{
public int LectureID { get; set; }
public string Title { get; set; }
public DateTime Date { get; set; }
public virtual ICollection<Student> Students { get; set; }
}
...
}
When these lazy loadable properties are not marked as virtual, the EF dynamic proxies cannot override them and you will never be able to navigate from one entity to a (set of) another.
Another bit of advice: use the strongly-typed .Include when eager loading:
namespace BusinessLogic
{
public static IEnumerable<Course> GetCourses()
{
using (var db = new MyEntities())
{
return db.Courses
.Include(x => x.Lectures.Select(y => y.Students))
.Include(x => x.Tutors)
.ToList();
}
}
}
I think the problem is because one (or more than one) property that you are calling in your View is (are) not included in your query. Make sure you are including all the navigation properties you need in the view. Try with this query:
using (var db = new MyEntities())
{
return db.Courses.db.Courses.Include(c=>c.Lectures.Select(l=>l.Students)).Include(c=>c.Tutors‌​).ToList()
}
If you need to add another relative property that you use in your View, then add another Include call for that property.
Another thing, when you need to eager load two levels (like Lectures.Students), you don't need to add a Include call for each level, with the call that you do for the second level is enough to include both. Could be this way:
.Include("Lectures.Students") // as you did it
Or:
.Include(c=>c.Lectures.Select(l=>l.Students))

Categories