I have these classes and got issue with auto increase id of sub lists:
class A
{
public int Id { get; set; }
public string Value { get; set; }
public List<B> Bs { get; set; }
}
class B
{
public int Id { get; set; }
public string Value { get; set; }
public List<C> Cs { get; set; }
}
class C
{
public int Id { get; set; }
public string Value { get; set; }
}
How can I automatically increase the id of the B and C? I can add values but id inside the sub list they're always 0.
In general, an A item can contain B with the same id, as well as different B items can contain C with the same id.
If you just need different ids, you can assign them right in the code just like other properties.
If it is required to support consistency and assign a new id to every sub-item, the items should be stored in separate collections:
var collectionA = db.GetCollection<A>("a");
var collectionB = db.GetCollection<B>("b");
var collectionC = db.GetCollection<C>("c");
BsonMapper.Global.Entity<A>().DbRef(x => x.Bs, "b");
BsonMapper.Global.Entity<B>().DbRef(x => x.Cs, "c");
foreach (var c in a.Bs.SelectMany(x => x.Cs).Where(x => x.Id == 0))
collectionC.Insert(c);
foreach (var b in a.Bs.Where(x => x.Id == 0))
collectionB.Insert(b);
collectionA.Insert(a);
More details about sub-documents are available in the documentation
Related
Here it is just an example. Suppose we have class Grouped (Items are gruoped by group name) as given below:
public class Grouped
{
public int Id { get; set; }
public string GroupName { get; set; }
public List<Item> Items{ get; set; }
}
public class Item
{
public string ItemName { get; set; }
}
What i need to do is to make a list of Normal Class from the above Grouped Object using only linq c# but not using ForEach or ForLoop.
pubic class Normal
{
public int Id { get; set;}
public string GroupName { get; set;}
public string ItemName { get; set;}
}
here is an example of list
Id
Name
Item
1
A
X
1
A
Y
2
B
Y
3
C
X
3
C
Y
3
C
Z
If you have a collection of Grouped (and you should, based on the desired output, otherwise there is no source for multiple Ids) you can use SelectMany to flatten a nested collection. Something along this lines:
IEnumerable<Grouped> grouped = ...;
var result = grouped
.SelectMany(g => g.Items.Select(i => new Normal
{
Id = g.Id,
GroupName = g.GroupName,
ItemName = i.ItemName
}))
.ToList();
I have more of a special case, and I am not sure what the reason is for what my issue is happening.
I have a class A with nested class B which also has a nested class C
public class A
{
[Key]
public int Id { get; set; }
public int Name { get; set; }
public List<B> Bs{ get; set; }
}
public class B
{
[Key]
public int Id { get; set; }
public int Name { get; set; }
public List<C> Cs{ get; set; }
[ForeignKey(nameof(A))]
public int AId{ get; set; }
}
public class C
{
[Key]
public int Id { get; set; }
public int Name { get; set; }
[ForeignKey(nameof(B))]
public int BId{ get; set; }
}
Now the problem that I have is that the values of class C aren't included into class B when class A is called alongside with Include(), respectively ThenInclude() methods.
The way values are brought from DB Tables is using a normal SQL Query combined with LINQ.
var query = from queryResult in _dbSet.Include(a => a.B).ThenInclude(B => b.C)
select new ResultModel()
{
Name = queryResult.Name
B = queryResult.B
}
The normal properties of B are being brought from db and mapped automatically to it, but the values for the nested List property aren't for some reason.
Do I need to make additional changes to select query in order for values of nested class C property be mapped into class B?
I hope the question was not too confusing.
The problem I had was the property being ignored because of not being included in the select. So the solution would be to change from
var query = from queryResult in _dbSet.Include(a => a.B).ThenInclude(B => b.C)
select new ResultModel()
{
Name = queryResult.Name
B = queryResult.B
}
to
var query = from queryResult in _dbSet.Include(a => a.B).ThenInclude(B => b.C)
select new ResultModel()
{
Name = queryResult.Name
B = queryResult.B.Select(x =>
new C {
Id = x.Id,
Name = x.Name,
}).ToList()
You can read more about this issue here.
Let's say i have 4 Tables A,B,C and D
What is the best way to get list of B to be in a dropdown list.
Condition is that each record should have associated list of D1
so that if B has no D1 records i should not show it in the dropdown list
D1 is a child class of D
How i did it:
// number 2 to specify the selected A Table normally it's a variable
var b_Per_A = Uow.B.GetAll().Where(x => x.A_Id == 2).ToList();
var b_list = new List<B>();
foreach (var b in b_list)
{
var d =
Uow.D1.GetAll().Where(x => x.C.B_Id == b.Id);
if (!d.IsNullOrEmpty()) // extension method that checks if a collection is null or empty
{
b_list.Add(b);
}
}
It works but it's slow
UPDATE:
The signature of GetAll() is
IQueryable<T> GetAll();
UPDATE 2:
My Model is
public class A
{
public int Id { get; set; }
public string Name { get; set; }
public IEnumerable<B> ChildBs { get; set; }
}
public class B
{
public int Id { get; set; }
public string Name { get; set; }
public A ParentA { get; set; }
public IEnumerable<C> ChildCs { get; set; }
}
public class C
{
public int Id { get; set; }
public string Name { get; set; }
public B ParentB { get; set; }
public IEnumerable<D> ChildDs { get; set; }
}
public class D
{
public int Id { get; set; }
public string Name { get; set; }
public C ParentC { get; set; }
}
public class D1 : D
{
/// other properties
}
Given that you've confirmed you have navigation properties on your entity classes, this could be achieved in a single expression:
var result = Uow.B.GetAll()
.Where(b =>
b.ParentA.Id == 2 &&
b.ChildCs.Any() &&
b.ChildCs.SelectMany(c => c.ChildDs).Any())
.ToList();
This will push the filtering to your database rather than doing it in memory and should be a lot more efficient.
Also, I'm assuming that a parent navigation property will never be null and that child collections are always initialized.
Example here
put your var d = Uow.D.GetAll().Where(x => x.C.B_Id == b.Id); outside of your foreach. I also recommend you to use IEnumerable/ICollection instead of List and use for loop instead of foreach. Indexed looping is faster.
Basically I have this class
public class Gasto
{
public int IdTienda { get; set; }
public int IdGasto { get; set; }
public System.DateTime Fecha { get; set; }
public string ConceptoDeGasto { get; set; }
public double Total { get; set; }
public string TipoDeGasto { get; set; }
public Nullable<int> IdVenta { get; set; }
public Nullable<System.DateTime> FechaVenta { get; set; }
public virtual Tienda Tienda { get; set; }
}
And I'm trying to build a ViewModelClass like this
public class CorteConVentas
{
// STILL NO ATRIBUTE -- THIS IS THE QUESTION
}
Here is the code for the controller where I will build a List of Gasto grouped by TipoDeGasto
var gastos = db.Gastos.Where(g => g.IdGasto >= corte.DesdeIdGasto && g.IdGasto <= corte.HastaIdGasto).ToList();
var GD = gastos.GroupBy(u => u.TipoDeGasto).Select(grp => new { TipoGasto = grp.Key, gastos = grp.ToList() } ).ToList();
As you can see the variable "GD" is a List of Strings (TipoGasto) with List of Gasto.
¿The issue (question) is this GD how can I declare it as an attribute of my viewModelClass?
I tried something like this for the ViewModel
public class CorteConVentas
{
public List<string, List<Gasto>> listaGastosAgrupada { get; set; }
}
But there is something wrong. The output of the error says:
Using the generic type List requires 1 type arguments
Here is the output after grouping by
Finally the solution as #Ziv Weissman said was not to use an anonymous type
So I created a class like this
public class CorteConVentas
{
public List<GastosAgrupados> listaGastosAgrupada { get; set; }
}
public class GastosAgrupados
{
public string TipoGasto { get; set; }
public List<Gasto> gastos { get; set;}
}
And then in the controller when creating the grouped list I did this
var gastos = db.Gastos.Where(g => g.IdGasto >= corte.DesdeIdGasto && g.IdGasto <= corte.HastaIdGasto).ToList();
var gd = gastos.GroupBy(u => u.TipoDeGasto).Select(grp => new GastosAgrupados { TipoGasto = grp.Key, gastos = grp.ToList()) } ).ToList();
Thanks to all for helping me.
You cannot declare a variable of anonymous type:
.Select(grp => new { TipoGasto = grp.Key, gastos = grp.ToList() } )
You must create another class which has these two props.
(or use a KeyValuePair)
Something like -
.Select(grp => new KeyValuePair<string,List<Gasto>> { Key = grp.Key, Value = grp.ToList() } )
Then you can create a strong typed prop.
public class CorteConVentas
{
List<KeyValuePair<string,List<Gasto>>> PropName {get; set;}
}
Simple answer, just look up GroupBy() in the docs, and see what it returns:
IEnumerable<IGrouping<TKey, TElement>>
which is your case would be:
public class CorteConVentas
{
public IEnumerable<IGrouping<string, Gasto>> listaGastosAgrupada { get; set; }
}
Update:
Didn't notice the select after the GroupBy(). Try this:
public class GrupoGastos // forgive my attempts at Spanish via Google Translate
{
public string TipoGasto {get; set;}
public List<Gasto> Gastos {get; set;}
}
then
var GD = gastos.GroupBy(u => u.TipoDeGasto)
.Select(grp => new GrupoGastos
{ TipoGasto = grp.Key, Gastos = grp.ToList() } )
.ToList();
and finally:
public class CorteConVentas
{
public List<GrupoGastos> listaGastosAgrupada { get; set; }
}
First, looks like you want a dictionary, not a list:
public class CorteConVentas
{
public Dictionary<string, List<Gasto>> dictaGastosAgrupada { get; set; }
}
Second, that select list with two columns is going to give you trouble because it comes out as an anonymous type, and you have no way to declare a dictionary that will contain it. Instead, just return the object:
var GD = gastos.GroupBy(u => u.TipoDeGasto).Select(grp);
So now you have GD which will let you enumerate over a list of grp objects. You can add them to your dictionary like this:
foreach (var grp in GD) dictaGastosAgrupada.Add(grp.Key, grp.ToList());
I have a fairly straight forward requirement - to populate a viewmodel, which has a SelectList as one of its properties - NewOccs is defined on the model as:
public class RatesList
{
[Key]
public long TypeID { get; set; }
public string TypeName { get; set; }
public int TypeCount { get; set; }
public IEnumerable<SelectListItem> NewOccs { get; set; }
}
My controller code to populate it is:
var rooms = dbt.Rooms.Where(r => r.hotel_id == AccID)
.GroupBy(p => p.RoomTypes).Select(g => new RatesList
{
TypeName = g.Key.type_name,
TypeCount = g.Count(),
NewOccs = dbt.Rates.Where(rt => rt.type_id == g.Key.type_id).GroupBy(rt => rt.occ).AsEnumerable()
.Select(proj => new SelectListItem
{
Text = proj.Key,
Value =proj.Key
})
}).ToList();
The Rates table it should be getting its information from is:
public class Rates
{
public int id { get; set; }
public long type_id { get; set; }
public DateTime ratedate { get; set; }
public decimal rate { get; set; }
public string occ { get; set; }
}
How to I access any of the other fields in my Rates table - when I'm populating the SelectList? For example, in VSExpressIDE intellisense only allows me to type proj.Key - the other properties are not there. I want occ to be the key/value and I would like the text to be a concatenation of occ and rate - ie:
Text = proj.occ + ' ' + rate.ToString()
...but rate and occ cannot be found in intellisense.
Thank you, Mark
If you step through your debugger, you'll see that GroupBy() provides a GroupedEnumerable, which contains Keys. The keys are Lookup<string, Rates>, because you used GroupBy on a string.
If you changed your Select to a SelectMany, you'd see all your Rates. But that would defeat the purpose of the GroupBy. I'm not totally sure what you want in the end, but here is a good guide to GroupBy
Like this:
public class Client
{
public int SelectedSexId { get; set; }
public IList<Sex> SexList { get; set; }
public IList<SelectListItem> SexListSelectListItems
{
get
{
SexList=SexList??new List<Sex>();
var list = (from item in SexList
select new SelectListItem()
{
Text = item.Name,
Value = item.Id.ToString(CultureInfo.InvariantCulture)
}).ToList();
return list;
}
set { }
}
}