How to retrieve the attribute of related object in linq, select projection - json

I am trying to retrieve the Person name in my viewmodel while projection in the below code:
// GET api/Tickets
public IQueryable Get()
{
var model = Uow.Tickets.GetAll().OrderByDescending(m => m.DateTimeTag)
.Select(m => new TicketViewModel
{
Id = m.Id,
TicketTitle = m.TicketTitle,
TicketBody = m.TicketBody,
DateTimeTag = m.DateTimeTag,
//AssignedTo = Uow.Persons.GetById(m.AssignedToPersonId).Name,
Status = m.Status.ToString(),
NoOfReplys = m.Replys.Count()
});
return model;
}
But when I uncomment the AssignedTo line, it gives me the error:
InnerException: {
Message: "An error has occurred.",
ExceptionMessage: "LINQ to Entities does not recognize the method 'Ticketing.Model.Person GetById(Int32)' method, and this method cannot be translated into a store expression.",
ExceptionType: "System.NotSupportedException",
StackTrace: " at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.DefaultTranslator.Translate(ExpressionConverter parent, MethodCallExpression call) at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.TypedTranslate(ExpressionConverter parent, MethodCallExpression linq) at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq) at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq) blah blah blah
The TicketViewModel class is:
public class TicketViewModel
{
public int Id { get; set; }
public string TicketTitle { get; set; }
public string TicketBody { get; set; }
public DateTime DateTimeTag { get; set; }
public string AssignedTo { get; set; }
public string Status { get; set; }
public int NoOfReplys { get; set; }
}
The actual Ticket class is:
public class Ticket
{
public int Id { get; set; }
public string TicketTitle { get; set; }
public string TicketBody { get; set; }
public DateTime DateTimeTag { get; set; }
public int AssignedToPersonId { get; set; }
public Status Status { get; set; }
public virtual ICollection<Reply> Replys { get; set; }
}
My desired output is:
[
{
Id: 3,
TicketTitle: "a problem",
TicketBody: "problem descripted here.",
DateTimeTag: "2012-04-21T00:00:00",
AssignedTo: "Peter", <== ATTENTION!!!
Status: "Open",
NoOfReplys: 0
}
]
Here, Peter is the name of the person who its id is in the ticket object.
My goal is to show the name instead of personId.
may be there is a better way, please help me do that.
thanks

In this case I think that your property:
public int AssignedToPersonId { get; set; }
should be:
public Person AssignedToPerson { get; set; }
in your Ticket class. Mapping to the reference is generally better so that you can access properties like this using Linq. This way the line that is giving you trouble can be:
AssignedTo = AssignedToPerson.Name
The reason it isn't working right now is because Entity Framework has no idea how to convert your line:
Uow.Persons.GetById(m.AssignedToPersonId).Name
to a Query expression. By using a reference mentioned above you will instead create a Join between the two tables and get back the desired data in a single query.
The other and probably less attractive option is to store the Id in your View Model and then do a query for the name outside your Linq query. This will work because you have already retrieve items from the database. Untested example below:
public IQueryable Get()
{
var model = Uow.Tickets.GetAll().OrderByDescending(m => m.DateTimeTag)
.Select(m => new TicketViewModel
{
Id = m.Id,
TicketTitle = m.TicketTitle,
TicketBody = m.TicketBody,
DateTimeTag = m.DateTimeTag,
AssignedToPersonId = m.AssignedToPersonId,
Status = m.Status.ToString(),
NoOfReplys = m.Replys.Count()
}).ToList();
model.ForEach(m => m.AssignedTo = Uow.Persons.GetById(m.AssignedToPersonId).Name);
return model;
}
Note however that this second method is making an additional query to the database for each Ticket object returned in the first query.

Related

Return a non-mapped Entity property based on a SUM of child records in the EntityFramework

Using an example, I have the following two Entities. The OrderEntity contains a collection of OrderLineEntites
public class OrderEntity
{
public string Reference { get; set; }
public string Description { get; set; }
public bool Confirmed { get; set; }
[NotMapped]
public int OrderLineCount { get; set; }
[InverseProperty("Order")]
public virtual ICollection<OrderLineEntity> OrderLineEntity__OrderEntity { get; set; }
}
public class OrderLineEntity
{
public string Name { get; set; }
public string Description { get; set; }
}
Using the following code I can load all the OrderLineEntities for all confirmed orders.
DbSet<OrderEntity> orderEntity.Where(x => x.Confirmed).Include(x => x.OrderLineEntity__OrderEntity)
What I need to do is set the non-mapped OrderLineCount property with the Count of the OrderLine records (to save actually loading them).
So for each loaded Order I have a fully populated Entity including the [NotMapped] property with an empty OrderLine collection.
Advise would be appreciated :)
Thanks
You can do this, but you have to change your approach. You have to manually map the objects yourself:
var query = from a in context.Orders.Where(x => x.Confirmed)
select new OrderEntity
{
Reference = a.Reference,
Description = a.Description,
Confirmed = a.Confirmed,
OrderLineCount = a.OrderLineEntity__OrderEntity.Count
};
return query.ToList();

Populating an object from an SQL Query

I want to populate my Master Detail object with data from an SQL Server query.
However I don't seem to form the Detail part of the query correctly.
Is it even possible?
I have the following classes
public class OrderCountReportHeader : IMyReport
{
[Key]
public int Id { get; set; }
public DateTime RunAt { get; set; }
public string Name { get; set; }
public virtual List<OrderCountReportDetail> Details { get; set; }
}
public class OrderCountReportDetail {
public virtual OrderBankReportHeader Header { get; set; }
public string ProductCode { get; set; }
public int Quantity { get; set; }
}
and I want to populate an instance of OrderCountReportHeader from the output of an SQL Query
private static string ReportSql() {
return #"
SET NOCOUNT ON;
select 1 as Id, getdate() as RunAt, 'test' as Name;
select 'RC' as ProductCode , 5 as Quantity; "
;
}
var result = context.Database.SqlQuery<OrderCountReportHeader>(sql).ToArray()[0];
Assert.AreEqual(#"test", s.Name); // true
Assert.AreEqual("RC", s.Details[0].ProductCode); // fails because Details is null
I wound up using two separate calls to SqlQuery
though I would be pleased to know a better way.
var header = connect.Database.SqlQuery<OrderCountReportHeader>(ReportSqlHeader()).ToArray()[0];
var details = connect.Database.SqlQuery<OrderCountReportDetail>(ReportSqlDetails()).ToList();
foreach (var det in details)
{
det.Header = header;
}
header.Details = details;

Web API Json Serialization Exception & Dynamic Entities

I have a web API controller method that is returning an object which is giving the client a 500 internal server error. On the server, the output windows says "Newtonsoft.Json.JsonSerializationException". But I cannot see anything wrong with the class I am returning.. and I am sure this has historically been working. Any help would be greatly appreciated!
EDIT: Is this a problem with the web API not being able to serialize a 'dynamic entity'? The code that generates the class is here:
var id = User.Identity.GetUserId();
var user = db.Users
.Where(u => u.Id == id)
.Include(u => u.Friends)
.FirstOrDefault();
return user;
I am returning the following class;
public class User : IdentityUser
{
public User()
{
this.Friends = new List<UserFriend>();
}
public string PhoneNumber { get; set; }
public string Email { get; set; }
public List<UserFriend> Friends { get; set; }
public bool HasRegistered { get; set; }
public string LoginProvider { get; set; }
}
The 'UserFriend' class looks like this;
public class UserFriend
{
public int UserFriendId { get; set; }
public string Id { get; set; }
public string FriendUserId { get; set; }
public string FriendUserName { get; set; }
public string FriendPhoneNumber { get; set; }
}
Strangely, when I hover over the returned object on the server, the type is: {System.Data.Entity.DynamicProxies.User_7283E76A736B4DD47E89120E932CD5C04B62F84C316961F02CDAE3EEF4786504}. I am not sure what this is.. :-O
I used AutoMapper to create a DTO instead of just returning the User class. The DynamicProxies class is because the query uses lazy loading and it has not got the object yet.
After installing automapper (Install-Package AutoMapper);
Mapper.CreateMap<User, UserDto>();
UserDto dto = Mapper.DynamicMap<UserDto>(user);
Then return the dto.

i want a jqGrid class that can return json by mvc

Using jqGrid in mvc razor
I want a class that can go for jqGrid json format and bind the grid.
I think that you don't need to define any class to produce JSON data needed for jqGrid. You can return anonymous object:
public JsonResult DynamicGridData (string sidx, string sord, int page, int rows)
{
var query = ...;
var totalRecords = query.Count();
return Json(new {
total = (totalRecords + rows - 1) / rows,
page,
records = totalRecords,
rows = (from item in query
select new {
id = item.Id.ToString(),
cell = new[] {
item.FirstName,
item.LastName,
item.Votes.ToString(),
item.Title
}
}).ToList()
},
JsonRequestBehavior.AllowGet);
}
Firstly this is the wrong way of asking question at Stackoverflow.com please read Faq and whathaveyoutried.com
But since you are new to Stackoverflow I will answer this one for you,
public class JqGridModel<T>
{
public int page { get; set; }
public Int32? total { get; set; }
public Double? records { get; set; }
public IEnumerable<T> GridData { get; set; }
public JqGridModel<T> Bind(IEnumerable<T> data)
{
records = data.Count();
GridData = data;
page = 1;
return this;
}
}
also from your comments you said,
actually i am trying to use jqgrid with mvc razor and entity framework.
I recommend you go through this article once, it might be helpful. Here too a jqGrid class is defined as... the above one is more generic though, but it all depends on your use case.
public class JqGridObject
{
public string Page { get; set; }
public int PageSize { get; set; }
public string SortColumn { get; set; }
public string SortOrder { get; set; }
public List<Fruit> Data { get; set; }
}
public class Fruit
{
public int Id { get; set; }
public string Name { get; set; }
}

Linq to SQL filtered association?

public class ForumTopic
{
public Guid ForumTopicId { get; set; }
public Guid OwnerId { get; set; }
public Guid CategoryId { get; set; }
public DateTime CreatedDate { get; set; }
public string Topic { get; set; }
public bool IsSticky { get; set; }
public bool IsClosed { get; set; }
public int ViewCount { get; set; }
public int TotalComments { get; set; }
public Comment LastComment { get; set; }
}
I then have a Linq query and I need to figure out how to populate the LastComment and I can't create a new ForumTopic becuase Linq tells me that is breaking the rules...
IQueryable<ForumTopic> query = from topic in context.ForumTopics
join comment in context.Comments on topic.ForumTopicId equals comment.TargetId into topicComments
from lastComment in topicComments.DefaultIfEmpty().OrderByDescending(c => c.CreatedDate).Take(1)
orderby topic.IsSticky, topic.CreatedDate descending
select topic;
The query returns everything correct in SQL, however topic.LastComment is null.
Any ideas?
The main problem is you're not assigning the LastComment. Without a relationship established in the database, it has no idea how to fill that object.
You're going to need to manually assign the comment:
IQueryable<ForumTopic> query = from topic in context.ForumTopics
orderby topic.IsSticky, topic.CreatedDate descending
select new ForumTopic
{
ForumTopicId = topic.ForumTopicId,
OwnerId = topic.OwnerId,
// .. etc
LastComment = topic.Comments.OrderByDescending(c => c.CreatedDate).FirstOrDefault();
};
Obviously, I'm assuming you have a parent-child relationship between topic and comments. If you don't, you should reconsider how you're using linq :p