Query Collection using composite key where key parts can match 'any' - c#

Is there an appropriate collection or algorithm that would allow me to get a value using a composite key where when querying parts of the key could be null to mean match any value?
For example, if I have the class:
class Key
{
string p1{ get; }
string p2{ get; }
string p3{ get; }
public Key(string p1, string p2 , string p3)
{ this.p1 = p1; this.p2 = p2; this.p3=p3; }
}
If I then created three keys e.g.
new Key( "a","b","c")
new Key( "d","b","c")
new Key( "e","f","c")
I would like a collection or algorithm with out iterating to allow for the following key
new Key( null, "b","c") to return the values mapped to the first two keys,
new key( null,null,"c") to return the values mapped to all of the keys.
Is there any way to do this?

Probably this would do for lookup by any combination of three key components. Note that key for pair lookup (A+B) is created by simple concat for simplicity. Real key should be Tuple.
var keys = new[] { new Key("a", "b", c"), ... };
class Map
{
// ... skip members declaration here
public Map(IEnumerable<Keys> keys)
{
all = keys;
mapA = keys.ToLookup(k => k.A);
mapB = keys.ToLookup(k => k.B);
mapC = keys.ToLookup(k => k.C);
// should be keys.ToLookup(k => Tuple.Create(k.A, k.B))
mapAB = keys.ToLookup(k => k.A + k.B);
mapAC = keys.ToLookup(k => k.A + k.C);
mapBC = keys.ToLookup(k => k.B + k.C);
mapABC = keys.ToLookup(k => k.A + k.B + k.C);
}
public IEnumerable<Key> Find(Key k)
{
if(k.A == null && k.B == null && k.C == null) return all;
if(k.A != null && k.B == null && k.C == null) return mapA[k.A];
if(k.A == null && k.B != null && k.C == null) return mapB[k.B];
if(k.A == null && k.B == null && k.C != null) return mapC[k.C];
if(k.A != null && k.B != null && k.C == null) return mapAB[k.A+k.B];
if(k.A != null && k.B == null && k.C != null) return mapAC[k.A+k.C];
if(k.A == null && k.B != null && k.C != null) return mapBC[k.B+k.C];
return mapABC[k.A+k.B+k.C];
}
}

Related

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.

Dynamic EF Where Clause raising ArgumentNullException

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.

How to write below sql query in linq c# where some of paramteres will be null sometimes

I have following query in sql,
select * from dbo.WaitingLists
where WaitingListTypeId in (1)
or StakeBuyInId in (Select StakeBuyInId from dbo.WaitingLists where StakeBuyInId in (5) and
WaitingListTypeId = 2)
in this, sometimes StakeBuyInId will be null or WaitingListTypeId will ne null. I want to perform this query via linq c# in following code.
public GameListItem[] GetMyWaitingList(Guid UserId, int LocalWaitingListTypeId, int GlobalWaitingListTypeId, int[] StakeBuyInIds)
{
ProviderDB db = new ProviderDB();
List<GameListItem> objtempGameListItem = new List<GameListItem>();
List<GameTables> objGameTablesList = new List<GameTables>();
var objWaitingListUser = db.WaitingLists.Where(x => x.UserId.Equals(UserId));
if (LocalWaitingListTypeId > 0 || (GlobalWaitingListTypeId > 0 && StakeBuyInIds != null))
{
objWaitingListUser = objWaitingListUser.Where(x => x.WaitingListTypeId == LocalWaitingListTypeId || (x.WaitingListTypeId == GlobalWaitingListTypeId
&& StakeBuyInIds != null ? StakeBuyInIds.Contains((Int32)x.StakeBuyInId) : true)
);
}
return objtempGameListItem.ToArray();
}
Here StakeBuyInIds int[] will be sometimes null, then how will i perform linq operation for above sql query. Thanks for any help.
You could probably just check for null outside of your expression, like this:
if (LocalWaitingListTypeId > 0 || (GlobalWaitingListTypeId > 0 && StakeBuyInIds != null))
{
if (StakeBuyInIds != null)
{
objWaitingListUser = objWaitingListUser.Where(
x => x.WaitingListTypeId == LocalWaitingListTypeId ||
(x.WaitingListTypeId == GlobalWaitingListTypeId &&
StakeBuyInIds.Contains((Int32)x.StakeBuyInId));
} else {
objWaitingListUser = objWaitingListUser.Where(
x => x.WaitingListTypeId == LocalWaitingListTypeId ||
x.WaitingListTypeId == GlobalWaitingListTypeId);
}
}
You might also be able to do this:
if (LocalWaitingListTypeId > 0 || (GlobalWaitingListTypeId > 0 && StakeBuyInIds != null))
{
var arrayNull = StakeBuyInIds != null;
var array = StakeBuyInIds ?? new int[0];
objWaitingListUser = objWaitingListUser.Where(
x => x.WaitingListTypeId == LocalWaitingListTypeId ||
(x.WaitingListTypeId == GlobalWaitingListTypeId &&
(arrayNotNull || array.Contains((Int32)x.StakeBuyInId)));
}
It effect it tests for null outside of the query, but ensures that it cannot be null when actually executing the query.
The waitingListTypeId and stakeBuyinId should be nullable int in your relational object WaitingList.
List<int?> WaitingListTypeIds=new List(new int?[]{1});
var StakeBuyInIds=from w in WaitingListsCollection where new List<int?>(new int?[]{5}).Contains(w.StakeBuyInId) && w.WaitingListTypeId = 2;
var output= from w in WaitingListsCollection where WaitingListTypeIds.Contains(w.WaitingListTypeId) || StakeBuyInIds.Contains(w.StakebuyInId)

Replacing several nodes in the same tree, using SyntaxNode.ReplaceNode

I'm currently working on three-way merging on syntax trees using Roslyn. I have a matching between all children on a a ClassDeclerationSyntax node, and want to perform a merge on the children, and then create a new tree based on that merge.
O is the input ClassDeclerationSyntax, and matching has three members (A, O, B) of the type MemberDeclerationSyntax.
var updated = O;
foreach (var m in matching)
{
if (m.A != null && m.B != null && m.O != null) {
var merge = Merge(m.A, m.O, m.B);
var oldUpdated = updated;
updated = updated.ReplaceNode(m.O, merge);
}
else if (m.A == null && m.O == null && m.B != null)
updated = updated.AddMembers(m.B);
else if (m.A != null && m.O == null && m.B == null)
updated = updated.AddMembers(m.A);
}
This does not work. In the second iteration ReplaceNode returns a completely unmodified node (oldUpdated == updated is true).
It seems that after the first iteration of the loop, all children have been reconstructed as new objects, and the original children-objects stored in my matching can no longer be found in the children list (updated.ChildNodes().Where(x => x == m.O) is empty).
What would a good way be to do this?
My current approach:
var updateMember = new Dictionary<MemberDeclarationSyntax, MemberDeclarationSyntax>();
var addMembers = new List<MemberDeclarationSyntax>();
foreach (var m in matching) {
if (m.A != null && m.B != null && m.O != null) {
var mergeChild = Merge(m.A, m.B, M.O);
updateMember.Add(m.O, child);
}
else if (m.A == null && m.O == null && m.B != null)
addMembers.Add(m.B);
else if (m.A != null && m.O == null && m.B == null)
addMembers.Add(m.A);
}
var merged = O.ReplaceNodes(updateMember.Keys.AsEnumerable(), (n1, n2) =>
{
return updateMember[n1];
}).AddMembers(addMembers.ToArray());

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