I tried to translate
select *
from (
select *, rng = row_number() over (partition by grp order by id)
from (
select *, grp = row_number() over (order by id) - row_number() over (partition by Name, Status, DateFinished order by id)
from tooling ) g
) gn
where rng = 1
order by id
from an earlier question (Grouping with partition and over in TSql)
With help with Row_number over (Partition by xxx) in Linq?
I got the solution to translate ONE of the row_number s but seems I'am out of luck to succesfully translate the entire question?
My attempt:
Tooling.OrderBy( x => x.Id)
.GroupBy( x => new {x.Name,x.Status,x.DateFinished} )
.Select( group => new { Group = group, Count = group.Count() } )
.SelectMany( groupWithCount =>
groupWithCount.Group.Select( b => b)
.Zip(
Enumerable.Range( 1, groupWithCount.Count ),
( j, i ) => new { j.Name,j.Status, j.DateFinished, RowNumber = i }
)
)
Try to use another way to get the result with LINQ. Get the previous record with ID < the current Id and check if all fields the same:
var Res = Tooling.Where(x=>{ var r = Tooling.Where(y=>y.Id<x.Id).OrderByDescending(y=>y.Id).FirstOrDefault();
if (r==null) return true;
return !((r.Name==x.Name) && (r.Status==x.Status) && (r.DateFinished==x.DateFinished));
})
.OrderBy( x => x.Id)
.Select(x=>x);
UPD: Here is a test routine:
public class TollingRecord
{
public int Id;
public String Name;
public int Status;
public DateTime? DateFinished;
}
...
private static void TestT1()
{
TollingRecord[] Tooling = new TollingRecord[]{ new TollingRecord() {Id=1, Name="Large", Status=0, DateFinished=null },
new TollingRecord() {Id=2, Name="Large", Status=1, DateFinished=null},
new TollingRecord() {Id=3, Name="Small", Status=0, DateFinished=null},
new TollingRecord() {Id=4, Name="Large", Status=2, DateFinished=null},
new TollingRecord() {Id=5, Name="Large", Status=2, DateFinished=null},
new TollingRecord() {Id=6, Name="Large", Status=1, DateFinished=null},
new TollingRecord() {Id=7, Name="Large", Status=1, DateFinished=null},
new TollingRecord() {Id=8, Name="Small", Status=1, DateFinished=DateTime.Now},
};
var Res = Tooling.Where(x=>{ var r = Tooling.Where(y=>y.Id<x.Id).OrderByDescending(y=>y.Id).FirstOrDefault();
if (r==null) return true;
return !((r.Name==x.Name) && (r.Status==x.Status) && (r.DateFinished==x.DateFinished));
})
.OrderBy( x => x.Id)
.Select(x=>x);
foreach (var a in Res)
{
Console.WriteLine("{0}/{1}/{2}", a.Id,a.Name,a.Status);
}
}
Outputs:
1/Large/0
2/Large/1
3/Small/0
4/Large/2
6/Large/1
8/Small/1
Related
I got a linq lambda select code that works before I added the Select index overload. Before, I got the list of records but I need the index which I use to assign a unique Id to each record. When I add with ToList(), I get an exception with no error/inner exception. Only way I can get the code to not throw an error is to use .AsEnumberable() but I need a list. I read many post that .ToList() works with the overload but I have been unsuccessful.
Here is my code and my attempt to fix this
var emps = this.DbContext.Employees
.GroupJoin(this.DbContext.Depts,
employee => employee.EmployeeId,
dept => dept.EmployeeId,
(employee, dept) => new { employee, dept }
)
.SelectMany(
employee_dept_left => employee_dept_left.dept.DefaultIfEmpty(),
(employee_dept_left, dept) => new { employee_dept_left, dept }
)
.Join(this.DbContext.Divs,
emp_emp_dept => emp_emp_dept.employee_dept_left.employee.DivId,
division => division.DivId,
(emp_emp_dept, division) => new { emp_emp_dept, division }
)
.Where(s => !string.IsNullOrEmpty(filter.selectedDiv))
.GroupBy(grouped => new
{
grouped.emp_emp_dept.employee_dept_left.employee.EmployeeId,
grouped.emp_emp_dept.employee_dept_left.employee.LastNm,
grouped.emp_emp_dept.employee_dept_left.employee.FirstNm,
grouped.emp_emp_dept.employee_dept_left.employee.DivId
})
.Select((joined, index) => new EmployeeViewModel
{
Id = index,
EmployeeId = joined.Key.EmployeeId,
LastNm = joined.Key.LastNm.Trim(),
FirstNm = joined.Key.FirstNm.Trim(),
DivisionId = joined.Key.DivId,
}).ToList();
The error message says
Could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
I tried using .AsEnumerable() instead of .ToList():
List<EmployeeViewModel> test = emps.Cast<EmployeeViewModel>().ToList();
but this throws an exception.
Any help is greatly appreciated.
Thanks in advance
Problem that this Select is not currently translatable to the SQL. You can make additional Select to solve issue with AsEnumerable().
...
.Select(joined => new
{
EmployeeId = joined.Key.EmployeeId,
LastNm = joined.Key.LastNm.Trim(),
FirstNm = joined.Key.FirstNm.Trim(),
DivisionId = joined.Key.DivisionId,
})
.AsEnumerable()
.Select((x, index) => new EmployeeViewModel
{
Id = index,
EmployeeId = x.EmployeeId,
LastNm = x.LastNm,
FirstNm = x.FirstNm,
DivisionId = x.DivisionId,
}).ToList();
And note that query is more readable in Query syntax when there are joins.
var query =
from employee in this.DbTracsContext.Employees
join dept in his.DbTracsContext.Depts on employee.EmployeeId equals dept.EmployeeId into employee_dept_left
from dept in employee_dept_left.DefaultIfEmpty()
join division in this.DbTracsContext.Depts on employee.DivisionId equals division.DivisionId
where string.IsNullOrEmpty(filter.DivisionSelection) || filter.DivisionSelection == "0" || employee.DivisionId == filter.DivisionSelection
group employee by new { employee.EmployeeId, employee.LastNm, employee.FirstNm, employee.DivisionId } into g
select new
{
EmployeeId = g.Key.EmployeeId,
LastNm = g.Key.LastNm.Trim(),
FirstNm = g.Key.FirstNm.Trim(),
DivisionId = g.Key.DivisionId,
};
var emps = query
.AsEnumerable()
.Select((x, index) => new EmployeeViewModel
{
Id = index,
EmployeeId = x.EmployeeId,
LastNm = x.LastNm,
FirstNm = x.FirstNm,
DivisionId = x.DivisionId,
}).ToList();
I have a method with a projection
public IQueryable<EmpDTO> GetEmployee(Func<Employee, EmpDTO> projection = null)
{
if(projection == null)
projection = emp => new EmpDTO {
Id = emp.Id,
Name = emp.Name,
Salary = emp.Salary,
};
return entities.Employees.Where(e => e.Salary > 10000).Select(projection);
}
It can be extended as follows:
query = classInstance.GetEmployee(emp => new EmpDTO {
Id = emp.Id,
Name = emp.Name,
Salary = emp.Salary,
Address = emp.Address
});
How can I APPEND only the "Address" field to the Func without
rewriting the entire fields (repeated fields Id, Name, Salary)
Using Expression, you can build a new lambda to initialize the fields:
public IQueryable<EmpDTO> GetEmployee(Expression<Func<Employee, EmpDTO>> addProj = null) {
Expression<Func<Employee, EmpDTO>> projection = emp => new EmpDTO {
Id = emp.Id,
Name = emp.Name,
Salary = emp.Salary,
};
if (addProj != null) {
var pBody = ((MemberInitExpression)projection.Body);
var newBindings = new ReadOnlyCollection<MemberBinding>(pBody.Bindings.Concat(((MemberInitExpression)addProj.Body).Bindings).ToList());
var newBody = Expression.MemberInit(pBody.NewExpression, newBindings);
projection = (Expression<Func<Employee, EmpDTO>>) Expression.Lambda(newBody, projection.Parameters);
}
return entities.Employees.Where(e => e.Salary > 10000).Select(projection);
}
You could also build the entire lambda from scratch, but that seemed like more work to me. Plus you can encapsulate the combine init logic into an extension method:
public static Expression<Func<TIn, TOut>> Add<TIn, TOut>(this Expression<Func<TIn, TOut>> proj, Expression<Func<TIn, TOut>> addProj = null) {
if (addProj != null) {
var pBody = ((MemberInitExpression)proj.Body);
var newBindings = new ReadOnlyCollection<MemberBinding>(pBody.Bindings.Concat(((MemberInitExpression)addProj.Body).Bindings).ToList());
var newBody = Expression.MemberInit(pBody.NewExpression, newBindings);
proj = (Expression<Func<TIn, TOut>>)Expression.Lambda(newBody, proj.Parameters);
}
return proj;
}
which reduces the GetEmployee body to:
public IQueryable<EmpDTO> GetEmployee(Expression<Func<Employee, EmpDTO>> addProj = null) {
Expression<Func<Employee, EmpDTO>> projection = emp => new EmpDTO {
Id = emp.Id,
Name = emp.Name,
Salary = emp.Salary,
};
if (addProj != null)
projection = projection.Add(addProj);
return entities.Employees.Where(e => e.Salary > 10000).Select(projection);
}
Main Code:
DomainServiceAccountManager d = new DomainServiceAccountManager();
EntityQuery<ListBuy> q = d.GetListMemberBuyQuery();
LoadOperation<ListBuy> l = d.Load(q);
DGListBuy.ItemsSource = l.Entities;
The code:
public IQueryable<ListBuy> GetListMemberBuy()
{
var membuy =
from mem in this.ObjectContext.Members
from b in this.ObjectContext.Buys.Where(b => b.ID_member == mem.ID)
.OrderByDescending(b => b.ID)
.DefaultIfEmpty()
select new { b.ID, mem.Name, b.Money, b.Tarikh };
return membuy;
}
I get the following message:
Cannot implicitly convert type 'System.Linq.IQueryable<AnonymousType#1>' to 'System.Linq.IQueryable<AccountManager.Web.ListBuy>'. An explicit conversion exists (are you missing a cast?)
You method GetListMemberBuy actually returns an IQueryable of anonymous type instead of an IQueryable of type ListBuy. These are not identical, hence the (compile time?) error.
I can only assume that ListBuy also exists in the database, but if so, then you can remove the anonymous type.
public IQueryable<ListBuy> GetListMemberBuy()
{
var membuy =
from mem in this.ObjectContext.Members
from b in this.ObjectContext.Buys.Where(b => b.ID_member == mem.ID)
.OrderByDescending(b => b.ID)
.DefaultIfEmpty()
select new ListBuy() { ID = b.ID, Name = mem.Name, Money = b.Money, Tarikh = b.Tarikh }; // <-- new ListBuy() !!
return membuy;
}
If ListBuy does not exist in the database, then you cannot return an IQueryable. Maybe this will work.
public IEnumerable<ListBuy> GetListMemberBuy()
{
var membuy =
from mem in this.ObjectContext.Members
from b in this.ObjectContext.Buys.Where(b => b.ID_member == mem.ID)
.OrderByDescending(b => b.ID)
.DefaultIfEmpty()
select new { b.ID, mem.Name, b.Money, b.Tarikh };
return membuy
.AsEnumerable()
.Select(b => new ListBuy() {
ID = b.ID, Name = b.Name, Money = b.Money, Tarikh = b.Tarikh
});
}
All this is a bit speculation since you didn't include the ListBuy class definition in your question.
I have 2 objects (lists loaded from XML) report and database (showed bellow in code) and i should analyse them and mark items with 0, 1, 2, 3 according to some conditions
TransactionResultCode = 0; // SUCCESS (all fields are equivalents: [Id, AccountNumber, Date, Amount])
TransactionResultCode = 1; // Exists in report but Not in database
TransactionResultCode = 2; // Exists in database but Not in report
TransactionResultCode = 3; // Field [Id] are equals but other fields [AccountNumber, Date, Amount] are different.
I'll be happy if somebody could found time to suggest how to optimize some queries.
Bellow is the code:
THANK YOU!!!
//TransactionResultCode = 0 - SUCCESS
//JOIN on all fields
var result0 = from d in database
from r in report
where (d.TransactionId == r.MovementID) &&
(d.TransactionAccountNumber == long.Parse(r.AccountNumber)) &&
(d.TransactionDate == r.MovementDate) &&
(d.TransactionAmount == r.Amount)
orderby d.TransactionId
select new TransactionList()
{
TransactionId = d.TransactionId,
TransactionAccountNumber = d.TransactionAccountNumber,
TransactionDate = d.TransactionDate,
TransactionAmount = d.TransactionAmount,
TransactionResultCode = 0
};
//*******************************************
//JOIN on [Id] field
var joinedList = from d in database
from r in report
where d.TransactionId == r.MovementID
select new TransactionList()
{
TransactionId = d.TransactionId,
TransactionAccountNumber = d.TransactionAccountNumber,
TransactionDate = d.TransactionDate,
TransactionAmount = d.TransactionAmount
};
//Difference report - database
var onlyReportID = report.Select(r => r.MovementID).Except(joinedList.Select(d => d.TransactionId));
//TransactionResultCode = 1 - Not Found in database
var result1 = from o in onlyReportID
from r in report
where (o == r.MovementID)
orderby r.MovementID
select new TransactionList()
{
TransactionId = r.MovementID,
TransactionAccountNumber = long.Parse(r.AccountNumber),
TransactionDate = r.MovementDate,
TransactionAmount = r.Amount,
TransactionResultCode = 1
};
//*******************************************
//Difference database - report
var onlyDatabaseID = database.Select(d => d.TransactionId).Except(joinedList.Select(d => d.TransactionId));
//TransactionResultCode = 2 - Not Found in report
var result2 = from o in onlyDatabaseID
from d in database
where (o == d.TransactionId)
orderby d.TransactionId
select new TransactionList()
{
TransactionId = d.TransactionId,
TransactionAccountNumber = d.TransactionAccountNumber,
TransactionDate = d.TransactionDate,
TransactionAmount = d.TransactionAmount,
TransactionResultCode = 2
};
//*******************************************
var qwe = joinedList.Select(j => j.TransactionId).Except(result0.Select(r => r.TransactionId));
//TransactionResultCode = 3 - Transaction Results are different (Amount, AccountNumber, Date, )
var result3 = from j in joinedList
from q in qwe
where j.TransactionId == q
select new TransactionList()
{
TransactionId = j.TransactionId,
TransactionAccountNumber = j.TransactionAccountNumber,
TransactionDate = j.TransactionDate,
TransactionAmount = j.TransactionAmount,
TransactionResultCode = 3
};
you may try something like below:
public void Test()
{
var report = new[] {new Item(1, "foo", "boo"), new Item(2, "foo2", "boo2"), new Item(3, "foo3", "boo3")};
var dataBase = new[] {new Item(1, "foo", "boo"), new Item(2, "foo22", "boo2"), new Item(4, "txt", "rt")};
Func<Item, bool> inBothLists = (i) => report.Contains(i) && dataBase.Contains(i);
Func<IEnumerable<Item>, Item, bool> containsWithID = (e, i) => e.Select(_ => _.ID).Contains(i.ID);
Func<Item, int> getCode = i =>
{
if (inBothLists(i))
{
return 0;
}
if(containsWithID(report, i) && containsWithID(dataBase, i))
{
return 3;
}
if (report.Contains(i))
{
return 2;
}
else return 1;
};
var result = (from item in dataBase.Union(report) select new {Code = getCode(item), Item = item}).Distinct();
}
public class Item
{
// You need also to override Equals() and GetHashCode().. I omitted them to save space
public Item(int id, string text1, string text2)
{
ID = id;
Text1 = text1;
Text2 = text2;
}
public int ID { get; set; }
public string Text1 { get; set; }
public string Text2 { get; set; }
}
Note that you need to either implement Equals() for you items, or implement an IEqualityComparer<> and feed it to Contains() methods.
Very primitive question but I am stuck (I guess being newbie). I have a function which is supposed to send me the list of companies : ALSO, I want the caller to be able to specify a top element for the drop-down list as well.. (say for "None"). I have following piece of code, how I will append the Top Element with the returning SelectList?
public static SelectList GetCompanies( bool onlyApproved, FCCIEntityDataContext entityDataContext, SelectListItem TopElement )
{
var cs = from c in entityDataContext.Corporates
where ( c.Approved == onlyApproved || onlyApproved == false )
select new
{
c.Id,
c.Company
};
return new SelectList( cs.AsEnumerable(), "Id", "Comapny" );
}
Thanks!
This should work for you:
List<Corporate> corporates =
(from c in entityDataContext.Corporates
where (c.Approved == onlyApproved || onlyApproved == false)
select c).ToList();
corporates.Add(new Corporate { Id = -1, Company = "None" });
return new SelectList(corporates.AsEnumerable(), "Id", "Comapny");
This method has always worked for me.
public static SelectList GetCompanies( bool onlyApproved, FCCIEntityDataContext entityDataContext, SelectListItem TopElement )
{
var cs = from c in entityDataContext.Corporates
where ( c.Approved == onlyApproved || onlyApproved == false )
select new SelectListItem {
Value = c.Id,
Text = c.Company
};
var list = cs.ToList();
list.Insert(0, TopElement);
var selectList = new SelectList( list, "Value", "Text" );
selectList.SelectedValue = TopElement.Value;
return selectList;
}
Update forgot the lesson I learned when I did this. You have to output the LINQ as SelectListItem.
cs.ToList().Insert(0, new { TopElement.ID, TopElement.Company });
You could convert it to a list as indicated or you could union the IQueryable result with a constant array of one element (and even sort it):
static void Main(string[] args)
{
var sampleData = new[] {
new { Id = 1, Company = "Acme", Approved = true },
new { Id = 2, Company = "Blah", Approved = true }
};
bool onlyApproved = true;
var cs = from c in sampleData
where (c.Approved == onlyApproved || onlyApproved == false)
select new
{
c.Id,
c.Company
};
cs = cs.Union(new [] {new { Id = -1, Company = "None" }}).OrderBy(c => c.Id);
foreach (var c in cs)
{
Console.WriteLine(String.Format("Id = {0}; Company = {1}", c.Id, c.Company));
}
Console.ReadKey();
}