the binary expression cannot be converted to a predicate expression in LINQ - c#

I want to get the week number of a certain given DateTime.
public static int WeekOf(DateTime? date)
{
if (date.HasValue)
{
GregorianCalendar gCalendar = new GregorianCalendar();
int WeekNumber = gCalendar.GetWeekOfYear(date.Value, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
return WeekNumber;
}
else
return 0;
}
And then I use the above method in:
public static List<ExpressionListDictionary> MyMethod(int weeknr)
{
using (DataAccessAdapter adapter = CreateAdapter())
{
LinqMetaData meta = new LinqMetaData(adapter);
var q = (from i in meta.Test
where WeekOf(i.StartDate) == weeknr
select new ExpressionListDictionary()
{
{"SomeId", i.Id}
}
);
return q.ToList();
}
}
And finally:
List<ExpressionListDictionary> someIDs = MyMethod(weeknr);
/* weeknr = 19 -> step by step debugging */
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: SD.LLBLGen.Pro.ORMSupportClasses.ORMQueryConstructionException: The binary expression '(WeekOf(Convert(EntityField(LPLA_1.StartDate AS StartDate))) == 19)' can't be converted to a predicate expression.
I do get the title error at return q.ToList(); . How can I achieve this?

I haven't ever used the LLBLGen library/framework... But probably this is the same problem that happens with Entity Framework/LINQ-to-SQL: you can't put in a query C# methods: the query must be executed by your db server, not locally, and your db server doesn't know how to execute C# code. So the problem would be in the
**WeekOf(i.StartDate)** == weeknr part of code (that is the only BinaryExpression of your query)
The exception you have posted is quite clear that the point of the error is the one that I have suggested. Then the reason is probably the one I gave you.
Taken from https://www.llblgen.com/tinyforum/Messages.aspx?ThreadID=22861
If you are using SQL Server, that supports DATEPART(isowk, ...) (or if you have MySQL, that supports WEEK(...))
public class SQLFunctionMappings : FunctionMappingStore
{
public SQLFunctionMappings()
{
Add(new FunctionMapping(
typeof(SQLFunctions),
"WeekOf",
1,
"DATEPART(isowk, {0})") // For SQL Server
// "WEEK({0}, 1)") For MySQL
);
}
}
public class SQLFunctions
{
public static int? WeekOf(DateTime? date)
{
return null;
}
}
and then you would use it like:
Where there is the row with LinqMetaData meta = new LinqMetaData(adapter), add:
meta.CustomFunctionMappings = new SQLFunctionMappings();
and change the where:
where SQLFunctions.WeekOf(i.StartDate) == weeknr
Here there is the list of the functions already mapped by llblgen, and how to map other functions.

you can try to Make the method you have WeekOf takes string instead of Datetime?
public static int WeekOf(String dateAsString)
{
//if (!string.IsNullOrEmpty(dateAsString))
if (!dateAsString.equals(string.empty))
{
GregorianCalendar gCalendar = new GregorianCalendar();
int WeekNumber = gCalendar.GetWeekOfYear(date.Value, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
return WeekNumber;
}
else
return 0;
}
And then you use the above below in:
public static List<ExpressionListDictionary> MyMethod(int weeknr)
{
using (DataAccessAdapter adapter = CreateAdapter())
{
LinqMetaData meta = new LinqMetaData(adapter);
var q = (from i in meta.Test
where i.startDate != null && WeekOf(i.StartDate.tostring()) == weeknr
select new ExpressionListDictionary()
{
{"SomeId", i.Id}
}
);
return q.ToList();
}
}

Try something like this
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
MyMethod(5);
}
public static int WeekOf(DateTime? date)
{
if (date.HasValue)
{
GregorianCalendar gCalendar = new GregorianCalendar();
int WeekNumber = gCalendar.GetWeekOfYear(date.Value, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
return WeekNumber;
}
else
return 0;
}
public static List<ExpressionListDictionary> MyMethod(int weeknr)
{
using (DataAccessAdapter adapter = CreateAdapter())
{
LinqMetaData meta = new LinqMetaData(adapter);
List<ExpressionListDictionary> q = (from i in meta.Test
where WeekOf(i.StartDate) == weeknr
select new ExpressionListDictionary()
{
Id = "SomeId"
}
).ToList();
return q;
}
}
public static DataAccessAdapter CreateAdapter()
{
return new DataAccessAdapter();
}
}
public class ExpressionListDictionary
{
public string Id { get; set; }
}
public class LinqMetaData
{
public List<LinqMetaData> Test {get;set;}
public DateTime StartDate {get;set;}
public int Id { get; set; }
public LinqMetaData(DataAccessAdapter adapter)
{
}
}
public class DataAccessAdapter : IDisposable
{
public void Dispose()
{
}
}
}
​

Related

LINQ query for DateTime ranges overlapping in collection items

In the following case, the method CanUnloadAll checks if there are no overlap in the unloading times, considering all trucks.
The current scenario, should returns TRUE but it is returning FALSE.
What's wrong with the logic in the LINQ query?
using System;
using System.Collections.Generic;
using System.Linq;
public class UnloadingTime
{
public DateTime Start { get; private set; }
public DateTime End { get; private set; }
public UnloadingTime(DateTime start, DateTime end)
{
this.Start = start;
this.End = end;
}
}
public static class UnloadingTrucks
{
public static bool CanUnloadAll(IEnumerable<UnloadingTime> unloadingTimes)
{
return unloadingTimes.Any(
TruckA => unloadingTimes.Any(
TruckB => TruckB != TruckA &&
!((
TruckA.Start.Date >= TruckB.End.Date ||
TruckB.Start.Date >= TruckA.End.Date
))
));
}
public static void Main(string[] args)
{
var format = System.Globalization.CultureInfo.InvariantCulture.DateTimeFormat;
UnloadingTime[] unloadingTimes = new UnloadingTime[]
{
new UnloadingTime(DateTime.Parse("3/4/2019 19:00", format), DateTime.Parse("3/4/2019 20:30", format)),
new UnloadingTime(DateTime.Parse("3/4/2019 22:10", format), DateTime.Parse("3/4/2019 22:30", format)),
new UnloadingTime(DateTime.Parse("3/4/2019 22:40", format), DateTime.Parse("3/4/2019 23:00", format))
};
Console.WriteLine(UnloadingTrucks.CanUnloadAll(unloadingTimes));
}
}
To make it easier, I am using .NET Fiddle.
https://dotnetfiddle.net/Mis663
Regards
Solution:
public static bool CanUnloadAll(IEnumerable<UnloadingTime> unloadingTimes)
{
bool hasOverlap = unloadingTimes.Any(
TruckA => unloadingTimes.Any(
TruckB => TruckB != TruckA &&
!((
TruckA.Start >= TruckB.End ||
TruckB.Start >= TruckA.End
))
));
return !hasOverlap;
}
You are using the DateTime.Date property, which is the date component without the time part. You must use the DateTime:
bool flag = TruckA.Start >= TruckB.End || TruckB.Start >= TruckA.End

Generic type and field accessing in C#

Instead of T (Type) if I use class name i.e. sampleclass this code works properly,
but if I use T, then it shows that
'T' does not contain a definition for 'TimeStamp' and no accessible
extension method 'TimeStamp' accepting a first argument of type 'T'
could be found (are you missing a using directive or an assembly
reference?)
//Get collection.
var collection = this.GetDatabaseConnection().GetCollection<T>
(collectionName);
//filter to read specific data.
var filter = Builders<T>.Filter.Where(result => result.TimeStamp >=
startTime && result.TimeStamp <= endTime);
List < T > queryData = collection.Find<T>(filter, null).ToList();
Previously it was as follows and working finely:
//Get collection.
var collection = this.GetDatabaseConnection().GetCollection<Sampleclass>.
(collectionName);
//filter to read data using specific timestamp.
var filter = Builders<Sampleclass>.Filter.Where(result =>
result.TimeStamp >=
startTime && result.TimeStamp <= endTime);
List < Sampleclass > queryData = collection.Find<Sampleclass>
(filter, null).ToList();
i would do it using a base class like this
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Driver;
using System;
using System.Linq;
namespace StackOverflow
{
public class Program
{
public class Entity
{
[BsonId]
public ObjectId Id { get; set; }
public DateTime TimeStamp { get; set; }
}
public class Sample : Entity
{
public string Something { get; set; }
}
private static void Main(string[] args)
{
var collection = new MongoClient("mongodb://localhost:27017")
.GetDatabase("Test")
.GetCollection<Entity>("Samples");
var sample = new Sample
{
Id = ObjectId.GenerateNewId(),
Something = "something",
TimeStamp = DateTime.UtcNow
};
collection.InsertOne(sample);
var result = collection.AsQueryable()
.Where(s =>
s.TimeStamp >= DateTime.UtcNow.AddMinutes(-1) &&
s.TimeStamp <= DateTime.UtcNow.AddMinutes(1))
.ToArray();
}
}
}
The problem is that T doesn't have a TimeStamp property as it's a generic type. If you always have to access the TimeStamp of whatever type you process in your collection, you could consider using an interface that has TimeStamp as a get function. All the types you want to process would have to implement that interface.
public interface MyInterface
{
TimeSpan TimeStamp { get; } //or whatever type your are using for your TimeStamp property
}
var collection = this.GetDatabaseConnection().GetCollection<MyInterface>
(collectionName);
var filter = Builders<MyInterface>.Filter.Where(result => result.TimeStamp >=
startTime && result.TimeStamp <= endTime);
List<MyInterface> queryData = collection.Find<MyInterface>(filter,null).ToList();
This way it's still quite generic. If you want to still use type T you would have to work with reflection to find the property TimeStamp on your processed type.
Edit: Some advice for using type T: You always have to consider that T can be anything, from an integer to any other object imaginable. Sometimes it can be quite useful if you have a method for example that doesn't need to access any properties on the type T object it is given but is used for many different types. I lately used T to build a little ParseOrDefault method to catch values that can't be converted:
private T ParseOrDefault<T>(Object value, T defaultValue)
{
try
{
return (T)Convert.ChangeType(value, typeof(T));
}
catch (Exception)
{
return defaultValue;
}
}
As soon as you have to deal with more specific use cases T is mostly not that useful.
after reading your comments on my first answer, i'd like to propose the following solution using MongoDB.Entities
hope this is more or less what you're trying to achieve...
using MongoDB.Entities;
using System;
using System.Collections.Generic;
namespace StackOverflow
{
public class Program
{
public class MySample : Entity
{
public DateTime TimeStamp { get; set; }
}
public class Sample1 : MySample
{
public string SomeProp { get; set; }
}
public class Sample2 : MySample
{
public string AnotherProp { get; set; }
}
private static void Main(string[] args)
{
new DB("test");
var sample1 = new Sample1 { SomeProp = "some prop value", TimeStamp = DateTime.UtcNow };
var sample2 = new Sample2 { AnotherProp = "another prop", TimeStamp = DateTime.UtcNow };
DB.Save(sample1);
DB.Save(sample2);
var s1 = FindSamples<Sample1>();
var s2 = FindSamples<Sample2>();
List<T> FindSamples<T>() where T : MySample
{
return DB.Find<T>()
.Many(s =>
s.TimeStamp >= DateTime.UtcNow.AddMinutes(-1) &&
s.TimeStamp <= DateTime.UtcNow.AddMinutes(1));
}
}
}
}

Can I use join in Entity Framwork Pagination syntax?

I was wondering if someone can help here.I'm new to Entity Framwork. I have 2 different Query.I want to join them and get 1.Here is my code:
public static List<BankDepositHistory> GetAllByPagination(int page ,int stepes)
{
page=page-1;
using(MyketAdsEntities context = new MyketAdsEntities())
{
var transactionlist = context.BankDepositHistories.ToList();
var start = page * stepes;
var result= context.BankDepositHistories.OrderByDescending(c=>c.AccountId)
//anny code that give me count as field
.Skip(start)
.Take(stepes)
.ToList();
return (result);
}
}
public static int GetCount()
{
using (MyketAdsEntities context = new MyketAdsEntities())
{
int count = context.BankDepositHistories.Count();
return count;
}
}
As you see I have 2 method. I just want to have GetAllByPagination.
Many thanks
Suppose you have below Entity, Then below setup should work.
public BankDepositHistory
{
public string UserName {get;set}
//etc..
}
Create a View Model
public class BankDepositHistoryVM
{
public List<BankDepositHistory> bankDetails {get;set;}
public int Count {get;set;}
}
Return View Model
public static List<BankDepositHistoryVM> GetAllByPagination(int page ,int stepes)
{
page=page-1;
using(MyketAdsEntities context = new MyketAdsEntities())
{
var transactionlist = context.BankDepositHistories.ToList();
var start = page * stepes;
var result= context.BankDepositHistories.OrderByDescending(c=>c.AccountId)
.Skip(start)
.Take(stepes)
.ToList();
List<BankDepositHistoryVM> resultVM = new List<BankDepositHistoryVM>();
resultVM.bankDetails = result;
resultVM.Count = result.Count();
return resultVM;
}
}
Call the method:
List<BankDepositHistory> bankDetails = className.GetAllByPagination.bankDetails;
int count = className.GetAllByPagination.Count;
List<BankDepositHistoryVM> allDetails = className.GetAllByPagination();
Hope helps.

SQLCLR custom aggregate with datetime parameter

Hy,
I have post a question about CLR User-Defined Aggregates few month ago on this post.
This works great with int. But now I would like to do the same functions with a datetime parameter.
But I can't get work.
Like this, the code won't work... The problem is on the read function who generate this error on sql server :
System.ArgumentOutOfRangeException: Les graduations doivent être comprises entre DateTime.MinValue.Ticks et DateTime.MaxValue.Ticks.
Nom du paramètre : ticks
System.ArgumentOutOfRangeException:
à System.DateTime..ctor(Int64 ticks)
à sMaxDatetime.Read(BinaryReader reader)
So I tried to convert my sql Datetime into ticks, with this, but it'not working eather. I have a OverflowException on the conversion to datetime.
I have found this post, and it's appear that I can't map my datetime to the BinaryReader...
So I running out of ideas to do my aggregate...
Have you a idea to do this ?
Here is the actual code :
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using System.Diagnostics.Eventing.Reader;
using System.Globalization;
using Microsoft.SqlServer.Server;
using System.Text;
using System.Collections;
using System.IO;
[Serializable]
[SqlUserDefinedAggregate(
Format.UserDefined,
IsInvariantToOrder = true,
IsInvariantToNulls = true,
IsInvariantToDuplicates = true,
MaxByteSize = -1)]
public struct sMaxDatetime : IBinarySerialize
{
#region Helpers
private struct MyData
{
public string Data { get; set; }
public DateTime? Group { get; set; }
public int CompareTo(MyData other)
{
if (Group == null)
return other.Group == null ? 0 : -1;
if (other.Group == null)
return 1;
return Group.Value.CompareTo(other.Group.Value);
}
public static bool operator < (MyData left, MyData right)
{
return left.CompareTo(right) == -1;
}
public static bool operator > (MyData left, MyData right)
{
return left.CompareTo(right) == 1;
}
}
#endregion
private MyData _maxItem;
public void Init()
{
_maxItem = default(MyData);
}
public void Accumulate(SqlString data, SqlDateTime group)
{
if (!data.IsNull && !group.IsNull)
{
var current = new MyData
{
Data = data.Value,
Group = group.Value,
};
if (current > _maxItem)
{
_maxItem = current;
}
}
}
public void Merge(sMaxDatetime other)
{
if (other._maxItem > _maxItem)
{
_maxItem = other._maxItem;
}
}
public SqlString Terminate()
{
return _maxItem.Data;
}
public void Read(BinaryReader reader)
{
//if (reader.ReadBoolean())
//{
_maxItem.Data = reader.ReadString();
_maxItem.Group = new DateTime(reader.ReadInt64());
//}
//else
//{
// _maxItem = default(MyData);
//}
}
public void Write(BinaryWriter writer)
{
if (_maxItem.Group.HasValue)
{
writer.Write(true);
writer.Write(_maxItem.Group.Value.ToString());
writer.Write(_maxItem.Data);
}
else
{
writer.Write(false);
}
}
}
PS : I have this related post with sql_variant unclosed who could do the tricks but I can't get to work eather.
I think you're working too hard with a custom struct to hold the datetime and name. Here's what I came up with:
using System;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.IO;
[Serializable]
[SqlUserDefinedAggregate(Format.UserDefined, Name = "sMaxDatetime", MaxByteSize = -1)]
public struct SO33215409 : IBinarySerialize
{
private SqlString _data;
private SqlDateTime _latest;
public void Init()
{
_data = SqlString.Null;
_latest = SqlDateTime.MinValue;
}
public void Accumulate(SqlString data, SqlDateTime dt)
{
if (dt > _latest)
{
_data = data;
_latest = dt;
}
}
public void Merge (SO33215409 Group)
{
if (Group._latest > _latest)
{
_data = Group._data;
_latest = Group._latest;
}
}
public SqlString Terminate ()
{
return _data;
}
public void Write (BinaryWriter w)
{
w.Write(_data.IsNull);
w.Write(_latest.IsNull);
if (_data.IsNull == false)
{
w.Write(_data.Value);
}
if (_latest.IsNull == false)
{
w.Write(_latest.Value.Ticks);
}
}
public void Read(BinaryReader r)
{
bool dataIsNull = r.ReadBoolean();
bool latestIsNull = r.ReadBoolean();
if (dataIsNull)
{
_data = SqlString.Null;
}
else
{
_data = r.ReadString();
}
if (latestIsNull)
{
_latest = SqlDateTime.Null;
}
else
{
DateTime d = new DateTime(r.ReadInt64());
_latest = new SqlDateTime( d );
}
}
}
And the SQL to exercise it:
WITH cte AS (
SELECT * FROM (VALUES
('Manager' , 'emp 1' , dateadd(year, -35, getdate())),
('Manager' , 'emp 2' , dateadd(year, -42, getdate())),
('Developer' , 'emp 3' , dateadd(year, -36, getdate())),
('Developer' , 'emp 4' , dateadd(year, -45, getdate())),
('Developer' , 'emp 5' , dateadd(year, -22, getdate()))
) AS x([Type], [Name], [DOB])
)
SELECT [Type], dbo.[sMaxDatetime]([Name], [DOB])
FROM cte
GROUP BY [Type]

Local sequence cannot be used in LINQ to SQL implementations of query operators except the Contains operator

I am getting this error on a webpage, on a Microsoft Report Viewer component:
An error has occurred during report
processing. Local sequence cannot be
used in LINQ to SQL implementations of
query operators except the Contains
operator.
My code:
public static IEnumerable GetUrunMiktarliDetayli()
{
Baglanti();
List<StokEnvanteriSatiri> urunListesi = new List<StokEnvanteriSatiri>();
urunListesi = GetUrunListesiDoldur();
var urunStok = from urunS in urunListesi
select new
{
urunS.AcilisMiktari,
urunS.MevcutMiktar,
urunS.UrunNo
};
var urunMD = from urun in db.TBLP1URUNs
join kategori in db.TBLP1URUNKATEGORIs
on urun.KATEGORIID equals kategori.ID
join a in urunStok
on urun.ID equals a.UrunNo
select new
{
DIGERGIRISLER = a.AcilisMiktari,
urun.URUNADI,
urun.URUNACIKLAMA,
kategori.TREENAME,
urun.STOKTURU,
urun.MARKA,
urun.MODEL,
urun.URUNTIPI,
urun.URUNDURUM,
urun.KRITIKSTOKMIKTARI,
urun.DEPOADI,
urun.YER,
urun.RAF,
urun.RAFOMRU,
KDVDAHILMI = urun.KDVDAHILMI==1 ? "EVET":"HAYIR",
urun.KDVORANI,
urun.SATFIYAT1,
urun.SATFIYAT1BIRIM,
urun.TEDFIYAT1,
urun.TEDFIYAT1BIRIM,
urun.HIZMETYENSURYIL,
urun.HIZMETYENSURAY,
urun.SATILANMIKTAR,
urun.IADEEDILENMIKTAR,
urun.KULLANILANMIKTAR,
urun.ZAYIMIKTAR,
urun.KONSINYECIKISMIKTAR,
urun.DIGERCIKISLAR,
urun.TEDARIKMIKTAR,
urun.IADEALINANMIKTAR,
urun.KONSINYEGIRISMIKTAR,
urun.EN,
urun.BOY,
urun.YUKSEKLIK,
urun.AGIRLIK,
urun.BOYUTAGIRLIGI,
urun.URUNKAYITTARIHI,
urun.GARANTISURESIBITIS,
urun.SONGUNCELLEMETARIHI,
urun.YENI,
urun.TESLIMATSURESI,
urun.TEDARIKCISTOKMIKTAR,
}
;
return urunMD;
}
public class StokEnvanteriSatiri
{
private string urunNo;
private string urunAdi;
private int acilisMiktari;
private int toplamTedarikMiktari;
private int toplamSatisMiktari;
private int mevcutMiktar;
private decimal satisFiyati;
private decimal toplamTutar;
private string paraBirimi;
public string UrunNo
{
get { return urunNo; }
set { urunNo = value; }
}
public string UrunAdi
{
get { return urunAdi; }
set { urunAdi = value; }
}
public int AcilisMiktari
{
get { return acilisMiktari; }
set { acilisMiktari = value;}
}
public int ToplamTedarikMiktari
{
get { return toplamTedarikMiktari; }
set { toplamTedarikMiktari = value; }
}
public int ToplamSatisMiktari
{
get { return toplamSatisMiktari; }
set { toplamSatisMiktari = value; }
}
public int MevcutMiktar
{
get { return mevcutMiktar; }
set { mevcutMiktar = value; }
}
public decimal SatisFiyati
{
get { return satisFiyati; }
set { satisFiyati = value; }
}
public decimal ToplamTutar
{
get { return toplamTutar; }
set { toplamTutar = value; }
}
public string ParaBirimi
{
get { return paraBirimi; }
set { paraBirimi = value; }
}
}
This method GetUrunListesiDoldur() basically returns a List<StokEnvanteriSatiri> list of StokEnvanterSatiri, I know the problem is that I am trying to join an in-memory list with an SQL table.
Is there any way to handle this?
I basically reversed the join and it worked
var urunMD = from urunStokbilgileri in urunStok
join urun in db.TBLP1URUNs
on urunStokbilgileri.UrunNo equals urun.ID
join kategori in db.TBLP1URUNKATEGORIs
on urun.KATEGORIID equals kategori.ID
......
You have to force LINQ to evaluate the second join locally. You can do this:
var urunMD = (from urun in db.TBLP1URUNs
join kategori in db.TBLP1URUNKATEGORIs
on urun.KATEGORIID equals kategori.ID).AsEnumerable()
.Join( [...] )
Rewrite your urunMD query to run in two parts. Use a Contains operator to filter the initial records in TBLP1URUNs and then return the database data only. Then use a separate LINQ-to-Objects query to combine the database data from TBLP1URUNs with the in-memory data in urunStok.

Categories

Resources