Cross Apply Values (1, columnName) as (code, display) in LINQ - sql

I have an entity like:
public class TEvent
{
public int? January { get; set; }
public int? February { get; set; }
public int? March { get; set; }
public int? April { get; set; }
public int? May { get; set; }
public int? June { get; set; }
public int? July { get; set; }
public int? August { get; set; }
public int? September { get; set; }
public int? October { get; set; }
public int? November { get; set; }
public int? December { get; set; }
//and much more
}
What I want my LINQ code to achieve:
SELECT MonthCode, * FROM T_Events
CROSS APPLY (VALUES (1, January), (2, February), (3, March), (4, April), (5, May), (6, June), (7, July), (8, August), (9, September), (10, October), (11, November), (12, December)) AS CA(MonthCode, Display)
WHERE Display = -1
The problem is that I don't know how to have LINQ know it is a column name.
What I've tried
var dd = new List<object>()
{
new {January = 1 },
new {February = 2},
new {March = 3},
new {April = 4},
new { May = 5},
new {June = 6},
new {July = 7},
new {Augest = 8},
new {September = 9},
new {October = 10},
new {November = 11},
new {December = 12}
};
var q =
from events in _context.TEvents
from mds in dd
Now when I write mds. I don't get anything because of course it is of type object, but if I don't use object how would I specify custom column names and get the value of 'Display'
Expected result:
The value of Months(Jan, feb etc...) can be (0 or -1). When the Columns are converted to Rows, I get 12 rows, each with it's own MonthCode and display, now let's say for a record March and April are -1, then the 3rd and 4th record will have Display = -1 and the rest will have 0, while all will retain it's month codes

I would suggest to use linq2db.EntityFrameworkCore, note that I'm one of the creators.
This extension brings power of linq2db to EF Core projects. And library supports join to local collections.
class MonthDescription
{
public int MonthCode { get; set; }
public string Display { get; set; }
}
var months = new []
{
new MonthDescription { MonthCode = 1, Display = "January" },
new MonthDescription { MonthCode = 2, Display = "February" },
new MonthDescription { MonthCode = 3, Display = "March" },
new MonthDescription { MonthCode = 4, Display = "April" },
new MonthDescription { MonthCode = 5, Display = "May" },
new MonthDescription { MonthCode = 6, Display = "June" },
new MonthDescription { MonthCode = 7, Display = "July" },
new MonthDescription { MonthCode = 8, Display = "Augest" },
new MonthDescription { MonthCode = 9, Display = "September" },
new MonthDescription { MonthCode = 10, Display = "October" },
new MonthDescription { MonthCode = 11, Display = "November" },
new MonthDescription { MonthCode = 12, Display = "December" }
};
var query =
from event in _context.TEvents.ToLinqToDB() // switching LINQ provider
from md in months
select new
{
event,
md
};

Thanks #Svyatoslav Danyliv For your answer. I've also found another way to achieve what I want through Union.
First Write the main query:
var evAll = from child in _context.TEvents
join parent in _context.TEventDomains
on child.ID equals parent.ID
join p in _context.TPriorities
on parent.IDPriority equals p.Idpriority into PJoin
from p in PJoin.DefaultIfEmpty()
select new TEventExtended(child, p.PriorityCode)
Now I can select for each month and union the answer. like
from ev in evAll
select new TEventExtended(ev, 1, ev.January))
.Union(
from ev in evAll
select new TEventExtended(ev, 2, ev.February))
...etc etc
Here, I created a class/record which extend TEvent by 3 properties i.e. MonthCode, Display, Priority, and created constructors which copies data from the original TEvent/TEventExtended
NOTE: If you're using C# 10. You don't need to create constructors, simply use: new TEventExtended() with { MonthCode = 12, Display = ev.December}
Finally after adding 12 unions
.Union(
from ev in evAll
select new TEventExtended(ev, 12, ev.December))
.Where(x => x.Display == -1)
.OrderBy(x => x.MonthCode >= request.Month ? x.MonthCode : x.MonthCode + 12)
.ThenBy(x => x.PCode)
.Select(x =>
new MyFinalDTO
{
//projection
}).ToListAsync(cancellationToken);

Related

EF Core Code First Cannot insert explicit value for identity column in table' when IDENTITY_INSERT is set to OFF

Im using code first approach in my project. Im trying to create dynamic Create action and dynamic Complete(UnitOfWork) action as well. Im getting that error after my function adding first value.
Let me show you to my function :
public void WebFairHallSeatingCreator(int webFairHallID)
{
int[] hallSeatingOrderColumnValue = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
string[] hallSeatingOrderRowValue = { "a", "b", "c", "d", "e", "f", "g" };
var hallSeatingOrder = new HallSeatingOrder();
for (int row = 0; row < hallSeatingOrderRowValue.Length; row++)
{
for (int col = 1; col <= hallSeatingOrderColumnValue.Length; col++)
{
if ((hallSeatingOrderRowValue.GetValue(row).ToString() == "c") || (hallSeatingOrderRowValue.GetValue(row).ToString() == "d"))
{
if ((col == 5) || (col == 6) || (col == 7))
{
hallSeatingOrder.HallSeatingOrderRowValue = "x";
hallSeatingOrder.HallSeatingOrderColumnValue = 15;
_unitOfWorkWFH.RepositoryHallSeatingOrder.Create(hallSeatingOrder);
}
else
{
hallSeatingOrder.HallSeatingOrderRowValue= hallSeatingOrderRowValue.GetValue(row).ToString();
hallSeatingOrder.HallSeatingOrderColumnValue = col;
hallSeatingOrder.WebFairHallID = webFairHallID;
_unitOfWorkWFH.RepositoryHallSeatingOrder.Create(hallSeatingOrder);
_unitOfWorkWFH.Complete();// This is the point
}
}
else
{
hallSeatingOrder.HallSeatingOrderRowValue = hallSeatingOrderRowValue.GetValue(row).ToString();
hallSeatingOrder.HallSeatingOrderColumnValue = col;
hallSeatingOrder.WebFairHallID = webFairHallID;
_unitOfWorkWFH.RepositoryHallSeatingOrder.Create(hallSeatingOrder);
_unitOfWorkWFH.Complete();// This is the point
}
}
}
}
here its my HallSeatingOrder class :
public class HallSeatingOrder : Base
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int HallSeatingOrderID { get; set; }
[Required]
public int WebFairHallID { get; set; }
public int? CompanyID { get; set; }
[Required]
public int HallSeatingOrderColumnValue { get; set; }
[Required]
public string HallSeatingOrderRowValue { get; set; }
public bool HallSeatingOrderIsSpecial { get; set; }
public Company Company { get; set; }
public WebFairHall WebFairHall { get; set; }
}
public class HallSeatingOrderConfiguration : IEntityTypeConfiguration<HallSeatingOrder>
{
public void Configure(EntityTypeBuilder<HallSeatingOrder> builder)
{
builder.HasKey(x => x.HallSeatingOrderID);
builder.HasOne(x => x.Company)
.WithMany(x => x.HallSeatingOrders)
.HasForeignKey(x => x.CompanyID)
.OnDelete(DeleteBehavior.NoAction);
builder.HasOne(x => x.WebFairHall)
.WithMany(x => x.HallSeatingOrders)
.HasForeignKey(x => x.WebFairHallID)
.OnDelete(DeleteBehavior.NoAction);
}
}
i allready look out of every question with my problem.
i try using ValueGeneratedOnAdd() and [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)] but none of it worked on me.
thanks for the #IvanStoev, i solve that problem. there is nothing wrong with my model and my configuration. there was a one mistake and let me show u that:
public void WebFairHallSeatingCreator(int webFairHallID)
{
int[] hallSeatingOrderColumnValue = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
string[] hallSeatingOrderRowValue = { "a", "b", "c", "d", "e", "f", "g" };
for (int row = 0; row < hallSeatingOrderRowValue.Length; row++)
{
for (int col = 1; col <= hallSeatingOrderColumnValue.Length; col++)
{
if ((hallSeatingOrderRowValue.GetValue(row).ToString() == "c") || (hallSeatingOrderRowValue.GetValue(row).ToString() == "d"))
{
if ((col == 5) || (col == 6) || (col == 7))
{
var hallSeatingOrder = new HallSeatingOrder();
hallSeatingOrder.HallSeatingOrderRowValue = "x";
hallSeatingOrder.HallSeatingOrderColumnValue = 15;
_unitOfWorkWFH.RepositoryHallSeatingOrder.Create(hallSeatingOrder);
_unitOfWorkWFH.Complete();
}
else
{
var hallSeatingOrder = new HallSeatingOrder();
hallSeatingOrder.HallSeatingOrderRowValue= hallSeatingOrderRowValue.GetValue(row).ToString();
hallSeatingOrder.HallSeatingOrderColumnValue = col;
hallSeatingOrder.WebFairHallID = webFairHallID;
_unitOfWorkWFH.RepositoryHallSeatingOrder.Create(hallSeatingOrder);
_unitOfWorkWFH.Complete();
}
}
else
{
var hallSeatingOrder = new HallSeatingOrder();
hallSeatingOrder.HallSeatingOrderRowValue = hallSeatingOrderRowValue.GetValue(row).ToString();
hallSeatingOrder.HallSeatingOrderColumnValue = col;
hallSeatingOrder.WebFairHallID = webFairHallID;
_unitOfWorkWFH.RepositoryHallSeatingOrder.Create(hallSeatingOrder);
_unitOfWorkWFH.Complete();
}
}
}
}
i need to create that var hallSeatingOrder = new HallSeatingOrder(); for each of for loop,thats all. Thanks to him.

Building lambda expression dynamically

I know how to build a simple lambda like x => x > 5:
int[] nbs = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
IEnumerable<int> result1 = nbs.Where(x => x > 5);
ParameterExpression parameter = Expression.Parameter(typeof(int), "x");
ConstantExpression constant = Expression.Constant(5);
BinaryExpression expressionBody = Expression.GreaterThan(parameter, constant);
Expression<Func<int, bool>> expression = Expression.Lambda<Func<int, bool>>(expressionBody, parameter);
IEnumerable<int> result2 = nbs.Where(expression.Compile());
But, how do I create a more complex lambda like this p=>p.FindAttribute("Gender")?.Value == "Female" in the same style as above?
public class Person
{
public bool POI { get; set; } = false;
public string Name { get; set; }
public List<Car> Cars { get; set; }
public List<Attribute> Attributes { get; set; }
public bool PersonOfInterest()
{
return POI;
}
public Attribute FindAttribute(string name)
{
return Attributes.FirstOrDefault(a => a.Name == name);
}
}
public class Attribute
{
public string Name { get; set; }
public string Value { get; set; }
public Attribute(string name, string value) { Name = name; Value = value; }
}
public class Car
{
public string Make { get; set; }
public int Horsepowers { get; set; }
public string Fuel { get; set; }
}
Person p1 = new Person();
p1.Name = "Thom";
p1.POI = true;
p1.Attributes = new List<Attribute>() {new Attribute("Length", "Tall"), new Attribute("Hair", "Long hair")};
p1.Cars = new List<Car>()
{
new Car(){Horsepowers = 100, Make = "Toyota", Fuel = "Diesel"},
new Car(){Horsepowers = 200, Make = "Fiat", Fuel = "Diesel"},
new Car(){Horsepowers = 300, Make = "Audi", Fuel = "Diesel"},
new Car(){Horsepowers = 150, Make = "Ferrari", Fuel = "Petrol"}
};
Person p2 = new Person();
p2.POI = false;
p2.Attributes = new List<Attribute>() { new Attribute("Nationality", "English"), new Attribute("Gender", "Female") };
p2.Name = "Sophie";
p2.Cars = new List<Car>()
{
new Car(){Horsepowers = 500, Make = "McLaren", Fuel = "Diesel"},
new Car(){Horsepowers = 200, Make = "Volvo", Fuel = "Diesel"},
new Car(){Horsepowers = 300, Make = "Audi", Fuel = "Diesel"},
new Car(){Horsepowers = 400, Make = "Ferrari", Fuel = "Diesel"}
};
IEnumerable<Person> res = persons.Where(p=>p.FindAttribute("Gender")?.Value == "Female");
Of course I could always create a new method on Person like:
public bool HasAttributeWithValue(string name, string value)
{
return FindAttribute(name)?.Value == value;
}
But it's still of interest to me if it's possible to construct the complex lambda dynamically!
This piece of code does the job.
ParameterExpression parameter = Expression.Parameter(typeof(Person), "p");
ConstantExpression my_null_object = Expression.Constant(null);
ConstantExpression gender = Expression.Constant("Gender");
MethodCallExpression methodcall = Expression.Call(parameter, typeof(Person).GetMethod("FindAttribute"), gender);
BinaryExpression is_null = Expression.Equal(methodcall, my_null_object);
ConstantExpression constant = Expression.Constant("Female");
MemberExpression member = Expression.Property(methodcall, typeof(Attribute).GetProperty("Value"));
BinaryExpression expressionBody = Expression.Equal(member, constant);
BinaryExpression cond = Expression.AndAlso(Expression.IsFalse(is_null), expressionBody);
Maybe clearer this way
ParameterExpression parameter = Expression.Parameter(typeof(Person), "p");
ConstantExpression my_null_object = Expression.Constant(null);
MethodCallExpression methodcall = Expression.Call(parameter, typeof(Person).GetMethod("FindAttribute"), Expression.Constant("Gender"));
BinaryExpression cond = Expression.AndAlso(
Expression.IsFalse(Expression.Equal(methodcall, my_null_object)),
Expression.Equal(
Expression.Property(methodcall, typeof(Attribute).GetProperty("Value")),
Expression.Constant("Female")
)
);
Null-conditional operator is a language feature. It can be converted to this code:
var attribute = p.FindAttribute("Gender");
return attribute == null ? false : attribute.Value == "Female";
To build an expression tree you can use Expression.Block:
var p = Expression.Parameter(typeof(Person), "p");
var findAttribute = Expression.Call(p,
typeof(Person).GetMethod(nameof(Person.FindAttribute)),
Expression.Constant("Gender"));
var attribute = Expression.Parameter(typeof(Attribute), "attribute");
var body = Expression.Block(
new[] {attribute}, // local variables
new Expression[]
{
Expression.Assign(attribute, findAttribute),
Expression.Condition(
Expression.Equal(attribute, Expression.Constant(null)),
Expression.Constant(false),
Expression.Equal(
Expression.PropertyOrField(attribute, "Value"),
Expression.Constant("Female")))
}
);
var lambda = Expression.Lambda<Func<Person, bool>>(body, p);

LINQ JOIN ON TOP 1 ROW

I would like to do a LINQ join including only one row. In SQL, that's what I want to do :
JOIN Person ON ( SELECT TOP (1) top1Person.ID FROM Person AS top1Person WHERE top1Person.CompanyID = Company.ID ) = Person.ID
But I am not able to do it in LINQ, I tried this :
join pers in ctx.Persons on cmp.Persons.First().ID equals pers.ID
but the Fisrt() method is not allowed here...
Someone could help me ? Thanks
There are many ways to accomplish that. One way would be to use First() or FirstOrDefault() on the result of query. Try this:
var results = (from id in ids
join p in persons on id equals p.ID
where p.CompanyID == companyID
select p).FirstOrDefault();
You can do OrderBy(x => x.ID).FirstOrDefault() first if you want the lowest ID first, or OrderByDesc(x => x.ID).FirstOrDefault() if you want the highest first.
EDIT: Upon further inspection of your sample code, it looks like this may be more of what you're looking for.
var results = (from company in companies
join p in persons on persons.First(x => x.CompanyID == company.ID).ID equals p.ID
select p);
You should be able to call First() in that spot, but it looks like your LINQ query doesn't quite match up with your SQL. Should you be doing ctx.Persons.First() intstead of cmp.Persons.First()? If the above solution doesn't work for you, then we need more information.
FINAL EDIT: One last full solution for a slightly different interpretation. This code is tested and works.
using System;
using System.Collections.Generic;
using System.Linq;
namespace com.test
{
public class Program
{
public static void Main(string[] args)
{
List<int> ids = new List<int>();
List<Company> companies = new List<Company>();
List<Person> persons = new List<Person>();
persons.Add(new Person(1, 1, "John Smith"));
persons.Add(new Person(2, 1, "Adam Jones"));
persons.Add(new Person(3, 1, "Alex Rabbit"));
persons.Add(new Person(4, 2, "Jessica Thurman"));
persons.Add(new Person(5, 2, "Sam Riot"));
persons.Add(new Person(6, 2, "Donald Lewis"));
persons.Add(new Person(7, 3, "Lindsay Bonaparte"));
persons.Add(new Person(8, 3, "Desmond Tutu"));
persons.Add(new Person(9, 3, "Kevin Gargoyle"));
persons.Add(new Person(10, 4, "Emily Francis"));
persons.Add(new Person(11, 4, "Caitlin Elizabeth"));
persons.Add(new Person(12, 4, "Harry Finstein"));
persons.Add(new Person(13, 4, "Carla Loper"));
companies.Add(new Company(1, "McDonalds", persons.Where(x => x.CompanyID == 1).ToList()));
companies.Add(new Company(2, "Burger King", persons.Where(x => x.CompanyID == 2).ToList()));
companies.Add(new Company(3, "Wendy's", persons.Where(x => x.CompanyID == 3).ToList()));
companies.Add(new Company(4, "Arby's", persons.Where(x => x.CompanyID == 4).ToList()));
var results = (from cmp in companies
join p in persons on cmp.Persons.First().ID equals p.ID
select p);
foreach (var p in results)
{
Console.WriteLine("Person: " + p.Name);
}
Console.ReadKey();
}
public class Person
{
public int ID { get; set; }
public int CompanyID { get; set; }
public string Name { get; set; }
public Person (int id, int companyID, string name)
{
ID = id;
CompanyID = companyID;
Name = name;
}
}
public class Company
{
public int ID { get; set; }
public string Name { get; set; }
public List<Person> Persons { get; set; }
public Company(int id, string name, List<Person> persons)
{
ID = id;
Name = name;
Persons = persons;
}
}
}
}

code first database : SqlException: Violation of PRIMARY KEY constraint 'PK_dbo.PrivatKasses'.

I have a model like this:
class PrivatKasse
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int ID { get; set; }
public decimal Betrag { get; set; }
public int BenutzerID { get; set; }
}
and a function that checks if the table is empty. if yes functions adds new rows. The function:
public static void DatabaseChecker()
{
using (var _db = new DataContext())
{
if (!_db.KasseGemeinsam.Any())
{
_db.KasseGemeinsam.Add(new Models.Data.KasseGemeinsamModel() { Jahr = DateTime.Now.Year.ToString(), Monat = MonatenVertauchen.ZahlZuMonaten(Convert.ToInt32(DateTime.Now.Month)), Haushalt = 0, Hygine = 0, Mobel = 0, Nahrung = 0, Schreibware = 0, Sonstiges = 0 });
_db.SaveChanges();
}
if (!_db.KassePrivat.Any())
{
_db.KassePrivat.Add(new Models.Data.KassePrivatModel() { Jahr = DateTime.Now.Year.ToString(), Monat = MonatenVertauchen.ZahlZuMonaten(Convert.ToInt32(DateTime.Now.Month)), UserID = UserIdentity.UserID, Fahrkosten = 0, Hygine = 0, Mobel = 0, Nahrung = 0, Schreibware = 0, Sonstiges = 0 });
_db.SaveChanges();
}
if (!_db.GemeinsamKasse.Any())
{
_db.GemeinsamKasse.Add(new Models.Data.GemeinsamKasse() { Betrag = 0 });
_db.SaveChanges();
}
if (!_db.PrivatKasse.Any())
{
List<int> userIDs = new List<int>();
foreach (var item in _db.UsersTbl)
{
userIDs.Add(item.ID);
}
using (var _db2 = new DataContext())
{
foreach (var item in userIDs)
{
_db2.PrivatKasse.Add(new Models.Data.PrivatKasse() { BenutzerID = item, Betrag = 0 });
}
_db2.SaveChanges();
}
}
}
But I get an SqlExeption :
System.Data.Entity.Infrastructure.DbUpdateException: "An error occurred while updating the entries. See the inner exception for details."
UpdateException: An error occurred while updating the entries. See the
inner exception for details.
SqlException: Violation of PRIMARY KEY constraint
'PK_dbo.PrivatKasses'. Cannot insert duplicate key in object
'dbo.PrivatKasses'. The duplicate key value is (0).
The statement has been terminated.
How can i fix this problem?
I believe the issue is because you are not providing the id (and the default in a int is 0) since you declare
[DatabaseGenerated(DatabaseGeneratedOption.None)]
which means that the db will not create the id, I recommend to use Identity instead
[DatabaseGenerated(DatabaseGeneratedOption.Identity )]
then you should be able to use
_db2.PrivatKasse.Add(new Models.Data.PrivatKasse() { BenutzerID = item, Betrag = 0 })
otherwise provide the id
_db2.PrivatKasse.Add(new Models.Data.PrivatKasse() {ID= customID, BenutzerID = item, Betrag = 0 })

SQL server convert datetime using LINQ

I have the query I need in SQL server but I'm using Entity Framework and would like the same results using LINQ. Here is the SQL:
select convert(varchar, date_recorded, 12), MAX(outdoor_temp), MAX(wind_speed)
from weatherdata
group by convert(varchar, date_recorded, 12)
order by convert(varchar, date_recorded, 12) DESC ;
This converts my datetime column to a correct format that allows me to group properly. Basically I need to be able to convert my SQL datetime to yymmdd format.
Many thanks
class Program
{
static void Main(string[] args)
{
var entries = new List<Entry>()
{
{new Entry { Date = DateTime.UtcNow, Outdoor_Temp = 10, Wind_Speed = 5 }},
{new Entry { Date = DateTime.UtcNow, Outdoor_Temp = 5, Wind_Speed = 10}},
{new Entry { Date = DateTime.UtcNow.AddDays(-1), Outdoor_Temp = 15, Wind_Speed = 7}}
};
(from e in entries
group e by e.Date.ToShortDateString() into g
select
new
{
StringDate = g.Key,
MaxWind_Speed = g.Max(entry => entry.Wind_Speed),
MaxOutdoor_Temp = g.Max(entry => entry.Outdoor_Temp)
}
).ToList().ForEach(Console.WriteLine);
}
public class Entry
{
public DateTime Date { get; set; }
public int Outdoor_Temp { get; set; }
public int Wind_Speed { get; set; }
public override string ToString()
{
return string.Format("Date : {0}, Outdoor_Temp : {1}, Wind_Speed : {2}", Date, Outdoor_Temp, Wind_Speed);
}
}
}