Populating an object from an SQL Query - sql-server-2008

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;

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();

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

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.

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; }
}

Issue when insert into mysql using PetaPoco Database.Insert<T>

I have a small project using PetaPoco to re-implement Membership Provider, Role Provider and Profile Provider of ASP.NET. And each table has two common field: name and lowered_name. And I face an issue when try to insert new record into database using Database.Insert(), the lowered_name field can not be insert.
E.g we have a table called Category
CREATE TABLE `category` (
`name` varchar(100) NOT NULL,
`lowered_name` varchar(100) NOT NULL,
PRIMARY KEY (`lowered_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
And a Category entity:
[TableName("category"), PrimaryKey("lowered_name")]
public class Category
{
[Column("name")]
public string Name { get; set; }
[Column("lowered_name")]
public string LoweredName { get; set; }
}
I have use PetaPoco to insert a new category like below
var name = Guid.NewGuid().ToString();
var cat = new Category() { Name = name, LoweredName = name.ToLower() };
var db = new Database("Test");
db.Insert(cat);
var retrievecat = db.SingleOrDefault<Category>("where lowered_name like #0", name.ToLower());
Console.WriteLine(retrievecat.LoweredName);
Assert.IsTrue(retrievecat != null);
However, this code doesn't work, a new record was create but the lowered_name field of the record is null.
When I change the db.Insert(cat) to:
db.Execute("insert into category(name, lowered_name) values (#0, #1)", name, name.ToLower());
Everything is fine.
I finished implement a lot of code for these providers above and they work well with PostGreSQL and SQL Server, and I don't want to re-implement every Insert function of each repository (I create a common Insert function in the AbstractRepository class).
Do you have any idea?
SchoTime has helped me to fixed the problem if lowered_name is a primary key. However, in my real app, name and lowered_name are not primary key.
I use an abstract class:
public abstract class AbstractEntity
{
#region Fields
private string _name;
#endregion Fields
#region Properties
[PetaPoco.Column(Name = "id")]
public long Id { get; set; }
[Required]
[PetaPoco.Column(Name = "name")]
public string Name
{
get { return _name; }
set
{
_name = value;
LoweredName = value.ToLower();
}
}
[Required]
[PetaPoco.Column(Name = "lowered_name")]
public string LoweredName { get; set; }
[PetaPoco.Column(Name = "description")]
public string Description { get; set; }
#endregion Properties
#region Initialization
public AbstractEntity()
{
}
public AbstractEntity(string name)
{
Name = name;
}
#endregion Initialization
}
And the User entity:
[PetaPoco.TableName("vietspring_security_user"), PetaPoco.PrimaryKey("id")]
public class User:AbstractEntity
{
#region Private Variables
private string _email;
#endregion
#region Constructors
public User():base()
{
InitMembers();
}
public User(int id)
{
Id = id;
InitMembers();
}
public User(string name): base(name)
{
Name = name;
InitMembers();
}
#endregion
#region Properties
[PetaPoco.Column("password")]
public virtual string Password { get; set; }
[PetaPoco.Column("password_format")]
public virtual int PasswordFormat { get; set; }
[PetaPoco.Column("password_salt")]
public virtual string PasswordSalt { get; set; }
[PetaPoco.Column("email")]
public virtual string Email
{
get
{
return _email;
}
set
{
_email = value;
LoweredEmail = value.ToLower();
}
}
[PetaPoco.Column("lowered_email")]
public virtual string LoweredEmail { get; set; }
[PetaPoco.Column("password_question")]
public virtual string PasswordQuestion { get; set; }
[PetaPoco.Column("password_answer")]
public virtual string PasswordAnswer { get; set; }
[PetaPoco.Column("comments")]
public virtual string Comments { get; set; }
[PetaPoco.Column("is_approved")]
public virtual bool IsApproved { get; set; }
[PetaPoco.Column("is_locked_out")]
public virtual bool IsLockedOut { get; set; }
[PetaPoco.Column("creation_date")]
public virtual DateTime CreationDate { get; set; }
[PetaPoco.Column("last_activity_date")]
public virtual DateTime LastActivityDate { get; set; }
[PetaPoco.Column("last_login_date")]
public virtual DateTime LastLoginDate { get; set; }
[PetaPoco.Column("last_locked_out_date")]
public virtual DateTime LastLockedOutDate { get; set; }
[PetaPoco.Column("last_password_change_date")]
public virtual DateTime LastPasswordChangeDate { get; set; }
[PetaPoco.Column("failed_password_attempt_count")]
public virtual int FailedPasswordAttemptCount { get; set; }
[PetaPoco.Column("failed_password_attempt_window_start")]
public virtual DateTime FailedPasswordAttemptWindowStart { get; set; }
[PetaPoco.Column("failed_password_answer_attempt_count")]
public virtual int FailedPasswordAnswerAttemptCount { get; set; }
[PetaPoco.Column("failed_password_answer_attempt_window_start")]
public virtual DateTime FailedPasswordAnswerAttemptWindowStart { get; set; }
[PetaPoco.ResultColumn]
public virtual IList<Application> Applications { get; set; }
#endregion
...
}
And in MembershipProvider, I call these below methods to create new User:
var userId = Convert.ToInt64(_userRepository.Insert(user));
_userRepository.AddApplication(app.Id, userId);
status = MembershipCreateStatus.Success;
I'm success in save user but name and lowered_name fields are null. How about this situation?
Because you have a primary key of lowered_name and it doesn't appear to be an auto increment column then you must set autoIncrement false as below.
[PrimaryKey("lowered_name", autoIncrement=false)]
AutoIncrement is true by default.

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