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 ;)
Related
I have the following object Job and Job sor are populated by reading in data from an XML file and Sor is populated from a database.
class Job
{
public int JobID { get; set; }
public string DepartmentCode { get; set; }
public string ClientReference { get; set; }
public string JobDescription { get; set; }
public List<JobSor> JobSorList { get; set; }
}
class JobSor
{
public int JobID { get; set; }
public string SorUserCode { get; set; }
public string SorNotes1 { get; set; }
public string SorNotes2 { get; set; }
}
class Sor
{
[Key]
public string code { get; set; }
public string description { get; set; }
public string contract { get; set; }
}
I want to write a linq query that will show me all the JobSors that do not exist in the Sor object.
This is what I have so far but I can’t reference the SorUserCode property?
var db = new dbContext();
var sor = db.Sors.Where(p => p.contract == "??");
var query =
from j in jobs
join p in sor on j.JobSorList.SorUserCode equals p.code into jp
from a in jp.DefaultIfEmpty()
select j;
How can I do this?
First get list of all JobSor from jobs list.
Then apply condition Where its SorUserCode value does not match with Any code value of the sor list.
Your query will be as below.
var query = jobs.SelectMany(x => x.JobSorList)
.Where(x => !sor.Any(y => y.code == x.SorUserCode));
I finally (after 4 weeks) got a result for a MVC5 project and it went well.
Now I'm trying to "limit" the number of results and it's flagging an error:
'Stoopid' is a type, which is not valid in the given context
'Student' is a type, which is not valid in the given context
Here's the model:
namespace viewModelA
{
public class Teacher
{
public int TeacherId { get; set; }
public string Code { get; set; }
public string Name { get; set; }
}
public class Student
{
public int StudentId { get; set; }
public string Code { get; set; }
public string Name { get; set; }
public string EnrollmentNo { get; set; }
}
public class Stoopid
{
[Key]
public int StoopID { get; set; }
public DateTime stopDt { get; set; }
}
public class ViewModel
{
public IEnumerable<Teacher> Teachers { get; set; }
public IEnumerable<Student> Students { get; set; }
public IEnumerable<Stoopid> Stoopids { get; set; }
}
}
and this is the linq query - notice that Teacher is fine, but Student and Stoopid are NOT. And they are all in the same .cs file. Am I missing something?
var result = (from t in Teacher
join s in Student on t.TeacherId equals s.StudentId
join st in Stoopid on s.StudentId equals st.StoopID
where t.TeacherId == 2
select new
{
TeacherID= t.TeacherId,
Code = t.Code,
t.Name,
s.StudentId,
sCode =s.Code,
sName=s.Name,
stopDt= st.stopDt
})
Edit: I added the relevant code to the HomeController. I also ran this thru LINQPad5 and it works fine so I don't know what's the deal
HomeController
You're referencing the class names in your Linq query, which is why it is throwing that error. You need to reference the actual List objects instead.
var result = (from t in mymodel.Teachers
join s in mymodel.Students on t.TeacherId equals s.StudentId
join st in mymodel.Stoopids on s.StudentId equals st.StoopID
where t.TeacherId == 2
select new
{
TeacherID= t.TeacherId,
Code = t.Code,
t.Name,
s.StudentId,
sCode =s.Code,
sName=s.Name,
stopDt= st.stopDt
})
I'm getting the following error when I try to insert a new row in one of my relational tables. I have the following two models:
public class CompanyCredit
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int creditId { get; set; }
public int planCredit { get; set; }
public DateTime? PlanCreditExpirationDate { get; set; }
}
And
public class CompanyInformation
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int id { get; set; }
[Required]
[DisplayName("Company Name:")]
public string companyName { get; set; }
public string timeZone { get; set; }
//navigation Properties
public virtual CompanyCredit Credits { get; set; }
}
And this Relation in the dbContext
modelBuilder.Entity<CompanyInformation>().HasOptional(e => e.Credits);
I'm trying to add a record inside CompanyCredit table like so:
if (_company.Credits == null)
{
var _credits = new CompanyCredit();
_credits.planCredit = 200;
_credits.PlanCreditExpirationDate = System.DateTime.UtcNow.AddMonths(1);
_company.Credits = _credits;
repo.InsertOrUpdate(_company, User.Identity.Name);
}
And Finally Insert or update just marks Company as changed and _credit as added like so:
_db.Entry(_credits).State = System.Data.EntityState.Added;
_db.Entry(Company).State = System.Data.EntityState.Modified;
_db.SaveChanges();
When this runs I get the following Error that I just can't seem to find the reason to.
Cannot insert the value NULL into column 'creditId', table 'Project.dbo.CompanyCredits'; column does not allow nulls. INSERT fails.
The statement has been terminated.
Thank in advanced for your help.
I found the problem was in the attribute [DatabaseGenerated(DatabaseGeneratedOption.Identity)] this should have been [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
I thought I would post this so others might benefit from it.
Could you please try reversing the order of entity state modification, just before the saveChanges call
_db.Entry(Company).State = System.Data.EntityState.Modified;
_db.Entry(_credits).State = System.Data.EntityState.Added;
_db.SaveChanges();
I have an Inventory Class that contains not only its own fields but several reference IDs to other classes.
public class Inventory {
public int Id { get; set; }
public string RtNum { get; set; }
public string AcntNum { get; set; }
public string CardNum { get; set; }
public string Num { get; set; }
[Range(1,3)]
public int Type { get; set; }
public int CompanyId { get; set; }
public int BranchId { get; set; }
public int PersonId { get; set; } }
In my action I generate several IEnumerable lists of the relevant fields from the other classes. I also have several non-list values I want to pass to the View. I know how to create a ViewModel to pass everything to the webgrid but have no way of iterating through the lists. I also know how to AutoMap an index to one list, see How to display row number in MVC WebGrid.
How would you combine the two so that you could use the index to iterate through multiple lists?
Update #1 (more detail)
public class Company {
public int Id { get; set; }
public string Name { get; set; } }
public class Branch {
public int Id { get; set; }
public string Name { get; set; } }
public class Person {
public int Id { get; set; }
public string Name { get; set; } }
public class MyViewModel {
public int PageNumber { get; set; }
public int TotalRows { get; set; }
public int PageSize { get; set; }
public IEnumerable<Inventory> Inventories { get; set; }
public int Index { get; set; }
public IEnumerable<string> CmpNm { get; set; }
public IEnumerable<string> BrnNm { get; set; }
public IEnumerable<string> PrnNm { get; set; } }
Controller
public class InventoryController : Controller
{ // I have a paged gird who’s code is not relevant to this discussion but a pagenumber,
// pagesize and totalrows will be generated
private ProjectContext _db = new ProjectContext();
public ActionResult Index() {
IEnumerable<Inventory> inventories = _db.Inventories;
List<string> cmpNm = new List<string>; List<string> brnNm = new List<string>; List<string> prnNm = new List<string>;
foreach (var item in inventories) { string x1 = "";
Company cmps = _db. Company.SingleOrDefault(i => i.Id == item.CompanyId); if (cmps!= null)
{ x1 = cmps.Name; } cmpNm.Add(x1); x1 = "";
Branch brns = _db. Branch.SingleOrDefault(i => i.Id == item. Branch Id); if (brns!= null) { x1 = brns.Name; } brnNm.Add(x1); x1 = "";
Person pers = _db.Persons.SingleOrDefault(i => i.Id == item. PersonId);
if (pers!= null) { x1 = pers.Name; } prnNm.Add(x1);
// the MyViewModel now needs to populated with all its properties and generate an index
// something along the line of
new MyViewModel { PageNumber= pagenumber, PageSize= pagesize, TotalRows=Totalrows, Inventories = inventories; CmpNm=cmpNm, BrnNm=brnNm, PrnNm=prnNm}
View (How to create the Index is the problem)
#model.Project.ViewModels.MyViewModel
#{ var grid = new WebGrid(Model.Inventories, Model.TotalRows, rowsPerPage: Model.PageSize); }
#grid.GetHtml( columns: grid.Columns(
Grid.Column(“PrnNm”, header: "Person", format: #Model.PrnNm.ElementAt(Index))
Grid.Column(“BrnNm”, header: "Branch", format: #Model.BrnNm.ElementAt(Index))
Grid.Column(“CmpNm”, header: "Company", format: #Model.CmpNm.ElementAt(Index))
grid.Column("RtNum", header: "Route"),
grid.Column("AcntNum", header: "Account"),
grid.Column("CardNum", header: "Card")
… ) )
What the grid should look like is self-evident.
It's pretty unclear what is your goal. But no matter what it is I would recommend you to define a real view model reflecting the requirements of your view and containing only the information you are interested in seeing in this grid:
public class InventoryViewModel
{
public int Id { get; set; }
public string PersonName { get; set; }
public string BranchName { get; set; }
public string CompanyName { get; set; }
public string RouteNumber { get; set; }
public string AccountNumber { get; set; }
public string CardNumber { get; set; }
}
Now you could have the main view model:
public class MyViewModel
{
public int PageNumber { get; set; }
public int TotalRows { get; set; }
public IEnumerable<InventoryViewModel> Inventories { get; set; }
}
Alright, the view is now obvious:
#model MyViewModel
#{
var grid = new WebGrid(
Model.Inventories,
rowsPerPage: Model.PageSize
);
}
#grid.GetHtml(
columns: grid.Columns(
grid.Column("Id", header: "Inventory id"),
grid.Column("PersonName", header: "Person"),
grid.Column("BranchName", header: "Branch"),
grid.Column("CompanyName", header: "Company"),
grid.Column("RouteNumber", header: "Route"),
grid.Column("AccountNumber", header: "Account"),
grid.Column("CardNumber", header: "Card")
)
)
Now all that's left is build this view model in your controller. Since I don't know what you are trying to achieve here, whether you need an inner join or a left outer join on those columns, I will take as an example here a left outer join:
public ActionResult Index()
{
var inventories =
from inventory in _db.Inventories
join person in _db.Persons on inventory.PersonId equals person.Id into outerPerson
join company in _db.Companies on inventory.CompanyId equals company.Id into outerCompany
join branch in _db.Branch on inventory.BranchId equals branch.Id into outerBranch
from p in outerPerson.DefaultIfEmpty()
from c in outerCompany.DefaultIfEmpty()
from b in outerBranch.DefaultIfEmpty()
select new InventoryViewModel
{
PersonName = (p == null) ? string.Empty : p.Name,
CompanyName = (c == null) ? string.Empty : c.Name,
BranchName = (b == null) ? string.Empty : b.Name,
Id = inventory.Id,
AccountNumber = inventory.AcntNum,
CardNumber = inventory.CardNum,
RouteNumber = inventory.RtNum
};
var model = new MyViewModel
{
PageSize = 5,
// TODO: paging
Inventories = inventories.ToList()
};
return View(model);
}
And that's pretty much it. Of course in this example I am leaving the pagination of the Inventories collection for you. It should be pretty trivial now to .Skip() and .Take() the number of records you need.
As you can see ASP.NET MVC is extremely simple. You define a view model to reflect the exact requirements of what you need to show in the view and then populate this view model in the controller. Most people avoid view models because they fail to populate them, probably due to lack of knowledge of the underlying data access technology they are using. As you can see in this example the difficulty doesn't lie in ASP.NET MVC at all. It lies in the LINQ query. But LINQ has strictly nothing to do with MVC. It is something that should be learned apart from MVC. When you are doing MVC always think in terms of view models and what information you need to present to the user. Don't think in terms of what you have in your database or wherever this information should come from.
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]