Following situation:
Table of users
Table of addresses
The user has a single optional reference to the address table (=left join)
The query to fetch the data is:
IQueryable<User> query =
from u in _dbContext.Users
join a in _dbContext.Address on u.AddressId equals a.Id into address
from addresses in address.DefaultIfEmpty()
select new User(u, a);
Now I want to do a sorting on the query based on the municipality of the address
query = query.OrderBy(u => u.Address.Municipality);
The problem is that the Address can be a null value (as the address is optional) and for that reason it is throwing following exception.
NullReferenceException: Object reference not set to an instance of an object.
Is there a way to order on the municipality knowing that it can be null?
Already tried following things with same outcome:
query = query.OrderBy(u => u.Address.Municipality ?? "");
query = query.OrderBy(u => u.Address == null).ThenBy(u => u.Address.Municipality);
You can use
query = query.OrderBy(u => u.Address.Municipality.HasValue);
You can write your comparer like this:
public class One
{
public int A { get; set; }
}
public class Two
{
public string S { get; set; }
}
public class T
{
public One One { get; set; }
public Two Two { get; set; }
}
public class OrderComparer : IComparer<Two>
{
public int Compare(Two x, Two y)
{
if (((x == null) || (x.S == null)) && ((y == null) || (y.S == null)))
return 0;
if ((x == null) || (x.S == null))
return -1;
if ((y == null) || (y.S == null))
return 1;
return x.S.CompareTo(y.S);
}
}
static void Main(string[] args)
{
var collection = new List<T> {
new T{ One = new One{A=1}, Two = new Two{ S="dd" } },
new T{ One = new One{A=5}, Two = null },
new T{ One = new One{A=0}, Two = new Two{ S=null } },
new T{ One = new One{A=6}, Two = new Two{ S="aa" } },
};
var comparer = new OrderComparer();
collection = collection.OrderBy(e => e.Two, comparer).ToList();
}
But in your case you have to write var collection = query.AsEnumerable().OrderBy(x=>x.Address).
Also there is other method:
var result = query.Where(x=>x.Address==null || x.Address.Municipality==null)
.Union(query.Where(x.Address!=null && x.Address.Municipality!=null).OrderBy(x=>x.Address.Municipality));
I create a simple demo and it works well when I add nullable foreign key to the two tables.
Besides, I do not understabd what is select new User(u, a); in your code.
Below is my sample code:
Models:
public class User
{
public int Id { get; set; }
public string Name { get; set; }
[ForeignKey("Address")]
public int? AddressId { get; set; }
public Address Address { get; set; }
}
public class Address
{
public int Id { get; set; }
public string AddressName { get; set; }
public string Municipality { get; set; }
}
Action:
IQueryable<User> query =
from u in _context.Users
join a in _context.Address on u.AddressId equals a.Id into address
from addresses in address.DefaultIfEmpty()
select new User
{
Id = u.Id,
Name = u.Name,
AddressId = u.AddressId,
Address = addresses
};
query = query.OrderBy(u => u.Address.Municipality);
Related
I am having a class like this.
public class CameraModel
{
public int JobId { get; set; }
public int ViewId { get; set; }
public Guid ViewGuid { get; set; }
public string Name { get; set; }
public int ViewNum { get; set; }
public int LayoutID { get; set; }
public List<CameraViewItemModel> CameraViewItems { get; set; }
}
The CameraViewItemModel class is like this:
public class CameraViewItemModel
{
public int JobID { get; set; }
public Guid ViewGuid { get; set; }
public int ViewID { get; set; }
public int CamNum { get; set; }
public Guid ChannelGuid { get; set; }
public string Name { get; set; }
public ActionType Action { get; set; }
}
Now, I am assigning the list of CameraViewItemModel like this:
// get all the cameramodel's
cameraModels = _unitOfWork.Context.CameraViews.Where(m => m.JobId == siteId)
.Select(m => new CameraModel
{
JobId = m.JobId,
ViewId = m.ViewId,
ViewGuid = m.ViewGuid,
Name = m.Name,
ViewNum = m.ViewNum,
LayoutID = m.LayoutId
}).ToList();
// get all the cameraviewitemmodels
cameraViewItemModels =
(from cameraView in _unitOfWork.Repository<CameraViews>().Get(x => x.JobId == siteId).Result
join cameraViewItem in _unitOfWork.Repository<CameraViewItems>().Get(x => x.JobId == siteId)
.Result on cameraView.ViewId equals cameraViewItem.ViewId into CameraViewItemResults
from cameraViewItemResult in CameraViewItemResults.DefaultIfEmpty()
join cameraChannel in _unitOfWork.Repository<CameraChannels>().Get(x => x.JobId == siteId)
.Result on (cameraViewItemResult == null ? new Guid() : cameraViewItemResult.ChannelGuid) equals cameraChannel.ChannelGuid into CameraChannelResults
from cameraChannelResult in CameraChannelResults.DefaultIfEmpty()
select new CameraViewItemModel
{
JobID = cameraView.JobId,
ViewID = cameraView.ViewId,
ViewGuid = cameraView.ViewGuid,
CamNum = cameraViewItemResult.CamNum,
ChannelGuid = cameraChannelResult.ChannelGuid,
Name = cameraChannelResult.Name
}).ToList();
// then do a 'join' on JobId, ViewId and ViewGuid and assign the list of cameraviewitemmodels to cameraModels.
foreach (var cameraModel in cameraModels)
{
cameraModel.CameraViewItems = (from cameraViewItem in cameraViewItemModels
where cameraModel.JobId == cameraViewItem.JobID
&& cameraModel.ViewId == cameraViewItem.ViewID
&& cameraModel.ViewGuid == cameraViewItem.ViewGuid
select cameraViewItem).ToList();
}
return cameraModels;
There are three tables in database:
CameraViews, CameraViewItems, CameraChannels.
CameraViews is the main table. It is left joined with CameraViewItems and CameraChannels to get the desired result. There may not be any data in CameraViewItems and CameraChannels for a corresponding CameraView.
Is it possible to assign the list of CameraViewItemModels to CameraModels in a single linq statement.
Here is a simple way to add values to a sub list, dunno if this is what you mean. You can keep selecting sub lists if that is necessary.
var parent_lst = new List<List<string>>(); // Root/parent list that contains the other lists
var sub_lst = new List<string>(); // Sub list with values
var selected_parent_lst = parent_lst[0]; // Here I select sub list, in this case by list index
selected_parent_lst.Add("My new value"); // And here I add the new value
i have table looks like below
ID | Reason | PrID
-----------------
1 abc null
2 dhe null
3 aerc 1
4 dwes 2
5 adfje 1
i have class
public class Reason
{
public int Id { get; set; }
public string Reson{ get; set; }
public List<SecondryReason> SecReason{ get; set; }
public int? PrimaryId { get; set; }
}
public class SecondryReason
{
public int Id { get; set; }
public string Reason { get; set; }
public int PrimaryReasonId { get; set; }
}
I want this to be displayed in hierarchy level
if the prid is Null need to treat this as the parent remaining all child
i am trying Linq and unable to achieve this
Suggest me how to do this in an easy way in linq
So: You have a list/enumerable of type , whereof the SecReason List property is null. Then, using linq you want a list, were the only the "root" reasons remain, but the Sub-reasons got put in the lists, but as type SecondaryReason?
If so, I found this way to do it (linq and foreach):
static IEnumerable<Reason> GetReasonsGrouped(List<Reason> reasons)
{
var result = reasons.Where(x => x.PrimaryId == null);
foreach (var item in result)
{
item.SecReason = reasons.Where(x => x.PrimaryId == item.Id)
.Select(x => new SecondryReason()
{ Id = x.Id,
ReasonName = x.ReasonName,
PrimaryReasonId = item.Id
})
.ToList();
}
return result;
}
Or just linq, but harder to read:
var result = reasons.Where(x => x.PrimaryId == null)
.Select(x =>
{
x.SecReason = reasons.Where(r => x.PrimaryId == x.Id)
.Select(r => new SecondryReason()
{
Id = r.Id,
ReasonName = x.ReasonName,
PrimaryReasonId = x.Id
})
.ToList();
return x;
});
Not sure if linq will be the best solution, here is my proposed changes and method to get an Hierarchy type:
public class Reason
{
public int Id { get; set; }
public string Reson { get; set; }
public List<Reason> SecReason { get; set; }
public int? PrimaryId { get; set; }
//Adds child to this reason object or any of its children/grandchildren/... identified by primaryId
public bool addChild(int primaryId, Reason newChildNode)
{
if (Id.Equals(primaryId))
{
addChild(newChildNode);
return true;
}
else
{
if (SecReason != null)
{
foreach (Reason child in SecReason)
{
if (child.addChild(primaryId, newChildNode))
return true;
}
}
}
return false;
}
public void addChild(Reason child)
{
if (SecReason == null) SecReason = new List<Reason>();
SecReason.Add(child);
}
}
private List<Reason> GetReasonsHierarchy(List<Reason> reasons)
{
List<Reason> reasonsHierarchy = new List<Reason>();
foreach (Reason r in reasons)
{
bool parentFound = false;
if (r.PrimaryId != null)
{
foreach (Reason parent in reasonsHierarchy)
{
parentFound = parent.addChild(r.PrimaryId.Value, r);
if (parentFound) break;
}
}
if (!parentFound) reasonsHierarchy.Add(r);
}
return reasonsHierarchy;
}
I have the below two classes:
public class FirstInner
{
public int Id { get; set; }
public string Type { get; set; }
public string RoleId { get; set; }
}
public class SecondInner
{
public int Id { get; set; }
public string Type { get; set; }
}
Again, there are lists of those types inside the below two classes:
public class FirstOuter
{
public int Id { get; set; }
public string Name { get; set; }
public string Title { get; set; }
public List<FirstInner> Inners { get; set; }
}
public class SecondOuter
{
public int Id { get; set; }
public string Name { get; set; }
public List<SecondInner> Inners { get; set; }
}
Now, I have list of FirstOuter and SecondOuter. I need to check if FirstOuter list is a subset of SecondOuter list.
Please note:
The names of the classes cannot be changed as they are from different systems.
Some additional properties are present in FirstOuter but not in SecondOuter. When comparing subset, we can ignore their presence in SecondOuter.
No.2 is true for FirstInner and SecondInner as well.
List items can be in any order---FirstOuterList[1] could be found in SecondOuterList[3], based on Id, but inside that again need to compare that FirstOuterList[1].FirstInner[3], could be found in SecondOuterList[3].SecondInner[2], based on Id.
I tried Intersect, but that is failing as the property names are mismatching. Another solution I have is doing the crude for each iteration, which I want to avoid.
Should I convert the SecondOuter list to FirstOuter list, ignoring the additional properties?
Basically, here is a test data:
var firstInnerList = new List<FirstInner>();
firstInnerList.Add(new FirstInner
{
Id = 1,
Type = "xx",
RoleId = "5"
});
var secondInnerList = new List<SecondInner>();
secondInner.Add(new SecondInner
{
Id = 1,
Type = "xx"
});
var firstOuter = new FirstOuter
{
Id = 1,
Name = "John",
Title = "Cena",
Inners = firstInnerList
}
var secondOuter = new SecondOuter
{
Id = 1,
Name = "John",
Inners = secondInnerList,
}
var firstOuterList = new List<FirstOuter> { firstOuter };
var secondOuterList = new List<SecondOuter> { secondOuter };
Need to check if firstOuterList is part of secondOuterList (ignoring the additional properties).
So the foreach way that I have is:
foreach (var item in firstOuterList)
{
var secondItem = secondOuterList.Find(so => so.Id == item.Id);
//if secondItem is null->throw exception
if (item.Name == secondItem.Name)
{
foreach (var firstInnerItem in item.Inners)
{
var secondInnerItem = secondItem.Inners.Find(sI => sI.Id == firstInnerItem.Id);
//if secondInnerItem is null,throw exception
if (firstInnerItem.Type != secondInnerItem.Type)
{
//throw exception
}
}
}
else
{
//throw exception
}
}
//move with normal flow
Please let me know if there is any better approach.
First, do the join of firstOuterList and secondOuterList
bool isSubset = false;
var firstOuterList = new List<FirstOuter> { firstOuter };
var secondOuterList = new List<SecondOuter> { secondOuter };
var jointOuterList = firstOuterList.Join(
secondOuterList,
p => new { p.Id, p.Name },
m => new { m.Id, m.Name },
(p, m) => new { FOuterList = p, SOuterList = m }
);
if(jointOuterList.Count != firstOuterList.Count)
{
isSubset = false;
return;
}
foreach(var item in jointOuterList)
{
var jointInnerList = item.firstInnerList.Join(
item.firstInnerList,
p => new { p.Id, p.Type },
m => new { m.Id, m.type },
(p, m) => p.Id
);
if(jointInnerList.Count != item.firstInnerList.Count)
{
isSubset = false;
return;
}
}
Note: I am assuming Id is unique in its outer lists. It means there will not be multiple entries with same id in a list. If no, then we need to use group by in above query
I think to break the question down..
We have two sets of Ids, the Inners and the Outers.
We have two instances of those sets, the Firsts and the Seconds.
We want Second's inner Ids to be a subset of First's inner Ids.
We want Second's outer Ids to be a subset of First's outer Ids.
If that's the case, these are a couple of working test cases:
[TestMethod]
public void ICanSeeWhenInnerAndOuterCollectionsAreSubsets()
{
HashSet<int> firstInnerIds = new HashSet<int>(GetFirstOuterList().SelectMany(outer => outer.Inners.Select(inner => inner.Id)).Distinct());
HashSet<int> firstOuterIds = new HashSet<int>(GetFirstOuterList().Select(outer => outer.Id).Distinct());
HashSet<int> secondInnerIds = new HashSet<int>(GetSecondOuterList().SelectMany(outer => outer.Inners.Select(inner => inner.Id)).Distinct());
HashSet<int> secondOuterIds = new HashSet<int>(GetSecondOuterList().Select(outer => outer.Id).Distinct());
bool isInnerSubset = secondInnerIds.IsSubsetOf(firstInnerIds);
bool isOuterSubset = secondOuterIds.IsSubsetOf(firstOuterIds);
Assert.IsTrue(isInnerSubset);
Assert.IsTrue(isOuterSubset);
}
[TestMethod]
public void ICanSeeWhenInnerAndOuterCollectionsAreNotSubsets()
{
HashSet<int> firstInnerIds = new HashSet<int>(GetFirstOuterList().SelectMany(outer => outer.Inners.Select(inner => inner.Id)).Distinct());
HashSet<int> firstOuterIds = new HashSet<int>(GetFirstOuterList().Select(outer => outer.Id).Distinct());
HashSet<int> secondInnerIds = new HashSet<int>(GetSecondOuterList().SelectMany(outer => outer.Inners.Select(inner => inner.Id)).Distinct());
HashSet<int> secondOuterIds = new HashSet<int>(GetSecondOuterList().Select(outer => outer.Id).Distinct());
firstInnerIds.Clear();
firstInnerIds.Add(5);
firstOuterIds.Clear();
firstOuterIds.Add(5);
bool isInnerSubset = secondInnerIds.IsSubsetOf(firstInnerIds);
bool isOuterSubset = secondOuterIds.IsSubsetOf(firstOuterIds);
Assert.IsFalse(isInnerSubset);
Assert.IsFalse(isOuterSubset);
}
private List<FirstOuter> GetFirstOuterList() { ... }
private List<SecondOuter> GetSecondOuterList() { ... }
i have tre tables T020_CLIENTI,T021_SITI,T520_REL_STRUMENTI_SITI that i would join and then distinct by T020.Ragione_sociale,T520.DA_DATA,T520.A_DATA but obtain as return parameters T020.Ragione_sociale,T020.id_cliente,T520.cod_stumento,T520.DA_DATA,T520.A_DATA
my tables are
public partial class T020_CLIENTI
{
public decimal ID_CLIENTE { get; set; }
public Nullable<decimal> ID_COMUNE { get; set; }
public Nullable<decimal> ID_CONSORZIO { get; set; }
public string COD_LINEA_ATTIVITA { get; set; }
}
public partial class T021_SITI
{
public decimal ID_SITO { get; set; }
public Nullable<decimal> ID_FORNITORE { get; set; }
public Nullable<decimal> ID_CLIENTE { get; set; }
}
public partial class T520_REL_STRUMENTI_SITI
{
public string COD_STUMENTO { get; set; }
public decimal ID_SITO { get; set; }
public System.DateTime DA_DATA { get; set; }
public System.DateTime A_DATA { get; set; }
}
my linq query is
using (var cont = DALProvider.CreateEntityContext())
{
var query =
from cliente in cont.T020_CLIENTI
from sito
in cont.T021_SITI
.Where(s => s.ID_CLIENTE == cliente.ID_CLIENTE)
.DefaultIfEmpty()
from relStrumenti
in cont.T520_REL_STRUMENTI_SITI
.Where(s => s.ID_SITO == sito.ID_SITO)
.DefaultIfEmpty()
select new
{
clienteRec = cliente,
sitoRec = sito,
relStrumentiRec = relStrumenti
};
if (!string.IsNullOrEmpty(aiFiltro.RAGIONE_SOCIALE))
query = query.Where(i => i.clienteRec.RAGIONE_SOCIALE.ToUpper().Contains(aiFiltro.RAGIONE_SOCIALE.ToUpper()));
var vRes = (from clienteDef in query
select new ClienteFiltrato
{
RAGIONE_SOCIALE = clienteDef.clienteRec.RAGIONE_SOCIALE,
ID_CLIENTE = clienteDef.clienteRec.ID_CLIENTE,
COD_STRUMENTO = clienteDef.relStrumentiRec.COD_STUMENTO,
DATA_DA = clienteDef.relStrumentiRec.DA_DATA,
DATA_A = clienteDef.relStrumentiRec.A_DATA
}) ;
return vRes.AsQueryable();
}
but in my linq query i don't know where i can insert distinct and input parameter (:pPOD) to obtain my linq that in oracle query is:
SELECT DISTINCT t020.ragione_sociale,
da_data,
a_data,
t020.id_Cliente,
:pPOD
FROM t020_clienti t020, t021_siti t021, T520_REL_STRUMENTI_SITI t520
WHERE t020.id_cliente = t021.id_cliente
AND t021.id_sito = t520.id_sito
AND (:pPOD is null or t520.cod_stumento = :pPOD)
ORDER BY da_data
where :pPOD is an input parameter that i could have set or not.
Try to add (s.COD_STUMENTO == pPod || pPod == null) to your Where clause, where you are filtering T520_REL_STRUMENTI_SITI entity. pPod should be a string variable.
Please have in mind that if you are using DefaultIfEmpty() in LINQ this will be translated to left join in SQL.
Modified query follows:
string pPod = null;
using (var cont = DALProvider.CreateEntityContext())
{
var query =
(from cliente in cont.T020_CLIENTI
from sito
in cont.T021_SITI
.Where(s => s.ID_CLIENTE == cliente.ID_CLIENTE)
.DefaultIfEmpty()
from relStrumenti
in cont.T520_REL_STRUMENTI_SITI
.Where(s => s.ID_SITO == sito.ID_SITO && (s.COD_STUMENTO == pPod || pPod == null))
.DefaultIfEmpty()
select new
{
clienteRec = cliente.Distinct(),
sitoRec = sito,
relStrumentiRec = relStrumenti
});
if (!string.IsNullOrEmpty(aiFiltro.RAGIONE_SOCIALE))
query = query.Where(i => i.clienteRec.RAGIONE_SOCIALE.ToUpper().Contains(aiFiltro.RAGIONE_SOCIALE.ToUpper()));
var vRes = (from clienteDef in query
select new ClienteFiltrato
{
RAGIONE_SOCIALE = clienteDef.clienteRec.RAGIONE_SOCIALE,
ID_CLIENTE = clienteDef.clienteRec.ID_CLIENTE,
COD_STRUMENTO = clienteDef.relStrumentiRec.COD_STUMENTO,
DATA_DA = clienteDef.relStrumentiRec.DA_DATA,
DATA_A = clienteDef.relStrumentiRec.A_DATA
}).Distinct() ;
return vRes.AsQueryable();
}
You can use:
string query =
((System.Data.Objects.ObjectQuery)query).ToTraceString();
This will show you the generated SQL from LINQ Queryable object.
I have following LINQ query:
var LINQFilter = (from Cash in _DataTable_Cash.AsEnumerable()
join CashOpeningsAssignments in _DataTable_CashOpeningsAssignments.AsEnumerable().Where(a => (a.Field<Int32>("cashopeningassignmentstatus_id") == 1 || a.Field<Int32>("cashopeningassignmentstatus_id") == 2))
on Cash.Field<Int32>("cash_id") equals CashOpeningsAssignments.Field<Int32>("cash_id") into into_cashopeningsassignments
from CashOpeningsAssignments in into_cashopeningsassignments.DefaultIfEmpty()
join Users in _DataTable_Users.AsEnumerable()
on CashOpeningsAssignments.Field<Int32>("user_id") equals Users.Field<Int32>("user_id") into into_users
from Users in into_users.DefaultIfEmpty()
select new
{
cash_id = Cash.Field<Int32>("cash_id"),
cellar_name = Cellars.Field<String>("cellar_name"),
cash_name = Cash.Field<String>("cash_name"),
cashstatus_name = CashStatus.Field<String>("cashstatus_name"),
user_name = (Users == null ? "[No Data]" : Users.Field<String>("user_firstname") + (Char)32 + Users.Field<String>("user_lastname")),
cashtransaction_amount = (Cash.Field<Int32>("cashstatus_id") == 2 ? 0.00 : 150.00)
});
I have problems showing the result because this Field returns null: CashOpeningsAssignments.Field<Int32>("user_id") when CashOpeningsAssignments is Empty.
I tried moving the .DefaultIfEmpty() into users but still not working, Any idea how i can solve this?
Answer
Use the overload of DefaultIfEmpty to create an empty item.
E.g.
into_cashopeningsassignments
.DefaultIfEmpty(new CashOpeningsAssignments())
Running Code
The code reflects what you're trying to do, even though I took some liberties with it. For instance, I used List<T> instead of DataTable, because I did not figure out how to use Field<T>(string name) in a DotNetFiddle.
It's live here: https://dotnetfiddle.net/YaAc6D
using System;
using System.Linq;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
var query =
from Cash
in _DataTable_Cash.AsEnumerable()
join CashOpeningsAssignments
in _DataTable_CashOpeningsAssignments.AsEnumerable()
.Where(a =>
(a.cashopeningassignmentstatus_id == 1 ||
a.cashopeningassignmentstatus_id == 2))
on Cash.cash_id
equals CashOpeningsAssignments.cash_id
into into_cashopeningsassignments
from CashOpeningsAssignments
in into_cashopeningsassignments.DefaultIfEmpty(new CashOpeningsAssignments())
join Users
in _DataTable_Users.AsEnumerable()
on CashOpeningsAssignments.user_id
equals Users.user_id
into into_users
from Users
in into_users.DefaultIfEmpty()
select new
{
cash_id = Cash.cash_id,
// cellar_name = Cellars.cellar_name,
cash_name = Cash.cash_name,
// cashstatus_name = CashStatus.cashstatus_name,
user_name = (Users == null ? "[No Data]" : Users.user_firstname + (Char)32 + Users.user_lastname),
cashtransaction_amount = (Cash.cashstatus_id == 2 ? 0.00 : 150.00)
};
foreach(var result in query)
{
Console.WriteLine(result);
}
}
public static List<Cash> _DataTable_Cash =
new List<Cash> { new Cash() };
public static List<Cellars> _DataTable_Cellars =
new List<Cellars> { new Cellars() };
public static List<CashStatus> _DataTable_CashStatus =
new List<CashStatus> { new CashStatus() };
public static List<CashOpeningsAssignments> _DataTable_CashOpeningsAssignments =
new List<CashOpeningsAssignments> { };
public static List<Users> _DataTable_Users =
new List<Users>() { new Users() };
}
public class Cash
{
public int cash_id { get; set; }
public string cash_name { get; set; }
public int cellar_id { get; set; }
public int cashstatus_id { get; set; }
}
public class Cellars
{
public string cellar_name { get; set; }
public int cellar_id { get; set; }
}
public class CashStatus
{
public int cashstatus_id { get; set; }
public string cashstatus_name { get; set; }
}
public class CashOpeningsAssignments
{
public int user_id { get; set; }
public int cash_id { get; set; }
public int cashopeningassignmentstatus_id { get; set; }
}
public class Users
{
public string user_firstname { get; set; }
public string user_lastname { get; set; }
public int user_id { get; set; }
}
See Also
https://msdn.microsoft.com/en-us/library/vstudio/bb355419%28v=vs.100%29.aspx