SQl to Linq query- subquery with datetime - linq

I would like to know the LINQ for this SQL.Appreciate any help
select Value FROM GPUD where param ='RVOUTc'
and dateandtime in (select max(b.dateandtime) from GPUD as b where b.param= 'rvoutc'and convert(date,getdate()) = convert(date,b.DateAndTime)
group by b.param )

Did not test it, but it should get you started, good luck.
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
var GPUDCollection = new List<GPUD>();
var maxDate = GPUDCollection.
Max(gpud => gpud.dateandtime.Date);
var result = GPUDCollection.
Where(gpud => gpud.Param.Equals("rvoutc") && gpud.dateandtime.Date.Equals(maxDate)).
Select(gpud => gpud.Value);
}
}
public class GPUD
{
public DateTime dateandtime { get; set; }
public string Param { get; set; }
public object Value { get; set; }
}

Related

Fluent LINQ EF Core - Select filtered child property

I have the classes below:
public class User
{
public Guid Id { get; set; }
public string Name { get; set; }
}
public class ParentEntity
{
public Guid Id { get; set; }
public string SomeProperty { get; set; }
public ICollection<ChildEntity> ChildEntities { get; set; }
}
public class ChildEntity
{
public Guid Id { get; set; }
public int Vote { get; set; }
public Guid UserId { get; set; }
}
public class ReturnedParentDto
{
public Guid Id { get; set; }
public string SomeProperty { get; set; }
public int Vote { get; set; }
}
I want to be able to return a full list of ParenEntities, but take an Id of the User class (UserClassId), then filter the ParentEntity's ICollection where UserUid = UserClassId, so only 1 ChildEntity is always returned. Then I would want to extract a specific field from that returned ChildEntity and merge it with the ParentEntity fields. The end result should be like the ReturnedParentDto.
I want to do it in the style like
ParentEntities.Include(v => v.ChildEntities).ToList()
That seems to be possible in EF Core 5, but my project is in 3.1.
You can do this as below
Approach 1:
var result = result = parentEntities.Include(x => x.ChildEntities.Where(y => y.UserId == userId))
.Select(x => new ReturnedParentDto {
Id = x.Id,
SomeProperty = x.SomeProperty,
Vote = x.ChildEntities.FirstOrDefault()?.Vote // userId is the variable here
});
Approach 2:
var result = parentEntities.Select(x =>
new ReturnedParentDto {
Id = x.Id,
SomeProperty = x.SomeProperty,
Vote = x.ChildEntities.FirstOrDefault(y => y.UserId == userId)?.Vote // userId is the variable here
});

Linq query which has a non db object within

I have multiple View Model classes that get data from the database using Linq (DBContext classes have a similar structure but with more fields)
I am lacking knowledge to get data from one class object because that is contained within a wrapper class that does not have a corresponding db object.
Thank you in advance for any help or suggestions on this.
Here are the class structures:
public class TopLevelViewModel
{
public TopLevelID { get; set; }
public TopLevelTitle { get; set; }
public List<SecondLevelViewModel> SeconLevel { set; get; }
public TopLevelViewModel()
{
Inner = new List<SecondLevelViewModel>();
}
}
public class SecondLevelViewModel
{
public SecondLevelID { get; set; }
public SecondLevelTitle { get; set; }
public TopLevelID { get; set; }
public WrapperViewModel { get; set; }
}
public class InnerViewModel
{
public InnerID { get; set; }
public InnerTitle { get; set; }
public SecondLevelID { get; set; }
}
public class WrapperViewModel
{
public List<InnerViewModel> Inner { set; get; }
public WrapperViewModel()
{
Inner = new List<InnerViewModel>();
}
}
Linq query to get data for the above db objects:
List<TopLevelViewModel> data = null;
data = db.TopLevel.Where(t => t.TopLevelID == URLTopLevelID)
.Select(t => new TopLevelViewModel()
{
TopLevelID = t.TopLevelID,
TopLevelTitle = t.TopLevelTitle,
SeconLevel = db.SecondLevel.Where(s => s.TopLevelID == t.TopLevelID)
.Select(s => new SecondLevelViewModel()
{
SecondLevelID = s.SecondLevelID,
SeconLevelTitle = s.SeconLevelTitle,
WrapperViewModel = ???
}).ToList<SecondLevelViewModel>()
}).ToList < TopLevelViewModel();
Query to get Inner object data:
db.Inner.Where(i => i.SecondLevelID == s.SecondLevelID)
.Select(i => new InnerViewModel()
{
InnerID = i.InnerID,
InnerTitle = i.InnerTitle
}).ToList<InnerViewModel>()
Question: how do I incorporate the above inner query to be part of WrapperViewModel class and have that in the main Linq query as a single query?
WrapperViewModel does not have a DB object but it is kind of a wrapper class to InnerViewModel class.
I think I figured it out. Hope it will be useful to someone. If there are any ideas/suggestions please share!
The single query would be:
List<TopLevelViewModel> data = null ;
data = db.TopLevel.Where(t => t.TopLevelID == URLTopLevelID).Select(t => new TopLevelViewModel()
{
TopLevelID = t.TopLevelID,
TopLevelTitle = t.TopLevelTitle,
SeconLevel = db.SecondLevel.Where(s => s.TopLevelID == t.TopLevelID).Select(s => new SecondLevelViewModel()
{
SecondLevelID = s.SecondLevelID,
SeconLevelTitle = s.SeconLevelTitle,
WrapperViewModel = new WrapperViewModel { Inner =
db.Inner.Where(i => i.SecondLevelID == s.SecondLevelID).Select(i => new InnerViewModel()
{
InnerID = i.InnerID,
InnerTitle = i.InnerTitle
}).ToList<InnerViewModel>()
}).ToList<SecondLevelViewModel>()
}).ToList < TopLevelViewModel();

Speed up large Linq to Entities call MVC3

Below is the code I am using and the database table it is pulling from has about 92000 records in it. The way it is pulling right now it is pulling all 92000 records then doing the filtering.
What I am looking to do is the filtering on the initial pull from the DB so that it does not take aproximately 40 seconds to load the page.
This is something I am still new at so I am lost as to how to do this and make it work with my view
public ViewResult Makes()
{
var items = (from item in DBCacheHelper.recallslist
orderby item.MFGTXT ascending
select item.ToDomainRecall()).GroupBy(item => item.MFGTXT).Select(grp => grp.First());
return View(items);
}
public static IEnumerable<Recall> recallslist
{
get
{
if (c["GetAllRecalls"] == null)
{
c.Insert("GetAllRecalls", GetAllRecalls());
return (IEnumerable<Recall>)c["GetAllRecalls"];
}
else
{
return (IEnumerable<Recall>)c["GetAllRecalls"];
}
}
}
public static IEnumerable<Recall> GetAllRecalls()
{
using (DealerContext context = new DealerContext())
{
var items = from item in context.recalls.ToList<Recall>()
select item.ToDomainRecall();
return items.ToList<Recall>();
}
}
SELECT
[Extent1].[RecallsId] AS [RecallsId],
[Extent1].[RECORD_ID] AS [RECORD_ID],
[Extent1].[CAMPNO] AS [CAMPNO],
[Extent1].[MAKETXT] AS [MAKETXT],
[Extent1].[MODELTXT] AS [MODELTXT],
[Extent1].[YEARTXT] AS [YEARTXT],
[Extent1].[MFGCAMPNO] AS [MFGCAMPNO],
[Extent1].[COMPNAME] AS [COMPNAME],
[Extent1].[MFGNAME] AS [MFGNAME],
[Extent1].[BGMAN] AS [BGMAN],
[Extent1].[ENDMAN] AS [ENDMAN],
[Extent1].[RCLTYPECD] AS [RCLTYPECD],
[Extent1].[POTAFF] AS [POTAFF],
[Extent1].[ODATE] AS [ODATE],
[Extent1].[INFLUENCED_BY] AS [INFLUENCED_BY],
[Extent1].[MFGTXT] AS [MFGTXT],
[Extent1].[RCDATE] AS [RCDATE],
[Extent1].[DATEA] AS [DATEA],
[Extent1].[RPNO] AS [RPNO],
[Extent1].[FMVSS] AS [FMVSS],
[Extent1].[DESC_DEFECT] AS [DESC_DEFECT],
[Extent1].[CONEQUENCE_DEFECT] AS [CONEQUENCE_DEFECT],
[Extent1].[CORRECTIVE_ACTION] AS [CORRECTIVE_ACTION],
[Extent1].[NOTES] AS [NOTES],
[Extent1].[RCL_CMPT_ID] AS [RCL_CMPT_ID]
FROM [dbo].[Recalls] AS [Extent1]
Update:
Ultimately I would like to only pull records from the Recalls Table where the MFGTXT is equal to the
MakeName in the AutoMake Table
public class AutoMake
{
[Key]
public int MakeID { get; set; }
public string MakeName { get; set; }
public AutoMake ToDomainAutoMakes()
{
return new AutoMake
{
MakeID = this.MakeID,
MakeName = this.MakeName
};
}
}
public class Recall
{
[Key]
public int RecallsId { get; set; }
public string RECORD_ID { get; set; }
public string CAMPNO { get; set; }
public string MAKETXT { get; set; }
public string MODELTXT { get; set; }
public string YEARTXT { get; set; }
public string MFGCAMPNO { get; set; }
public string COMPNAME { get; set; }
public string MFGNAME { get; set; }
public string BGMAN { get; set; }
public string ENDMAN { get; set; }
public string RCLTYPECD { get; set; }
public string POTAFF { get; set; }
public string ODATE { get; set; }
public string INFLUENCED_BY { get; set; }
public string MFGTXT { get; set; }
public string RCDATE { get; set; }
public string DATEA { get; set; }
public string RPNO { get; set; }
public string FMVSS { get; set; }
public string DESC_DEFECT { get; set; }
public string CONEQUENCE_DEFECT { get; set; }
public string CORRECTIVE_ACTION { get; set; }
public string NOTES { get; set; }
public string RCL_CMPT_ID { get; set; }
public Recall ToDomainRecall()
{
return new Recall
{
RECORD_ID = this.RECORD_ID,
CAMPNO = this.CAMPNO,
MAKETXT = this.MAKETXT,
MODELTXT = this.MODELTXT,
YEARTXT = this.YEARTXT,
MFGCAMPNO = this.MFGCAMPNO,
COMPNAME = this.COMPNAME,
MFGNAME = this.MFGNAME,
BGMAN = this.BGMAN,
ENDMAN = this.ENDMAN,
RCLTYPECD = this.RCLTYPECD,
POTAFF = this.POTAFF,
ODATE = this.ODATE,
INFLUENCED_BY = this.INFLUENCED_BY,
MFGTXT = this.MFGTXT,
RCDATE = this.RCDATE,
DATEA = this.DATEA,
RPNO = this.RPNO,
FMVSS = this.FMVSS,
DESC_DEFECT = this.DESC_DEFECT,
CONEQUENCE_DEFECT = this.CONEQUENCE_DEFECT,
CORRECTIVE_ACTION = this.CORRECTIVE_ACTION,
NOTES = this.NOTES,
RCL_CMPT_ID = this.RCL_CMPT_ID
};
}
}
If you want to add server side filtering outside of your repository methods, you need to return your types as IQueryable rather than IEnumerable and not call .ToList, .AsEnumerable, or any other method that would cause .GetEnumerator to be called. Additionally, your cast `(IEnumerable)c["GetAllRecalls"];' forces LINQ to Objects to be used for subsequent requests rather than retaining the expression tree and using Entity Framework. That being said, you may need to move your call to ToDomainRecall method to after the additional filter is applied as well because that can't be translated to your database. Here are some of the changes you would need to make:
public ViewResult Makes()
{
var items = (from item in DBCacheHelper.recallslist
orderby item.MFGTXT ascending
select item.ToDomainRecall()).GroupBy(item => item.MFGTXT).Select(grp => grp.First());
return View(items);
}
public static IQueryable<Recall> recallslist
{
get
{
if (c["GetAllRecalls"] == null)
{
c.Insert("GetAllRecalls", GetAllRecalls(context));
}
return c["GetAllRecalls"];
}
}
public static IQueryable<Recall> GetAllRecalls(DealerContext context)
{
var items = context.recalls;
return items;
}
Looks like your DBaccess is done in the call to DBCacheHelper.recallslist.
You need to edit the sql that runs from/in this function.
As Eranga pointed out, you don't show how you are filtering the large number down to a smaller number of records. I assume you want 20 or 100 at a time? If so, please see the accepted answer here:
efficient way to implement paging
Specifically, this part which shows how to only retrieve rows x to y (where x = #p0 + 1 AND y = #p0 + #p1):
SELECT [t1].[CodCity],
[t1].[CodCountry],
[t1].[CodRegion],
[t1].[Name],
[t1].[Code]
FROM (
SELECT ROW_NUMBER() OVER (
ORDER BY [t0].[CodCity],
[t0].[CodCountry],
[t0].[CodRegion],
[t0].[Name],
[t0].[Code]) AS [ROW_NUMBER],
[t0].[CodCity],
[t0].[CodCountry],
[t0].[CodRegion],
[t0].[Name],
[t0].[Code]
FROM [dbo].[MtCity] AS [t0]
) AS [t1]
WHERE [t1].[ROW_NUMBER] BETWEEN #p0 + 1 AND #p0 + #p1
ORDER BY [t1].[ROW_NUMBER]

Asp.net MVC 3 Linq DateTime

I need a linq statement that returns all entries from a certain date. A the moment I have a class in my controller which handles Events. My Index class contains a linq statement which groups the vents by date and returns how many there are in each date. I want a browse class which returns a list of Events connected with a certain date. Here is my mode:
namespace NewAtAClick.Models
{
public class WhatsOn
{
public int ID { get; set; }
public DateTime? start { get; set; }
public DateTime? end { get; set; }
public string Name { get; set; }
public string Desc { get; set; }
public string link { get; set; }
public bool CalenderDisplay { get; set; }
public DateTime? day { get; set; }
public int whtscount { get; set; }
}
}
And here's my classes in the WhatsOn controller;
public ViewResult Index(WhatsOn model)
{
DateTime myDate = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day);
var datequery =
db.WhatsOns.Where(c => c.start > myDate)
.OrderByDescending(c => c.start)
.GroupBy(c => c.start).AsEnumerable().Select(
sGroup => new WhatsOn
{
day = sGroup.Key,
whtscount = sGroup.Count()
});
return View(datequery);
}
public ViewResult Browse(DateTime? day , int? id)
{
var eventsquery = from c in db.WhatsOns
where c.start == day
select c;
return View(eventsquery);
}
A the moment the linq query in the browse class returns nothing, just an empty table. Any help is greatly appreciated! Thanks.
UPDATE:
Hey! Got it working
Here;s my new controller;
public ViewResult Browse(int? id, DateTime? day, DateTime? start)
{
var eventsquery = from c in db.WhatsOns where c.start.Value.Day == day.Value.Day select c;
return View(eventsquery);
}
And what did the trick, in my actionlink in my view....
#Html.ActionLink("Browse", "Browse", new { start=item.start, day=item.day })
Thanks for you help!!
does
var eventsquery = from c in db.WhatsOns
where c.start.Value.Date == day.Value.Date
select c;
work?
When comparing DateTime objects, keep in mind that the '==' sign also looks at seconds, miliseconds, etc.
Do you have any results you DO expect? Is the database table filled with information?
Edit: DateTime.Now you already used ;)

Joining Linq Expressions

I'm working with the new EF4 CTP4 although I don't think this has much to do with that. I am trying to set up a system where I can add auditable fields for our database automatically. What I'm trying to do is combine the following two expressions
a => new
{
a.CreatedBy,
a.CreatedTime,
a.UpdatedBy,
a.UpdatedTime
}
and
a => new
{
a.Id,
a.Name,
}
so the result is equivalant to
a => new
{
a.Id,
a.Name,
a.CreatedBy,
a.CreatedTime,
a.UpdatedBy,
a.UpdatedTime
}
the result I need to be an Expression<Func<T, object>>. I've been poking around and tried several things with Expression.Invoke and Expression.And(andalso) and haven't found anything that is working for me.
I'm not quite sure if this is possible but any help would be appreciated.
I don't think you can simply 'merge' two expressions. But you can use alternate API to create mappings with EntityMap.
public static class MapBuilder
{
public static Expression<Func<T, object>> GetMap<T>(Expression<Func<T, object>> func) where T: IAuditable
{
var body = func.Body as NewExpression;
var param = Expression.Parameter(typeof(T), "o");
var propertyAccessExprs = new List<Expression>();
foreach (MemberInfo member in body.Members)
{
propertyAccessExprs.Add(Expression.Property(param, member.Name));
}
var props = typeof(IAuditable).GetProperties();
foreach (PropertyInfo prop in props)
{
propertyAccessExprs.Add(Expression.Property(param, prop.Name));
}
var columnMappins = new List<Expression>();
foreach (var access in propertyAccessExprs)
{
columnMappins.Add(Expression.Call(typeof(EntityMap).GetMethod("Column", new Type[] {typeof(Object)}), Expression.Convert(access, typeof(Object))));
}
var RowExpr = Expression.Call(typeof(EntityMap).GetMethod("Row"), Expression.NewArrayInit(typeof(EntityMapColumn), columnMappins));
var result = Expression.Lambda<Func<T, object>>(RowExpr, param);
return result;
}
}
The usage is
var builder = new ModelBuilder();
builder.Entity<SimpleAuditableObject>()
.HasKey(o => o.Id)
.MapSingleType(MapBuilder.GetMap<SimpleAuditableObject>(o => new { o.Id, o.Name }));
Where
public interface IAuditable
{
int CreatedBy { get; set; }
DateTime CreatedTime { get; set; }
int UpdatedBy { get; set; }
DateTime UpdatedTime { get; set; }
}
public class SimpleAuditableObject : IAuditable
{
public int Id { get; set; }
public string Name { get; set; }
public int CreatedBy { get; set; }
public DateTime CreatedTime { get; set; }
public int UpdatedBy { get; set; }
public DateTime UpdatedTime { get; set; }
}
HTH.

Resources