Append to a Func in LINQ - linq

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);
}

Related

LINQ query fails with nullable variable ormlite

I'm trying to write following LINQ query using ServiceStack Ormlite.
dbConn.Select<Product>(p => p.IsActive.HasValue && p.IsActive.Value)
Here, Product is my item class and "IsActive" is Nullable Bool property in that class. When this line executes it always throws "InvalidOperationException" with the message
variable 'p' of type '' referenced from scope '', but it is not defined
I tried different variants as following but still same exception result
dbConn.Select<Product>(p => p.IsActive.HasValue == true && p.IsActive.Value == true)
dbConn.Select<Product>(p => p.IsActive != null && p.IsActive.Value == true)
But if I just write
dbConn.Select<Product>(p => p.IsActive.HasValue)
then it works.
I'm puzzled what is the problem? Is this servicestack ormlite issue?
My answer can handle Nullable value like "value" and "HasValue" with servicestack ormlite. And But also with datetime nullable ,like 'createdate.value.Year'.
you must change two place.
modify VisitMemberAccess method:
protected virtual object VisitMemberAccess(MemberExpression m)
{
if (m.Expression != null)
{
if (m.Member.DeclaringType.IsNullableType())
{
if (m.Member.Name == nameof(Nullable<bool>.Value))
return Visit(m.Expression);
if (m.Member.Name == nameof(Nullable<bool>.HasValue))
{
var doesNotEqualNull = Expression.NotEqual(m.Expression, Expression.Constant(null));
return Visit(doesNotEqualNull); // Nullable<T>.HasValue is equivalent to "!= null"
}
throw new ArgumentException(string.Format("Expression '{0}' accesses unsupported property '{1}' of Nullable<T>", m, m.Member));
}
if (m.Member.DeclaringType == typeof(DateTime))
{
var ExpressionInfo = m.Expression as MemberExpression;
if (ExpressionInfo.Member.DeclaringType.IsNullableType())
{
if (ExpressionInfo.Member.Name == nameof(Nullable<bool>.Value))
{
var modelType = (ExpressionInfo.Expression as MemberExpression).Expression.Type;
var tableDef = modelType.GetModelDefinition();
var columnName = (ExpressionInfo.Expression as MemberExpression).Member.Name;
var QuotedColumnName = GetQuotedColumnName(tableDef, columnName);
if (m.Member.Name == "Year")
{
return new PartialSqlString(string.Format("DATEPART(yyyy,{0})", QuotedColumnName));
}
if (m.Member.Name == "Month")
return new PartialSqlString(string.Format("DATEPART(mm,{0})", QuotedColumnName));
}
if (ExpressionInfo.Member.Name == nameof(Nullable<bool>.HasValue))
{
var doesNotEqualNull = Expression.NotEqual(ExpressionInfo.Expression, Expression.Constant(null));
return Visit(doesNotEqualNull); // Nullable<T>.HasValue is equivalent to "!= null"
}
}
else
{
var modelType = ExpressionInfo.Expression.Type;
var tableDef = modelType.GetModelDefinition();
var columnName = ExpressionInfo.Member.Name;
var QuotedColumnName = GetQuotedColumnName(tableDef, columnName);
if (m.Member.Name == "Year")
return new PartialSqlString(string.Format("DATEPART(yyyy,{0})", QuotedColumnName));
if (m.Member.Name == "Month")
return new PartialSqlString(string.Format("DATEPART(mm,{0})", QuotedColumnName));
}
}
if (m.Expression.NodeType == ExpressionType.Parameter || m.Expression.NodeType == ExpressionType.Convert)
{
var propertyInfo = (PropertyInfo)m.Member;
var modelType = m.Expression.Type;
if (m.Expression.NodeType == ExpressionType.Convert)
{
var unaryExpr = m.Expression as UnaryExpression;
if (unaryExpr != null)
{
modelType = unaryExpr.Operand.Type;
}
}
var tableDef = modelType.GetModelDefinition();
if (propertyInfo.PropertyType.IsEnum)
return new EnumMemberAccess(
GetQuotedColumnName(tableDef, m.Member.Name), propertyInfo.PropertyType);
return new PartialSqlString(GetQuotedColumnName(tableDef, m.Member.Name));
}
}
var member = Expression.Convert(m, typeof(object));
var lambda = Expression.Lambda<Func<object>>(member);
var getter = lambda.Compile();
return getter();
}
modify VisitLambda method :
protected virtual object VisitLambda(LambdaExpression lambda)
{
if (lambda.Body.NodeType == ExpressionType.MemberAccess && sep == " ")
{
MemberExpression m = lambda.Body as MemberExpression;
if (m.Expression != null)
{
string r = VisitMemberAccess(m).ToString();
if (m.Member.DeclaringType.IsNullableType())
return r;
return string.Format("{0}={1}", r, GetQuotedTrueValue());
}
}
return Visit(lambda.Body);
}
This is nature of the Linq. In order to achieve what you need, you will need to use two where closes:
dbConn.Where<Product>(p => p.IsActive.HasValue).Where(p=>p.Value==true);

Filtering data from 2 table

Here I have 2 query variable for showing a different type of data,But i want to extract a particular data from first query ,which don't have reference in second query
I have the following code,but it does not work properly as.first query variable is used to join 4 table like A,B,C,D and Second Query variable Join table A and B.Here i want a data from First variable and it don't have any reference in second table
public List<ProductEntityList> GetProductListRegister()
{
var ProductList1 = new List<ProductEntityList>();
var ProductList = new List<ProductEntityList>();
var finalList = new List<ProductEntityList>();
try
{
using (HabitGreen01Entities ob = new HabitGreen01Entities())
{
var list1 = from ctr in ob.TblCreateUsers
join shp in ob.TblShopMasters
on ctr.id equals shp.Fk_CreateUser_Id
join prd in ob.TblProductMaster01
on shp.id equals prd.FK_ShopMaster_Id
select new ProductEntityList
{
Id = prd.id,
Name = prd.Name,
ShopName = shp.Name,
UserName = ctr.Name
};
ProductList1 = list1.ToList();
var temp = from pd in ob.TblProductMaster01
join cr in ob.TblAccountSettings
on pd.id equals cr.f_productmaster01Id
select new ProductEntityList
{
Id = (int)cr.f_productmaster01Id,
Name=pd.Name
};
ProductList = temp.ToList();
var temp1 = from item in ProductList1
where !ProductList.Contains(x=>item.Id)
select new ProductEntityList
{
Id = item.Id,
Name = item.Name,
ShopName = item.ShopName,
UserName = item.UserName
};
finalList = temp1.ToList();
// var temp1= ProductList1.Select(f=>f.Id).Intersect(ProductList.Select(b=>b.Id));
//finalList = matches.ToList();
}
}
catch (Exception e)
{
throw e;
}
return (finalList);
}
Try this:
var temp1 = from item in ProductList1
where !ProductList.Select(c=>c.Id).Contains(item.Id)
select new ProductEntityList
{
Id = item.Id,
Name = item.Name,
ShopName = item.ShopName,
UserName = item.UserName
};
You can write directly:
var finalList = (from item in ProductList1
where !ProductList.Select(c=>c.Id).Contains(item.Id)
select new ProductEntityList
{
Id = item.Id,
Name = item.Name,
ShopName = item.ShopName,
UserName = item.UserName
}).ToList();
var result = ProductList1.Where(p => !ProductList.Any(p2 => p2.Id== p.Id));
finalList = result.ToList();

Optimize queries for Union, Except, Join with LINQ and C#

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.

Error in View when i try use inherit Linq magic

I try make table where i keep children-parent data. Ofc root of parents is "0" and here in table can by many roots. When i try make this work i got error.
Unable to create a constant value of type 'Projekty03.ViewsModels.ParagrafViewModel'. Only primitive types ('such as Int32, String, and Guid') are supported in this context.
public ViewResult Index()
{
List<ParagrafViewModel> _paragrafparent = new List<ParagrafViewModel>();
_paragrafparent.Add(new ParagrafViewModel { ParagrafID = 0, ParagrafNazwa = "Root" });
var _paragrafparent2 = from pr in paragrafRepository.All
orderby pr.ParagrafID
select new ParagrafViewModel
{
ParagrafID = pr.ParagrafID,
ParagrafNazwa = pr.ParagrafNazwa
};
var _paragrafparent3 = _paragrafparent.Concat(_paragrafparent2).AsEnumerable();
var paragraf = from par in paragrafRepository.All
join rodzic_p in _paragrafparent3
on par.ParagrafParent equals rodzic_p.ParagrafID
orderby par.ParagrafParent, par.ParagrafID
select new ParagrafViewModel
{
ParagrafID = par.ParagrafID,
ParagrafNazwa = par.ParagrafNazwa,
ParagrafParent = par.ParagrafParent,
ParagrafCzynny = par.ParagrafCzynny,
ParagrafWplyw = par.ParagrafWplyw,
ParagrafParentNazwa = rodzic_p.ParagrafNazwa
};
return View(paragraf);
}
I believe is sht wrong with my poor magic LINQ think. How resolve this ?
Ok here is a answer for my own question. Im sure is not a pretty solution but ...
public ViewResult Index()
{
List<ParagrafViewModel> _paragrafparent = new List<ParagrafViewModel>();
_paragrafparent.Add(new ParagrafViewModel { ParagrafID = 0, ParagrafNazwa = "Root", ParagrafCzynny=false, });
var _paragrafparent2 = from pr in paragrafRepository.AllIncluding(paragraf => paragraf.WniosekPodzial)
orderby pr.ParagrafID
select new ParagrafViewModel
{
ParagrafID = pr.ParagrafID,
ParagrafNazwa = pr.ParagrafNazwa,
ParagrafParent = pr.ParagrafParent,
ParagrafCzynny = pr.ParagrafCzynny,
ParagrafWplyw = pr.ParagrafWplyw,
WniosekPodzialNazwa = pr.WniosekPodzial.WniosekPodzialNazwa
};
var _paragrafparent3 = _paragrafparent.Concat(_paragrafparent2).AsEnumerable();
var paragrafModel = from par in _paragrafparent3
join rodzic_p in _paragrafparent3
on par.ParagrafParent equals rodzic_p.ParagrafID
orderby par.ParagrafParent, par.ParagrafID
select new ParagrafViewModel
{
ParagrafID = par.ParagrafID,
ParagrafNazwa = par.ParagrafNazwa,
ParagrafParent = par.ParagrafParent,
ParagrafCzynny = par.ParagrafCzynny,
ParagrafWplyw = par.ParagrafWplyw,
ParagrafParentNazwa = rodzic_p.ParagrafNazwa,
WniosekPodzialNazwa = par.WniosekPodzialNazwa
};
return View(paragrafModel);
}

LINQ Union with Constant Values

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();
}

Resources