Dynamic EF Where Clause raising ArgumentNullException - c#

I'm trying to code a method that, in it's class given the values of some of the attributes, returns a filtered DbSet. The code, so far, is:
public IEnumerable<Pesquisa> Pesquisas {
get {
PrometheusDBContext db = new PrometheusDBContext();
var temp = db.Pesquisas;
if ((this.Filtro.Nome != null) && (this.Filtro.Nome.Trim() != ""))
{
temp = (temp.Where(p => SqlFunctions.PatIndex(this.Filtro.Nome, p.Nome) > 0) as DbSet<Pesquisa>);
}
if ((this.Filtro.CodTipoPesquisa != null) && (this.Filtro.CodTipoPesquisa.Trim() != ""))
{
temp = (temp.Where(p => p.CodTipoPesquisa == this.Filtro.CodTipoPesquisa.Trim()) as DbSet<Pesquisa>);
}
if ((this.Filtro.IDStatusPesquisa != null) && (this.Filtro.IDStatusPesquisa > 0))
{
temp = (temp.Where(p => p.IDStatusPesquisa == this.Filtro.IDStatusPesquisa) as DbSet<Pesquisa>);
}
if ((this.Filtro.DataCriacao_Inicial != null) && (this.Filtro.DataCriacao_Final != null))
{
temp = (temp.Where(p => (p.DataCriacao >= this.Filtro.DataCriacao_Inicial) && (p.DataCriacao <= this.Filtro.DataCriacao_Final)) as DbSet<Pesquisa>);
}
else
{
if (this.Filtro.DataCriacao_Inicial != null)
{
temp = (temp.Where(p => p.DataCriacao >= this.Filtro.DataCriacao_Inicial) as DbSet<Pesquisa>);
}
if (this.Filtro.DataCriacao_Final != null)
{
temp = (temp.Where(p => p.DataCriacao <= this.Filtro.DataCriacao_Final) as DbSet<Pesquisa>);
}
}
return temp
.Include(p => p.Usuario)
.Include(p => p.StatusPesquisa)
.Include(p => p.TipoPesquisa)
.Include(p => p.ModeloTermoAdesao)
.Include(p => p.Pacientes)
.ToList();
}
Problem is: everytime one of the attributes is filled with some value (i.e.: this.Filtro.Nome = "test" ), the ToList() raises an ArgumentNullExcpetion. Any ideas?

You shouldn't cast to DbSet at the end of each line.
Also, declare
IQueryable<Pesquisa> temp = db.Pesuisas;
// your code follows.
The reason behind it is that although you start with a DbSet, applying operators changes its type. Your dynamic cast returns null then.

Related

Get result using condition not works properly (C#)

I trying to get data from datatabase and assign it to list
Here is 2 conditions. 1 - I have only categoryId and 2 - I have category and subcategoryId
I wrote code like this
public async Task<List<TimelineItemDto>> GetFilteredClasses(int? categoryId, int? subcategoryId)
{
List<TimelineItemDto> filteredClasses;
if (categoryId != null)
{
filteredClasses = await _context.Events
.Where(x => x.CategoryId == categoryId && x.EventType == EntityType.Class)
.ProjectTo<TimelineItemDto>()
.ToListAsync();
}
if (categoryId != null && subcategoryId != null)
{
filteredClasses = await _context.Events
.Where(x => x.CategoryId == categoryId && x.SubcategoryId == subcategoryId &&
x.EventType == EntityType.Class)
.ProjectTo<TimelineItemDto>()
.ToListAsync();
}
else
{
filteredClasses = await _context.Events.Where(x =>
x.EventType == EntityType.Class).ProjectTo<TimelineItemDto>()
.ToListAsync();
}
return filteredClasses;
}
but at first if I got this
A second if and at else all okay and it's executed and filling list
How I can solve this?
Well I would do something like below :
public async Task<List<TimelineItemDto>> GetFilteredClasses(int? categoryId, int? subcategoryId)
{
var filteredClasses = _context.Events.Where(x => x.EventType == EntityType.Class);
if (categoryId != null)
{
filteredClasses = filteredClasses.
.Where(x => x.CategoryId == categoryId);
}
if (categoryId != null && subcategoryId != null)
{
filteredClasses = filteredClasses.Where(x => x.SubcategoryId == subcategoryId );
}
return await filteredClasses.ProjectTo<TimelineItemDto>()
.ToListAsync();;
}
This way you will avoid materializing multiple queries.
You should update the condition flow as below:
if (categoryId != null && subcategoryId != null)
{
...
}
else if (categoryId != null)
{
...
}
else
{
...
}
With above, the filteredClasses will not be overridden by last else condition. Your current code first evaluate if and then if & else. Both are different code blocks and last else is always getting executed.

The cast to value type System.Boolean failed because the materialized value is null, result type's generic parameter must use a nullable type

This code was working before but now I've got this error: The cast to value type 'System.Boolean' failed because the materialized value is null. Either the result type's generic parameter or the query must use a nullable type.
public async Task<ActionResult> BankDepositVoucher(BankDepositVoucherSearchViewModel search, int? PageNo)
{
var model = new BankDepositVoucherListViewModel
{
Search = search ?? new BankDepositVoucherSearchViewModel()
};
if (search != null)
{
search.StartDate = search.StartDate.ToStartOfDay();
search.EndDate = search.EndDate.ToEndOfDay();
}
try
{
var Vouchers = DbManager.Invoices.Include(x => x.BankDepositVoucher)
.Where(x => x.Type == InvoiceType.BankDepositVoucher
&& (x.VoucherNumber == search.VoucherNo || search.VoucherNo == null)
&& (x.BankDepositVoucher.SlipNo.Contains(search.SlipNo) || search.SlipNo == null)
&& (x.BankDepositVoucher.ChequeNo.Contains(search.ChequeNo) || search.ChequeNo == null)
&& (x.BankDepositVoucher.Bank.AccountName.Contains(search.BankDetails)
|| search.BankDetails == null)
&& (x.BankDepositVoucher.AccountName.Contains(search.AccountName) || search.AccountName == null)
&& (x.BankDepositVoucher.Narration.Contains(search.Narration) || search.Narration == null)
&& (x.TotalAmount == search.Amount || search.Amount == null)
&& (x.Date >= search.StartDate || search.StartDate == null)
&& (x.Date <= search.EndDate || search.EndDate == null));
//model.Pager = new Pager(await Vouchers.CountAsync(), PageNo, 10);
model.Vouchers = await Vouchers.OrderByDescending(x => x.VoucherNumber)
//.Skip((model.Pager.CurrentPage - 1) * model.Pager.PageSize)
//.Take(model.Pager.PageSize)
.Select(x => new BankDepositVoucherBaseViewModel
{
Id = x.Id,
VoucherNumber = x.VoucherNumber,
AccountName = x.BankDepositVoucher.AccountName,
BankAccountName = x.BankDepositVoucher.Bank.AccountName,
Date = x.Date,
ChequeNo = x.BankDepositVoucher.ChequeNo,
Narration = x.BankDepositVoucher.Narration,
SlipNo = x.BankDepositVoucher.SlipNo,
TotalAmount = x.TotalAmount,
IsCleared = x.BankDepositVoucher.IsCleared
}).ToListAsync();
}
catch (Exception ex)
{
Console.WriteLine("", ex.Message);
}
return PartialView(model);
}
This is the part throwing above mentioned exception
model.Vouchers = await Vouchers.OrderByDescending(x => x.VoucherNumber)
//.Skip((model.Pager.CurrentPage - 1) * model.Pager.PageSize)
//.Take(model.Pager.PageSize)
.Select(x => new BankDepositVoucherBaseViewModel
{
Id = x.Id,
VoucherNumber = x.VoucherNumber,
AccountName = x.BankDepositVoucher.AccountName,
BankAccountName = x.BankDepositVoucher.Bank.AccountName,
Date = x.Date,
ChequeNo = x.BankDepositVoucher.ChequeNo,
Narration = x.BankDepositVoucher.Narration,
SlipNo = x.BankDepositVoucher.SlipNo,
TotalAmount = x.TotalAmount,
IsCleared = x.BankDepositVoucher.IsCleared
}).ToListAsync();
The issue is likely that when populating the view model it cannot deal with the fact that a record may not have a BankDepositVoucher.
For instance:
IsCleared = x.BankDepositVoucher.IsCleared
This should probably be:
IsCleared = x.BankDepositVoucher?.IsCleared ?? false
One other thing to improve performance considerably:
While it may look concise in the code to write statements like this:
.Where(x => x.Type == InvoiceType.BankDepositVoucher
&& (x.VoucherNumber == search.VoucherNo || search.VoucherNo == null)
&& (x.BankDepositVoucher.SlipNo.Contains(search.SlipNo) || search.SlipNo == null)
&& (x.BankDepositVoucher.ChequeNo.Contains(search.ChequeNo) || search.ChequeNo == null)
&& (x.BankDepositVoucher.Bank.AccountName.Contains(search.BankDetails)
|| search.BankDetails == null)
&& (x.BankDepositVoucher.AccountName.Contains(search.AccountName) || search.AccountName == null)
&& (x.BankDepositVoucher.Narration.Contains(search.Narration) || search.Narration == null)
&& (x.TotalAmount == search.Amount || search.Amount == null)
&& (x.Date >= search.StartDate || search.StartDate == null)
&& (x.Date <= search.EndDate || search.EndDate == null));
It is more efficient to write it out as:
.Where(x => x.Type == InvoiceType.BankDepositVoucher);
if(!string.IsNullOrEmpty(search.VoucherNo))
Voucher = Voucher.Where(x => x.VoucherNumber == search.VoucherNo);
if(!string.IsNullOrEmpty(search.SlipNo))
Voucher = Voucher.Where(x => x.BankDepositVoucher.SlipNo.Contains(search.SlipNo))
// etc.
The reason is that in the first case you are generating a much larger SQL statement to be sent to the database, and it is quite easy to "slip up" on conditions if that query is ever edited in the future. (missing parenthesis, etc.) The second example only adds conditions to the query if they are needed, keeping the resulting SQL statement much more compact.

LINQ using Contains with entity

Am using ASP.NET MVC 5 to create an Application for billing, now i have thing function which receive a filter object with different variables, am having a problem with contains when i search, what am i doing wrong
public static List<Quote> getCustomerQuotes(QuoteFilter filter)
{
using (var db = new AppDBContext())
{
var q = db.Quotes.Where(u => u.entryDate > 0); ;
if (filter.type != null)
{
q = q.Where(u => u.quoteType == filter.type);
}
if (filter.only_permitable != null)
{
q = q.Where(u => !Values.NON_PERMITABLE_QUOTES.Contains(u.quoteType));
}
if (filter.quote_status != null)
q = q.Where(u => u.quote_status == (int)filter.quote_status);
if (filter.quotenumber != null)
{
q = q.Where(u => u.quote_number.Contains(filter.quotenumber));
}
if (filter.permitnumber != null)
q = q.Where(u => u.permit_number.Contains(filter.permitnumber));
if (filter.permit_status != null)
q = q.Where(u => u.permit_status == (int)filter.permit_status);
if (filter.quoteId != null)
q = q.Where(u => u.Id == (int)filter.quoteId);
if (filter.customer_id != null)
q = q.Where(u => u.customer_id == (int)filter.customer_id);
q = q.OrderByDescending(u => u.Id);
FileLogger.Log("getCustomerQuotes", q.ToString());
return q.ToList();
}
}
When i call the function and pass quotenumber, the contains doesnt search, it returns nothing
You have to evaluate your expression, before you apply the OrderByDescending.
q = q.Where(u => u.quote_number.Contains(filter.quotenumber)).ToList();
This should be happen also to the rest places.
Is quote number alpha-numeric? If yes, as Contains is case sensitive can you try comparison by first turning source and target to same case ? like
q = q.Where(u => u.quote_number.ToLower().Contains(filter.quotenumber.ToLower()));
Cheers
Ok, am answering my own question after finding a solution or i may call it a hack
public static List<Quote> getCustomerQuotes(QuoteFilter filter)
{
using (var db = new AppDBContext())
{
var q = db.Quotes.Where(u =>
(filter.type != null ? u.quoteType == filter.type : u.quoteType > 0) &&
(filter.only_permitable != null ? !Values.NON_PERMITABLE_QUOTES.Contains(u.quoteType) : u.permitType > 0) &&
(filter.quote_status != null ? u.quote_status == filter.quote_status : u.quote_status > -100) &&
(!string.IsNullOrEmpty(filter.quotenumber) ? u.quote_number.Contains(filter.quotenumber) || u.groupName.Contains(filter.quotenumber) : u.quoteType > 0) &&
(!string.IsNullOrEmpty(filter.permitnumber) ? u.permit_number.Contains(filter.permitnumber) || u.groupName.Contains(filter.permitnumber) : u.quoteType > 0) &&
(filter.permit_status != null ? u.permit_status == filter.permit_status : u.quoteType > 0) &&
(filter.quoteId != null ? u.Id == filter.quoteId : u.Id > 0) &&
(filter.customer_id != null ? u.customer_id == filter.customer_id : u.customer_id > -1)
).OrderByDescending(u => u.Id);
//FileLogger.Log("getCustomerQuotes", q.ToString());
return q.ToList();
}
}
i dont know why it didn't work the first time but now it works.

C# use left outer join in LINQ

This is my method:
public IEnumerable<Web_Vendor> GetSaleBrands()
{
using (var _db = new LuxedecorContext())
{
IQueryable<Web_Vendor> web_vendors = _db.Web_Vendor.
Where(x =>
(_db.Web_Promotion.Where(z => z.VendorID != string.Empty
&& z.Static == true && z.Percent != 0 &&
(x.WebPromotion.Expires >= DateTime.Now || x.WebPromotion.Expires == null))
.Select(r => r.VendorID).Contains(x.VendorID))
||
(_db.NavItems.Where(y => x.WebPromotion.VendorID == y.SC1
&& !(y.Promotion == "" || y.Promotion == null)
&& (y.PromotionStart <= DateTime.Now) && (y.PromotionEnd >= DateTime.Now || y.PromotionEnd == null))
.Select(g => g.SC1).Contains(x.WebPromotion.VendorID)))
.Where(x => x.Active == true).OrderBy(x => x.NameExtra)
.ThenBy(x => x.Sequence);
var a = web_vendors.ToList(); // 16 values
var b = web_vendors.Include(x => x.WebPromotion).ToList(); // 13 values
}
throw new NotImplementedException();
}
I need to Include nullable values to my collection of Web_Vendors. I know, it's famous question, but I need to return collection of Web_Vendors instead of collection of dynamic types. Is it possible?

Handling null result

I have a db request that could return null:
Pony MyPony = db.Pony.Where(p => p.PonyOwnerId == user.UserId).First();
If there is no row in my db, there is an error message.
How to accept an empty query?
You can use FirstOrDefault
Pony myPony = db.Pony.Where(p => p.PonyOwnerId == user.UserId).FirstOrDefault();
if (myPony == null)
{
..
}
You can write:
Pony myPony = db.Pony.Where(p => p.PonyOwnerId == user.UserId).FirstOrDefault();
if( myPony != null ) {
// Do something
}
var MyPony = db.Pony.FirstOrDefault(p => p.PonyOwnerId != null && p.PonyOwnerId == user.UserId);
or
var MyPony = db.Pony.Where(p => p.PonyOwnerId != null && p.PonyOwnerId == user.UserId).FirstOrDefault();
or
if (db.Pony.FirstOrDefault(p => p.PonyOwnerId != null && p.PonyOwnerId == user.UserId) != null)
{
//Do stuff
}

Categories

Resources