how to make group by and orderby linq - c#

This is my model:
public partial class DEGIVREUSE_SITE
{
public int EVENEMENT_ID { get; set; }
public string DEGIVREUSE_ID { get; set; }
public string SITE_COD { get; set; }
public string ETAT_GLOBAL { get; set; }
public string ETAT_CARBURANT { get; set; }
public Nullable<int> KM_CHASSIS { get; set; }
public Nullable<int> HEURE_CHASSIS { get; set; }
public string ETAT_FONCTIONNEMENT { get; set; }
public Nullable<int> HEURE_GROUPE { get; set; }
public string COMMENTAIRE { get; set; }
public virtual DEGIVREUSE DEGIVREUSE { get; set; }
public virtual SITE SITE { get; set; }
public virtual EVENEMENT EVENEMENT { get; set; }
}
[DataContract]
public class InventaireDegivreuse : Evenement
{
public InventaireDegivreuse()
: base(-1, Global.EvenementType.InventaireDegivreuse, DateTime.MinValue)
{
}
public InventaireDegivreuse(int id, DateTime date, string libelle, string societe)
: base(id, (int)Global.EvenementType.InventaireDegivreuse, date, libelle, "", "", societe)
{
ListeDegivreuses = new List<EtatDegivreuse>();
}
[DataMember]
public List<EtatDegivreuse> ListeDegivreuses { get; set; }
public void AddDegivreuse(EtatDegivreuse degivreuse)
{
lock (ListeDegivreuses)
ListeDegivreuses.Add(degivreuse);
}
public int NbDegivreuses
{
get
{
lock (ListeDegivreuses)
return ListeDegivreuses.Count;
}
}
public override void GenereLibelle()
{
Libelle = Properties.Resources.InventaireDegivreuse.Replace("%s", SocieteNom);
}
}
I need to orderby all Events with EVENEMENT_DT_CREA and after for each societe i get the first element of InventaireDegivreuse (the newer one which has the biggest EVENEMENT_DT_CREA) , I try this query but i had a bad result:
var #eventss = GetQuery(unitOfWork).Include(entity => entity.EVENEMENT).OrderByDescending(e => e.EVENEMENT.EVENEMENT_DT_CREA).GroupBy(m => m.EVENEMENT.SOCIETE_NOM).First().ToList();
In my solution for only one societe, i have correct result like this:
public InventaireDegivreuse GetLastBySociete(IReadOnlyUnitOfWork unitOfWork, string societeName)
{
var #event = GetQuery(unitOfWork).Include(entity => entity.EVENEMENT).OrderByDescending(e => e.EVENEMENT.EVENEMENT_DT_CREA).FirstOrDefault(m => m.EVENEMENT.SOCIETE_NOM ==societeName);
return DatabaseMapping.Map<DEGIVREUSE_SITE, InventaireDegivreuse>(#event);
}
Any idea please?

It will be hard to decide what do you want but I suspect that you want something like this:
var #event = GetQuery(unitOfWork)
.Include(entity => entity.EVENEMENT)
.GroupBy(e => e.EVENEMENT.SOCIETE_NOM)
.Select(g => g.OrderByDescending(e => e.EVENEMENT.EVENEMENT_DT_CREA)
.First());

Related

Retrieve part of structured data from POCOs C#

I have a List<TerminalLink> from which I want to retrieve a subset of. See the class structure below.
The subset is defined by a "map" which identifies which ChannelLink to select in any given scenario. Multiple ChannelLinks may be required per scenario, perhaps from the same ChannelGroup, perhaps from different ChannelGroups on the same TerminalLink, and perhaps from multiple TerminalLinks in my original in memory list.
The ChannelLink can only be select if it's ChannelGroupLink and TerminalLink match too (they form part of the index, I guess).
I have tried various Linq and foreach approaches, but I'm running into tens of lines of code, which is ugly and difficult to follow. I'm sure there is an elegant approach.
How do I return a pruned List<TerminalLink> containing just the data that matches my map (i.e. containing only TerminalLinks and ChannelGroupLinks with required ChannelLinks)?
Class structure:
public class TerminalLink
{
public string TerminalName { get; set; }
public string TerminalType { get; set; }
public ChannelGroupLink[] ChannelGroups { get; set; }
}
public class ChannelGroupLink
{
public string GroupName { get; set; }
public int ItemType { get; set; }
public int SubType { get; set; }
public ChannelLink[] Channels { get; set; }
}
public class ChannelLink
{
public string ChannelName { get; set; }
public string PathName { get; set; }
public string DataType { get; set; }
public bool IsOutput { get; set; }
}
EDIT to include the latest attempt:
public static List<TerminalLink> GetCompatibleChannelLinks(List<TerminalLinkMap> terminalLinkMaps, List<TerminalLink> asBuiltList)
{
List<TerminalLink> matching = new List<TerminalLink>();
var terminalsWithSomeMatchingChannelGroups = from terminal in asBuiltList
from tlm in terminalLinkMaps
where terminal.TerminalType == tlm.ItemSubTypeName
from cgsMaps in tlm.ChannelGroupsToLink
from cgs in terminal.ChannelGroups
where cgsMaps.IsLinkMatch(cgs.GroupName, cgs.ItemType, cgs.SubType)
select (
link: new TerminalLink()
{
TerminalName = terminal.TerminalName,
TerminalType = terminal.TerminalType
},
original: terminal, map: tlm
);
foreach (var possibleLink in terminalsWithSomeMatchingChannelGroups)
{
var linkCandidate = possibleLink.link;
var originalData = possibleLink.original;
var map = possibleLink.map;
foreach (var cgs in originalData.ChannelGroups)
{
foreach (var channel in cgs.Channels)
{
// WIP
}
}
}
}
EDIT 2: inclusion of TeminalLinkMap
public class TerminalLinkMap
{
public string TerminalTypeName { get; set; }
public string ItemSubTypeName { get; set; } // Match on this.
public ChannelGroupToLink[] ChannelGroupsToLink { get; set; }
}
public class ChannelGroupToLink
{
public string GroupName { get; set; } // regex match is ok.
public int ItemType { get; set; }
public int SubType { get; set; }
public ChannelToLink[] Channels { get; set; }
public ChannelGroupToLink GetClone() => GetClone(this);
public bool IsLinkMatch(string groupName, int itemType, int subType)
{
return
GroupName == groupName
&& ItemType == itemType
&& SubType == subType;
}
}
public class ChannelToLink
{
public int ItemType { get; set; }
public string DataType { get; set; }
public int DataSizeInBits { get; set; }
public bool IsOutput { get; set; }
public ChannelToLink GetClone() => GetClone(this);
public bool IsLinkMatch(string dataType, bool isOutput)
{
return
DataType == dataType
&& IsOutput == isOutput;
}
public static ChannelToLink GetClone(ChannelToLink original) => new ChannelToLink()
{
ItemType = original.ItemType,
DataType = original.DataType,
DataSizeInBits = original.DataSizeInBits,
IsOutput = original.IsOutput
};
}
There is no need for a join.
Try following:
class Program
{
static void Main(string[] args)
{
List<TerminalLink> asBuiltList = new List<TerminalLink>();
List<TerminalLink> links = asBuiltList
.Select(x => new { TerminalLink = x, ChannelGroups = x.ChannelGroups.Where(y => y.IsLinkMatch("Apple", 123, 456)) })
.Where(x => x.ChannelGroups.Count() > 0)
.Select(x => new TerminalLink() { TerminalName = x.TerminalLink.TerminalName, TerminalType = x.TerminalLink.TerminalType, ChannelGroups = x.ChannelGroups.ToArray()})
.ToList();
}
}
public class TerminalLink
{
public string TerminalName { get; set; }
public string TerminalType { get; set; }
public ChannelGroupLink[] ChannelGroups { get; set; }
}
public class ChannelGroupLink
{
public string GroupName { get; set; }
public int ItemType { get; set; }
public int SubType { get; set; }
public ChannelLink[] Channels { get; set; }
public Boolean IsLinkMatch(string groupName, int itemType, int subType)
{
return (this.GroupName == groupName) && ( this.ItemType == itemType) && (this.SubType == subType);
}
}
public class ChannelLink
{
public string ChannelName { get; set; }
public string PathName { get; set; }
public string DataType { get; set; }
public bool IsOutput { get; set; }
}
}

Mapping Viewmodel to model using AutoMapper

I have written a controller method in asp.net api that would return a viewmodel called AllocationsViewModel. The GetAllocationsViewModel contains subsets of three more viewmodels. The GetAllocationsGrouped currently returns FIRMWIDE_MANAGER_ALLOCATION and I need to return this FirmWideAllocationsViewModel instead. I have installed Automapper 8.0 and added some code to do the mapping. Is that enough to do the job. I can see only the ManagerStrategyID and ManagerStrategyID values coming through the values are comming null for the fields. I have run the original query and can see there are values for all the fields
public class FIRMWIDE_MANAGER_ALLOCATION
{
private decimal _groupPercent;
public int FIRM_ID { get; set; }
public string FIRM_NAME { get; set; }
public int? MANAGER_STRATEGY_ID { get; set; }
public int? MANAGER_FUND_ID { get; set; }
public int MANAGER_ACCOUNTING_CLASS_ID { get; set; }
public int? MANAGER_FUND_OR_CLASS_ID { get; set; }
public string MANAGER_FUND_NAME { get; set; }
public string MANAGER_ACCOUNTING_CLASS_NAME { get; set; }
public string MANAGER_STRATEGY_NAME { get; set; }
public int? PRODUCT_ID { get; set; }
public string PRODUCT_NAME { get; set; }
public int? QUANTITY { get; set; }
public decimal? NAV { get; set; }
}
public class FirmWideAllocationsViewModel
{
private decimal _groupPercent;
public int FirmID { get; set; }
public string FirmName { get; set; }
public int? ManagerStrategyID { get; set; }
public int? ManagerFundID { get; set; }
public int ManagerAccountClassID{ get; set; }
public int? ManagerFundOrClassID { get; set; }
public string ManagerFundName { get; set; }
public string ManagerAccountingClassName { get; set; }
public string ManagerStrategyName { get; set; }
public int? ProductID { get; set; }
public string ProductName { get; set; }
public int? Quantity { get; set; }
public decimal? Nav { get; set; }
}
public IHttpActionResult Details(int id, DateTime date)
{
var viewModel = GetAllocationsViewModel(id, date);
if (viewModel == null) return NotFound();
return Ok(viewModel);
}
private AllocationsViewModel GetAllocationsViewModel(int id, DateTime date)
{
var ms = GetStrategy(id);
DateTime d = new DateTime(date.Year, date.Month, 1).AddMonths(1).AddDays(-1);
if (ms.FIRM_ID != null)
{
var firm = GetService<FIRM>().Get(ms.FIRM_ID.Value);
var currentEntity = new EntityAllocationsViewModel(new EntityViewModel { EntityId = firm.ID, EntityName = firm.NAME, EntityType = EntityType.Firm });
var allocationsGrouped = Mapper.Map<List<FIRMWIDE_MANAGER_ALLOCATION>, List<FirmWideAllocationsViewModel>>(GetAllocationsGrouped(EntityType.ManagerStrategy, id, d).ToList());
var missingProducts = GetMissingProducts();
var vm = new AllocationsViewModel
{
CurrentEntity = currentEntity,
ManagerAllocations = allocationsGrouped,
MissingProducts = missingProducts
};
return vm;
}
return null;
}
public class AllocationsViewModel
{
public EntityAllocationsViewModel CurrentEntity { get; set; }
public List<FirmWideAllocationsViewModel> ManagerAllocations { get; set; }
public object MissingProducts { get; set; }
}
I have added the following code after installing autommapper 8.0
public class AutoMapperConfig
{
public static void Initialize()
{
Mapper.Initialize((config) =>
{
config.ReplaceMemberName("FIRM_ID", "FirmID");
config.ReplaceMemberName("FIRM_NAME", "FirmName");
config.ReplaceMemberName("MANAGER_STRATEGY_ID", "ManagerStrategyID");
config.ReplaceMemberName("MANAGER_FUND_ID", "ManagerFundID");
config.ReplaceMemberName("MANAGER_ACCOUNTING_CLASS_ID", "ManagerAccountClassID");
config.ReplaceMemberName("MANAGER_FUND_OR_CLASS_ID", "ManagerFundOrClassID");
config.ReplaceMemberName("MANAGER_FUND_NAME", "ManagerFundName");
config.ReplaceMemberName("MANAGER_ACCOUNTING_CLASS_NAME", "ManagerAccountingClassName");
config.ReplaceMemberName("MANAGER_STRATEGY_NAME", "ManagerStrategyName");
config.ReplaceMemberName("PRODUCT_ID", "ProductID");
config.ReplaceMemberName("PRODUCT_NAME", "ProductName");
config.ReplaceMemberName("QUANTITY", "Quantity");
config.ReplaceMemberName("NAV", "Nav");
config.CreateMap<FIRMWIDE_MANAGER_ALLOCATION, FirmWideAllocationsViewModel>().ReverseMap();
});
}
}
protected void Application_Start()
{
AutoMapperConfig.Initialize();
GlobalConfiguration.Configure(WebApiConfig.Register);
}
The issue has been resolved. I had to amend the grouping statement that is called to include all the fields . It was working fine earlier but with the upgrade of the latest entity framework, I think its the case
allocations = allocations.GroupBy(x => new { x.MANAGER_STRATEGY_ID, x.PRODUCT_ID, x.EVAL_DATE })
.Select(group => new FIRMWIDE_MANAGER_ALLOCATION { EVAL_DATE = group.First().EVAL_DATE,
FIRM_ID = group.First().FIRM_ID,
FIRM_NAME = group.First().FIRM_NAME,
MANAGER_ACCOUNTING_CLASS_ID = group.First().MANAGER_ACCOUNTING_CLASS_ID,
MANAGER_ACCOUNTING_CLASS_NAME = group.First().MANAGER_ACCOUNTING_CLASS_NAME,
MANAGER_FUND_ID = group.First().MANAGER_FUND_ID,
MANAGER_FUND_NAME = group.First().MANAGER_FUND_NAME,
MANAGER_FUND_OR_CLASS_ID = group.First().MANAGER_FUND_OR_CLASS_ID,
NAV = group.First().NAV,
Percent = group.First().Percent,
MANAGER_STRATEGY_ID = group.First().MANAGER_STRATEGY_ID,
EMV = group.Sum(x => x.EMV),
USD_EMV = group.Sum(x => x.USD_EMV),
MANAGER_STRATEGY_NAME = group.First().MANAGER_STRATEGY_NAME,
PRODUCT_ID = group.First().PRODUCT_ID,
PRODUCT_NAME = group.First().PRODUCT_NAME })
.ToList();

Automapper configuration complex mappings

I hav been using automapper for sometime trying figure out how to handle different situation. I came across below situation and need some help figuring out the best approach. Below are my EF related classes;
public sealed class Invoice
{
public int InvoiceID { get; set; }
public DateTime InvoiceDate { get; set; }
public string CustomerName { get; set; }
public string CustomerAddress { get; set; }
public double? DiscountAmt { get; set; }
public Transaction InvoiceTransaction { get; set; }
public int TransactionID { get; set; }
}
public sealed class Transaction
{
public Transaction()
{
this.TransactionItems = new List<TransactionDetail>();
}
public int TransactionID { get; set; }
public DateTime TransactionDate { get; set; }
public DateTime TransactionLogDate { get; set; }
public TransactionType TransactionType { get; set; }
public IList<TransactionDetail> TransactionItems { get; set; }
public Invoice RefferingInvoice { get; set; }
public string Remarks { get; set; }
}
public sealed class TransactionDetail
{
public int TransactionID { get; set; }
public string ProductItemcode { get; set; }
public Product Product { get; set; }
public double Qty
{
get
{
return Math.Abs(this.StockChangeQty);
}
}
public double StockChangeQty { get; set; }
public double? UnitPrice { get; set; }
}
public sealed class Product
{
public Product()
{
this.StockTransactions = new List<TransactionDetail>();
}
public string ItemCode { get; set; }
public string ProductName { get; set; }
public string Manufacturer { get; set; }
public double UnitPrice { get; set; }
public IList<TransactionDetail> StockTransactions { get; set; }
public double Qty
{
get
{
if (this.StockTransactions.Count == 0)
{
return 0;
}
else
{
return this.StockTransactions.Sum(x => x.StockChangeQty);
}
}
}
public bool Discontinued { get; set; }
}
These are my view model classes;
public class InvoiceReportViewModel
{
public InvoiceReportViewModel()
{
LineItems = new List<InvoiceReportLineItemViewModel>();
}
public int InvoiceID { get; set; }
public DateTime InvoiceDate { get; set; }
public string CustomerName { get; set; }
public string CustomerAddress { get; set; }
public double? DiscountAmt { get; set; }
public string Remarks { get; set; }
public string StringInvoiceNo
{
get
{
return InvoiceID.ToString("########");
}
}
public IList<InvoiceReportLineItemViewModel> LineItems { get; set; }
}
public class InvoiceReportLineItemViewModel
{
public string ItemCode { get; set; }
public string ProductName { get; set; }
public string Manufacturer { get; set; }
public double? UnitPrice { get; set; }
public double Qty { get; set; }
public double LineTotal
{
get
{
if (UnitPrice.HasValue)
{
return UnitPrice.Value * Qty;
}
else
{
return 0;
}
}
}
}
My requirement is to convert the Invoice EF object to InvoiceReportViewModel object.
To do this I need to setup the profile. This is where I run into a problem; as it's not straight forward. The only way I see this done is by create my own Resolver by extending TypeConverter and manually doing the conversion by overriding ConvertCore method.
If there another way of getting this done (something with less work)???
Also I feel I could Map TransactionDetails EF class to InvoiceReportLineItemViewModel class by using the Mapper.CreateMap()..ForMember(...
But how can I use the mapper to convert it within the ConvertCore method?
Thanks in advance
In your case I do not see any requirements to use any custom converters.
You can convert Invoice EF object to InvoiceReportViewModel using simple Mapper.CreateMap like following:
public class InvoiceProfile: Profile
{
protected override void Configure()
{
Mapper.CreateMap<Invoice, InvoiceReportViewModel>()
.ForMember(c => c.CustomerName, op => op.MapFrom(v => v.CustomerName))
.ForMember(c => c.DiscountAmt, op => op.MapFrom(v => v.DiscountAmt))
.ForMember(c => c.InvoiceDate, op => op.MapFrom(v => v.InvoiceDate))
.ForMember(c => c.LineItems, op => op.MapFrom(v => v.InvoiceTransaction.TransactionItems));
Mapper.CreateMap<TransactionDetail, InvoiceReportLineItemViewModel>()
.ForMember(c => c.ProductName, op => op.MapFrom(v => v.Product.ProductName))
.ForMember(c => c.Qty, op => op.MapFrom(v => v.Qty))
//and so on;
}
}
Do not forget to register "InvoiceProfile"

how to make Value cannot be null, null

Im having a method like this
public List<GSMData> GetGSMList()
{
return meters.Select(x => x.Gsmdata.Last())
.ToList();
}
and i get a "Value cannot be null." exeption. How do i make it so the value can be null?
GSMData object
public class GSMData : Meter
{
public DateTime TimeStamp { get; set; }
public int SignalStrength { get; set; }
public int CellID { get; set; }
public int LocationAC { get; set; }
public int MobileCC { get; set; }
public int MobileNC { get; set; }
public string ModemManufacturer { get; set; }
public string ModemModel { get; set; }
public string ModemFirmware { get; set; }
public string IMEI { get; set; }
public string IMSI { get; set; }
public string ICCID { get; set; }
public string AccessPointName { get; set; }
public int MobileStatus { get; set; }
public int MobileSettings { get; set; }
public string OperatorName { get; set; }
public int GPRSReconnect { get; set; }
public string PAP_Username { get; set; }
public string PAP_Password { get; set; }
public int Uptime { get; set; }
}
you better check for Gsmdata having items or not
public List<GSMData> GetGSMList()
{
return meters.Where(x=>x.Gsmdata!=null && x.Gsmdata.Any()).Select(x => x.Gsmdata.Last())
.ToList();
}
I'm guessing your Gsmdata property is null.
In this case, you could use the Linq .Where() method, to make this:
public List<GSMData> GetGSMList()
{
return meters.Where(x => x.Gsmdata != null)
.Select(x => x.Gsmdata.Last())
.ToList();
}
But you should clarify what exactly is null for better answers.
Try excluding null items by filtering the list immediately, and before doing the select.
public List GetGSMList()
{
return meters.Where(x=> x.Gsmdata != null).Select(x => x.Gsmdata.Last())
.ToList();
}

ASP.Net C# MVC Issue Mapping Domain Model to ViewModel using AutoMapper

I'm having a little difficulty mapping a domain model to a view model, using AutoMapper.
My controller code is:
//
// GET: /Objective/Analyst
public ActionResult Analyst(int id)
{
var ovm = new ObjectiveVM();
ovm.DatePeriod = new DateTime(2013, 8,1);
var objectives = db.Objectives.Include(o => o.Analyst).Where(x => x.AnalystId == id).ToList();
ovm.ObList = Mapper.Map<IList<Objective>, IList<ObjectiveVM>>(objectives);
return View(ovm);
}
I am getting an error on the ovm.ObList = Mapper.... (ObList is underlined in red with the error):
'ObList': cannot reference a type through an expression; try 'Objectives.ViewModels.ObjectiveVM.ObList' instead
My Objective Class is:
public class Objective
{
public int ObjectiveId { get; set; }
public int AnalystId { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public Analyst Analyst { get; set; }
}
My ObjectiveVM (view model) is:
public class ObjectiveVM
{
public DateTime DatePeriod { get; set; }
public class ObList
{
public int ObjectiveId { get; set; }
public int AnalystId { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string AnalystName { get; set; }
public bool Include { get; set; }
}
}
In my startup/global.asax.cs I have used AutoMapper to map the Objective to the ObjectiveVM:
Mapper.CreateMap<Objective, ObjectiveVM.ObList>()
.ForMember(dest => dest.Include, opt => opt.Ignore())
.ForMember(dest => dest.AnalystName, opt => opt.MapFrom(y => (y.Analyst.AnalystName)));
Any help would be much appreciated,
Mark
Ok, thanks for all the suggestions - what I've ended up with is:
Controller:
//
// GET: /Objective/Analyst
public ActionResult Analyst(int id)
{
var ovm = new ObjectiveVM().obList;
var objectives = db.Objectives.Include(o => o.Analyst).Where(x => x.AnalystId == id).ToList();
ovm = Mapper.Map<IList<Objective>, IList<ObjectiveVM.ObList>>(objectives);
var ovm2 = new ObjectiveVM();
ovm2.obList = ovm;
ovm2.DatePeriod = new DateTime(2013, 8,1);
return View(ovm2);
}
ViewModel:
public class ObjectiveVM
{
public DateTime DatePeriod { get; set; }
public IList<ObList> obList { get; set; }
public class ObList
{
public int ObjectiveId { get; set; }
public int AnalystId { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string AnalystName { get; set; }
public bool Include { get; set; }
}
}
CreateMap:
Mapper.CreateMap<Objective, ObjectiveVM.ObList>()
.ForMember(dest => dest.Include, opt => opt.Ignore())
.ForMember(dest => dest.AnalystName, opt => opt.MapFrom(y => (y.Analyst.AnalystName)))
;
If I've mis-understood any advice, and you provided the answer, please post it - and I'll mark it as such.
Thank you,
Mark
As the commenter nemesv has rightly mentioned, the problem is about
ovm.ObList = Mapper.Map<IList<Objective>, IList<ObjectiveVM>>(objectives);
ObList is not a member of ObjectiveVM so, you should change the ObjectiveVM like this:
public class ObjectiveVM
{
public DateTime DatePeriod { get; set; }
public IList<ObList> obList { get; set; }
public class ObList
{
public int ObjectiveId { get; set; }
public int AnalystId { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string AnalystName { get; set; }
public bool Include { get; set; }
}
}
Update:
Controller:
public ActionResult Analyst(int id)
{
var ovm = new ObjectiveVM { DatePeriod = new DateTime(2013, 8, 1) };
var objectives = db.Objectives.Include(
o => o.Analyst).Where(x => x.AnalystId == id).ToList();
ovm.obList = Mapper.Map<IList<Objective>,
IList<ObjectiveVM.ObList>>(objectives);
return View(ovm);
}

Categories