DbFunctions.TruncateTime LINQ equivalent in EF CORE - sqlite

I have the following functioning LINQ in my .net app
public ActionResult Index()
{
Dictionary<DateTime?, List<Event>> result;
result = (from events in db.Events.Include("Activity")
where events.IsActive
group events by DbFunctions.TruncateTime(events.DateTimeFrom) into dateGroup
select new { EventDate = dateGroup.Key, Events = dateGroup.ToList() }).ToDictionary(x => x.EventDate, x => x.Events);
return View(result);
}
When I use this in EF Core, I can't use DbFunctions. How can I rewrite this to make it work in Microsoft.EntityFrameworkCore ? I am using SQLite if that makes a difference.

In EF6 DbFunctions.TruncateTime is used instead of DateTime.Date property because for some reason the later is not supported.
In EF Core the former is not needed simply because DateTime.Date now is recognized and translated correctly.
group events by events.DateTimeFrom.Date into dateGroup
Unfortunately there is no documentation (yet) of what is supported, so as a general rule of thumb, always try the corresponding CLR method/property (if any) and check if it translates to SQL and how.

To use DbFunctions in ASP.NET CORE You must create an object.
var DbF = Microsoft.EntityFrameworkCore.EF.Functions;
Now you can easily use it.
var now = DateTime.Now;
int count = db.Tbl.Count(w => DbF.DateDiffDay(now, w.RegisterDate) <= 3);
more detail on github

DbFunctions are not supported yet for EF Core. However you can use "Raw Sql Queries".
You can find documentation of "Raw Sql Queries" here
And also you can track here for DbFunctions for EF Core

EF Core 3.0
I finally found an answer that works. The issue is that I was wanting to Group by Date on a DateTime column in the database.
The key for me was to use the EF.Property function. This allows the class to have the DateTime property which is used for adding that level of data, but below allowed me to then redefine it as a Date. However.. I suspect if I decalared the property on the class, it would have already allowed me to use the .Date function which it was not allowing me todo.
So the solution may rather be to define the property on the model, or use the below to define it in your query.
EF.Property(s, "dt").Date
Full code
var myData = _context.ActivityItems
.GroupBy(a => new { nlid = a.lid, nsd = EF.Property<DateTime>(a, "dt").Date })
.Select(g => new
{
g.Key.nlid,
g.Key.nsd,
cnt = g.Count()
});

I managed to rewrite this in Lambda as well and make it async. Seems to be working the same.
var temp = await _context.Events.Where(x => x.IsActive)
.Include(a => a.Activity)
.GroupBy(x => x.DateTimeFrom.Date)
.Select(g => new { EventDate = g.Key, Events = g.ToList() }).ToDictionaryAsync(x => x.EventDate, x => x.Events);

EF Core 2.0 now supports mapping database functions to static methods on your context.
Check out the section 'Database scalar function mapping' here - https://learn.microsoft.com/en-us/ef/core/what-is-new/

In my case, it is working in this way instead of DbFunctions.TruncateTime or EntityFunctions.TruncateTime-
result = _context.ActivityItems.Where(a => a.DateTimeFrom.Value.Date == paramFilter.DateTimeFrom.Value.Date);
It converts date first in the server side in where condition and then compare with parameter date value-
WHERE CONVERT(date, [a].[DateTimeFrom]) = #__paramFilter_DateTimeFrom_Value_Date_0

Related

NHibernate: adding calculated field to query results

I have inherited an ASP.NET website built on NHibernate, with which I have no experience. I need to add a calculated field based on a column in a related table to an existing query. In SQL, this would be done easily enough using a correlated subquery:
select
field1,
field2,
(select count(field3) from table2 where table2.table1ID = table1.ID) calc_field
from
table1
where
[criteria...]
Unfortunately, of course, I can't use SQL for this. So in reality, I have three related questions:
What is the best way to trace through the web of interfaces, base classes, etc used by NHibernate in order to pinpoint the object where I need to add the field?
Having located that object, what, if anything, has to be done besides adding a public property to the object corresponding to the new field?
Are there any NHibernate-specific considerations with regard to referencing a related object in a query?
Here is the existing code that performs the search:
public INHibernateQueryable<C> Search(ISearchQuery query, string sortField)
{
_session = GetSession();
var c = _session.Linq<C>();
c.Expand("IP");
c.Expand("LL");
c.Expand("LL.Address");
c.Expand("LL.Address.City");
c.Expand("LL.Address.City.State");
c.Expand("LL.Address.City.County");
c.Expand("CE");
c.Expand("IC");
c.Expand("AR");
c.Expand("ER");
c.Expand("Status");
var res = _SearchFilters
.Where(x => x.ShouldApply(query))
.Aggregate(c, (candidates, filter) => (INHibernateQueryable<C>) filter.Filter(candidates, query));
res = SortSearch(res, sortField);
return res;
}
I appreciate any advice from experienced Hibernators.
Thanks,
Mike
If you are only interested in returning a query containing a computed value, you can still call a stored procedure in NHibernate and map the results to a POCO in the same way as you map a table for CRUD operations; obviously read-only instead of updatable.
Have a look at the ISession.CreateSQLQuery method; I can post an example from one of my projects if you need one.

ORDER BY NEWID in LINQ and bind to Repeater control

Morning,
I would like to know how to write the following SQL statement in LINQ.
SELECT TOP 6 * FROM Questions
ORDER BY NEWID()
I would also like to know, how i could bind this to a asp.net repeater control to display the 6 questions.
Many thanks :)
The Linq style would be
Questions.OrderBy(q=>Guid.NewGuid()).Take(6)
then you attach that to a repeater by setting its DataSource property to the above, and calling the DataBind method.
You would have to be able to invoke the NEWID() function to generate your random guids. To do so, you could take some hints here and first create a pseudo-method mapped to the NEWID() function on your data context.
[System.Data.Linq.Mapping.Function(Name="NEWID", IsComposable=true)]
public Guid NewId()
{
throw new NotImplementedException();
}
Once that is set, you could then write your query to use this function:
var query = dc.Questions
.OrderBy(question => dc.NewId())
.Take(6);
You can inspect the SQL query generated for this and it should match.
Questions.OrderBy(q=>Sql.NewGuid()).Take(6)
This will invoke the NEWID() in SQL statement.
(from db in context.Questions
order by Guid.NewGuid()
select db).Take(6);
I know answer is already selected, but still I'm adding my way to achieve this. Faced same situation today and tried couple of ways, used questions.OrderBy(q => Guid.NewGuid()).ToList() and couple of more suggestions. Later I thought to add a new field string RandomOrder in view model and assigned Guid.NewGuid().ToString() in loop and then used questions.OrderBy(i => i.RandomOrder).ToList() and this worked great.
I had requirement to shuffle questions if author selected option shuffleAlways while creating assessment. If not then sort on regular sorting order. Here is complete solution:
private List<AssessmentQuestionsViewModel> LoadAllQuestions(string assessmentId, bool shuffleQuestions)
{
List<AssessmentQuestionsViewModel> questions = new List<AssessmentQuestionsViewModel>();
var items = assessmentQuestionRepository.GetAll().Where(i => i.AssessmentId == assessmentId).ToList();
foreach (var item in items)
{
questions.Add(new AssessmentQuestionsViewModel
{
Id = item.Id,
AssessmentId = item.AssessmentId,
QuestionText = item.QuestionText,
HintText = item.HintText,
QuestionType = item.QuestionType,
MaxMarks = item.MaxMarks,
SortOrder = item.SortOrder,
RandomOrder = Guid.NewGuid().ToString(),
Answers = LoadAllAnswers(item.Id)
});
}
if (shuffleQuestions)
{
questions = questions.OrderBy(i => i.RandomOrder).ToList();
}
else
{
questions = questions.OrderBy(i => i.SortOrder).ToList();
}
return questions;
}
And this worked like charm. Hope this help others.
I assume you are using ORDER BY NEWID() as a way to select random data from your questions? If so, you should avoid using NEWID() (or it's LINQ equivalent), causes tons a new guid to be generated for every record in your table. On a large dataset, that's devestating.
Instead, see Linq Orderby random ThreadSafe for use in ASP.NET for an optimized solution to random sorts. Then just add a take operator and your set.
Random random = new Random();
int seed = random.Next();
var RandomQuestions = Questions.OrderBy( s => (~(s.Shuffle & seed)) & (s.Shuffle | seed)); // ^ seed);
return RandomQuestions.Take(6);

ASP.NET Entity Framework 4 Navigation Entity not reflecting changes made to database

I have two database tables (NEWS and NEWS_IMAGES) that I have produced an entity model for with a one to many association made between them in the model.
However when I query the model using the Navigation property (NEWS_IMAGES) it doesn't return any recent database inserts but if I query the navigation entity itself then I get all latest changes.
First Method using Navigation property:
IEnumerable<NEWS_IMAGES> imgs = dal.NEWS.Where(n => n.NEWS_ID == NewsID).FirstOrDefault().NEWS_IMAGES;
Second method using the actual entity (returns all recent changes):
IEnumerable<NEWS_IMAGES> imgs = dal.NEWS_IMAGES.Where(i => i.News_ID == NewsID)
This is the code that inserts a record to the NEWS_IMAGES entity:
NEWS_IMAGES img = new NEWS_IMAGES
{
News_ID = newsID,
News_Image_Filename_Small = file_Sm,
News_Image_Filename_Medium = file_Med,
News_Image_Filename_Large = file_Lrge,
News_Image_Order = imgCnt + 1
};
dal.NEWS_IMAGES.AddObject(img);
dal.SaveChanges();
The default behavior of the EF is to load only the entity directly accessed by the application (e.g. News). IF the EF loaded all the related entities (e.g. News_Images) you will end up loading more entities than you actually need.
You can uses something called eager loading to load related entities using the Include() method.
In your case you will have something like this:
var imgs= dal.NEWS.Include("NEWS_IMAGES").Where(n => n.NEWS_ID == NewsID).FirstOrDefault();
You should try using the following which should do the trick:
IEnumerable<NEWS_IMAGES> imgs =
dal.NEWS.Where(n => n.NEWS_ID == NewsID).SelectMany(i => i.NEWS_IMAGES)

EF4 throws NotSupported exception when it (imho) shouldn't

OK, this thing just puzzles me.
I have a table, say Users, with columns UserID, Name, etc. Have an object mapped to it using CTP5. So now I want to test it, and do the following:
List<User> users = new List();
// Some init code here, making say 3 users.
using (UsersDbContext)
{
// insert users
}
So far so good, works fine.
Now I want to see if the records match, so I select the users back using the following code.
using (UsersDbContext dbc = UsersDbContext.GetDbContext())
{
List<Users> usersRead = dbc.Users.Where(x => x.ID >= users[0].ID && x.ID <= users[users.Count - 1].ID).ToList();
}
This throws and exception:
System.NotSupportedException: LINQ to
Entities does not recognize the method
'User get_Item(Int32)' method, and
this method cannot be translated into
a store expression.
EF has difficulties seeing that I'm just asking to return an int in Users[0].ID ?
If I replace a call to users[0].ID with a straight int - works fine.
I get what it's trying to do, but I thought it should be pretty easy to check if the method belongs to .NET or Sql Server ?
You are trying to access an indexer in an EF expression, which doesn't translate to an SQL query. You'll have to move the parameters outside the query like this:
int first = users[0].ID;
int last = users[users.Count - 1].ID;
List<Users> usersRead = dbc.Users.Where(x => x.ID >= first && x.ID <= last).ToList();

SQL injection concerns

ok i use this route
routes.MapRoute(
"Catalog/Data",
"Catalog/{*data}",
new { controller = "Catalog", action = "Category", data = "" }
);
the Url looks something like http://localhost/Catalog/Computer/Harddrives/internal
Data beening the Computer/Harddrives/internal part
i split it apart and validate the route
here is where my concerns are, atm i do not check for sql injection
i check the route by getting the category from the database using enitity framework
with this function
public Category GetByRoute(string Route)
{
return (from c in XEntity.CategorySet
.Where(c => c.Route == Route)
.Where(c => c.IsEnabled == true)
select c).FirstOrDefault();
}
should i be worried about sql injection with this?
Linq2Sql and the Entity Framework use SQL parameters (except for one edge case) so you'll be fine.
In your case you're actually using Linq over the CategorySet, and linq is executed locally in this case, so it's CategorySet that's touching the database, the where constraints run after (I believe). Again in this case there's no problem.

Resources