Linq: Joining two tables - c#

On my WPF Grid I am populating the following properties. The last two are coming from another table. I would like to get the mapping rule from another table where sourceelementid in the transaction table equals the id of the messagefield table
public List<MessageFieldViewModel> GetAllViewModelMsgFields()
{
messageFieldVModel = messageField.GetAllMessageField().Select(msgFields => new MessageFieldViewModel
{
Id = msgFields.Id,
Code = msgFields.Code,
Name = msgFields.Name,
Position = msgFields.Position,
Length = msgFields.Length,
IsMapped = (transactionRuleList.Any(tr=> tr.SourceElementId ==msgFields.Id)),
MappingRule = transactionRuleList.Where(mapRule => mapRule.MappingRule.Any(tr=> tr.SourceElementId ==msgFields.Id)),
})
.ToList();
return messageFieldVModel;
}
but the Mapping rule column throws error. Can some one help me ?

Here is a solution :
transactionRuleList = transationRuleViewModel.GetAllTranslationRules();
messageFieldVModel = messageField.GetAllMessageField().Select(msgFields => new MessageFieldViewModel
{
Id = msgFields.Id,
Code = msgFields.Code,
Name = msgFields.Name,
Position = msgFields.Position,
Length = msgFields.Length,
IsMapped = (transactionRuleList.Any(tr => tr.SourceElementId == msgFields.Id)),
MappingRule = transactionRuleList.Any(mapRule => mapRule.SourceElementId == msgFields.Id)?transactionRuleList.First(mapRule => mapRule.SourceElementId == msgFields.Id).MappingRule:null

Related

How to write LINQ query for fetching the specific records and generating new result at the same time?

I have to update one field in the row of the table after fetching two records from the same row. As an easiest practice I have fetched two records individually, created a new value and then updating that particular property through Entity framework. I think there is a better way to do the same thing with less code. If any body can suggest please.
if (objModel.amountpaid==0)
{
using (estatebranchEntities db=new estatebranchEntities())
{
int rentVar = Convert.ToInt32(db.PropertyDetails.Where(m => m.propertyid == objVM.propertyid).Select(m => m.rent).SingleOrDefault());
int balanceVar = Convert.ToInt32(db.PropertyDetails.Where(m => m.propertyid == objVM.propertyid).Select(m => m.balance).SingleOrDefault());
int balanceUpdateVar = (rentVar + balanceVar);
var propInfo = new PropertyDetail() { balance = balanceUpdateVar };
//var result = (from a in db.PropertyDetails
// where a.propertyid == objVM.propertyid
// select new PropertyDetail
// {
// rent = a.rent,
// balance = a.balance
// }).ToList();
db.PropertyDetails.Attach(propInfo);
db.Entry(propInfo).Property(z => z.balance).IsModified = true;
db.SaveChanges();
}
}
Here is what I think you can do.
Fetch the data once and update once.
using (estatebranchEntities db=new estatebranchEntities())
{
var propDetails = db.PropertyDetails.FirstOrDefault(m => m.propertyid == objVM.propertyid);
if (propDetails != null)
{
int rentVar = Convert.ToInt32(propDetails.rent);
int balanceVar = Convert.ToInt32(propDetails.balance);
int balanceUpdateVar = rentVar + balanceVar;
//now do the update
propDetails.balance = balanceUpdateVar;
db.Entry(proDetails).State = EntityState.Modified;
db.SaveChanges();
}
}
if you need to use the rentVar,balanceVar or the balanceUpdateVar, outside of the using statement then declare them outside it.

How do you reuse mapping functions on Nested entities in Entity Framework?

I have seen multiple questions that are similar to this one but I think my case is slightly different. I'm using EF6 to query the database and I'm using data projection for better queries.
Given that performance is very important on this project I have to make sure to just read the actual fields that I will use so I have very similar queries that are different for just a few fields as I have done this I have noticed repetition of the code so I'm been thinking on how to reuse code this is currently what I Have:
public static IEnumerable<FundWithReturns> GetSimpleFunds(this DbSet<Fund> funds, IEnumerable<int> fundsId)
{
IQueryable<Fund> query = GetFundsQuery(funds, fundsId);
var results = query
.Select(f => new FundWithReturns
{
Category = f.Category,
ExpenseRatio = f.ExpenseRatio,
FundId = f.FundId,
Name = f.Name,
LatestPrice = f.LatestPrice,
DailyReturns = f.FundDailyReturns
.Where(dr => dr.AdjustedValue != null)
.OrderByDescending(dr => dr.CloseDate)
.Select(dr => new DailyReturnPrice
{
CloseDate = dr.CloseDate,
Value = dr.AdjustedValue.Value,
}),
Returns = f.Returns.Select(r => new ReturnValues
{
Daily = r.AdjDaily,
FiveYear = r.AdjFiveYear,
MTD = r.AdjMTD,
OneYear = r.AdjOneYear,
QTD = r.AdjQTD,
SixMonth = r.AdjSixMonth,
ThreeYear = r.AdjThreeYear,
YTD = r.AdjYTD
}).FirstOrDefault()
})
.ToList();
foreach (var result in results)
{
result.DailyReturns = result.DailyReturns.ConvertClosingPricesToDailyReturns();
}
return results;
}
public static IEnumerable<FundListVm> GetFundListVm(this DbSet<Fund> funds, string type)
{
return funds
.Where(f => f.StatusCode == MetisDataObjectStatusCodes.ACTIVE
&& f.Type == type)
.Select(f => new FundListVm
{
Category = f.Category,
Name = f.Name,
Symbol = f.Symbol,
Yield = f.Yield,
ExpenseRatio = f.ExpenseRatio,
LatestDate = f.LatestDate,
Returns = f.Returns.Select(r => new ReturnValues
{
Daily = r.AdjDaily,
FiveYear = r.AdjFiveYear,
MTD = r.AdjMTD,
OneYear = r.AdjOneYear,
QTD = r.AdjQTD,
SixMonth = r.AdjSixMonth,
ThreeYear = r.AdjThreeYear,
YTD = r.AdjYTD
}).FirstOrDefault()
}).OrderBy(f=>f.Symbol).Take(30).ToList();
}
I'm trying to reuse the part where I map the f.Returns so I tried created a Func<> like the following:
private static Func<Return, ReturnValues> MapToReturnValues = r => new ReturnValues
{
Daily = r.AdjDaily,
FiveYear = r.AdjFiveYear,
MTD = r.AdjMTD,
OneYear = r.AdjOneYear,
QTD = r.AdjQTD,
SixMonth = r.AdjSixMonth,
ThreeYear = r.AdjThreeYear,
YTD = r.AdjYTD
};
and then use like this:
public static IEnumerable<FundListVm> GetFundListVm(this DbSet<Fund> funds, string type)
{
return funds
.Where(f => f.StatusCode == MetisDataObjectStatusCodes.ACTIVE
&& f.Type == type)
.Select(f => new FundListVm
{
Category = f.Category,
Name = f.Name,
Symbol = f.Symbol,
Yield = f.Yield,
ExpenseRatio = f.ExpenseRatio,
LatestDate = f.LatestDate,
Returns = f.Returns.Select(MapToReturnValues).FirstOrDefault()
}).OrderBy(f=>f.Symbol).Take(30).ToList();
}
The compiler is ok with it but at runtime, it crashes and says: Internal .NET Framework Data Provider error 1025
I tried to convert the Func into Expression like I read on some questions and then using compile() but It didn't work using AsEnumerable is also not an option because It will query all the fields first which is what I want to avoid.
Am I trying something not possible?
Thank you for your time.
It definitely needs to be Expression<Func<...>>. But instead of using Compile() method (not supported), you can resolve the compile time error using the AsQueryable() method which is perfectly supported (in EF6, the trick doesn't work in current EF Core).
Given the modified definition
private static Expression<Func<Return, ReturnValues>> MapToReturnValues =
r => new ReturnValues { ... };
the sample usage would be
Returns = f.Returns.AsQueryable().Select(MapToReturnValues).FirstOrDefault()

Handling null nested values when using .Select() in Lambda with Entity Framework

We are having a hard time trying to figure out the best way to handle this with out declaring a loop after you get the data.
For instance take this piece of code: (Data2 is tied to Data with a foreign key)
context.Data.Select(_ => new DataModel
{
Id = _.Id,
Data2 = new Data2Model
{
Id = _.Data2.Id,
Name = _.Data2.Name,
Date = _.Data2.Date
},
Date = _.Date
});
If _.Data2 is not null then this runs correctly but if _.Data2 happens to be null then this will error. The way we are getting around this now is to add Data2Id to our DataModel and then loop through all of the records to get the information if its not null.
var lst = context.Data.Select(_ => new DataModel
{
Id = _.Id,
Data2Id = _.Data2ID
Date = _.Date
}).ToList();
foreach(var item in lst)
{
if (item.Data2Id != null)
{
var dataItem = context.Data2.FirstOrDefault(_ => _.Id == item.Data2Id);
item.Data2 = new Data2Model
{
Id = dataItem.Id,
Name = dataItem.Name,
Date = dataItem.Date
}
}
}
Is there a cleaner / better way to keep this in the original select loop.
Thanks
Try:
Data2 = _.Data2 == null ? null : new Data2Model
{
Id = _.Data2.Id,
Name = _.Data2.Name,
Date = _.Data2.Date
},
context.Data.Select(_ => new DataModel
{
Id = _.Id,
Data2 = _.Data2 == null ?
new Data2Model{Id = _.Data2ID} :
new Data2Model{ Id=_.Data2.Id, Name=_.Data2.Name, Data=_.Data2.Date},
Date = _.Date
});
I'm making the assumption that if it is null you still have the _.Data2ID at hand?
Using the ternary operator
If Data2 is null then return a new Data2Model with just the other _.Data2ID value
If it is not null then go ahead and create a new Data2Model with all the details.
You can extract the logic in separate method to shorten your LINQ query and potentially reuse your code:
private static DataModel Map(DataModel _)
{
Data2Model model = _.Data2 ?? new Data2Model();
return new DataModel
{
Id = _.Id,
Date = _.Date,
Data2 = new Data2Model
{
Id = model.Id,
Name = model.Name,
Date = model.Date
}
};
}
Your query than becomes:
context.Data.Select(Map);
You should replace the artificial types with your own.

StringBuilder within IEnumerable

I have a ControlMeasure table that holds information on each control measure and a ControlMeasurepeopleExposed Table that holds a record for each person exposed in the control measure this could be 1 record or many records.
I Have a controller that populates a List view
For each item in the list, Control Measure, I would like to create a string that shows all the People at risk
e.g.
PeopleString = "Employees, Public, Others";
Ive added a foreach in the controller to show what I'm trying to do however I'm aware that this wont work.
The controller is this:
public ActionResult ControlMeasureList(int raId)
{
//Populate the list
var hazards = new List<Hazard>(db.Hazards);
var controlMeasures = new List<ControlMeasure>(db.ControlMeasures).Where(x => x.RiskAssessmentId == raId);
var cmcombined = (
from g in hazards
join f in controlMeasures
on new { g.HazardId } equals new { f.HazardId }
select new CMCombined
{
Activity = f.Activity,
ControlMeasureId = f.ControlMeasureId,
ExistingMeasure = f.ExistingMeasure,
HazardName = g.Name,
LikelihoodId = f.LikelihoodId,
Rating = f.Rating,
RiskAssessmentId = f.RiskAssessmentId,
SeverityId = f.SeverityId,
}).OrderBy(x => x.Activity).ToList();
var cmPeopleExp = new List<ControlMeasurePeopleExposed>(db.ControlMeasurePeopleExposeds).Where(x => x.RiskAssessmentId == raId);
var peopleExp = from c in cmPeopleExp
join d in db.PeopleExposeds
on c.PeopleExposedId equals d.PeopleExposedId
orderby d.Name
select new RAPeopleExp
{
RAPeopleExpId = c.PeopleExposedId,
PeopleExpId = c.PeopleExposedId,
PeopleExpName = d.Name,
RiskAssessmentId = c.RiskAssessmentId,
ControlMeasureId = c.ControlMeasureId
};
var model = cmcombined.Select(t => new FullControlMeasureListViewModel
{
ControlMeasureId = t.ControlMeasureId,
HazardName = t.HazardName,
LikelihoodId = t.LikelihoodId,
Rating = t.Rating,
SeverityId = t.SeverityId,
Activity = t.Activity,
ExCM = t.ExistingMeasure,
//This section here is where I'm struggling
var PeopleString = new StringBuilder();
foreach (var p in peopleExp)
{
PeopleString.AppendLine(p.PeopleName);
{
PeopleExposed = PeopleString,
});
return PartialView("_ControlMeasureList", model);
}
I know I cant directly put this code in the controller but it does represent what I want to do.
You can't foreach within an object initializer (which is what you're trying to do when instantiating FullControlMeasureListViewModel). You can, however, use a combination of string.Join and peopleExp.Select:
var model = cmcombined.Select(t => new FullControlMeasureListViewModel
{
//other props
PeopleExposed = string.Join(",", peopleExp
.Where(p => p.ControlMeasureId == t.ControlMeasureId)
.Select(p => p.PeopleExpName));
//other props
});

EF4 insert entity with childs. Using POCO T4 template

I'm literally going crazy with this. I receive from the HTML the data of one "Father" entity, along with the data of three child entities.
In my modelbinder, I create stub entities for relationships, containing only the primary key.
This is my ModelBinder code:
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
DefaultModelBinder binder = new DefaultModelBinder();
var estimate = (Estimate)binder.BindModel(controllerContext, bindingContext);
estimate.Id = Guid.NewGuid();
estimate.OwnerSociety = ModelBinderHelper.MapComplexType<OwnerSociety, int>(controllerContext, "OwnerSociety", int.Parse, c => c.IDOwnerSociety);
estimate.EstimateType = ModelBinderHelper.MapComplexType<EstimateType, Guid>(controllerContext, "EstimateType", Guid.Parse, c => c.Id);
estimate.Brand = ModelBinderHelper.MapComplexType<Brand, int>(controllerContext, "Brand", int.Parse, c => c.IDBrand);
estimate.FromAccount = ModelBinderHelper.MapComplexType<User, int>(controllerContext, "FromAccount", int.Parse, c => c.IDUser);
estimate.ManagerDirector = ModelBinderHelper.MapComplexType<User, int>(controllerContext, "ManagerDirector", int.Parse, c => c.IDUser);
estimate.Projects.Add(new Project
{
StrategicPlanner = ModelBinderHelper.MapComplexType<User, int>(controllerContext, "StrategicPlanner_1", int.Parse, c => c.IDUser),
Activity = ModelBinderHelper.MapComplexType<Activity, int>(controllerContext, "Activity_1", int.Parse, c => c.IDActivity),
ProjectState = ModelBinderHelper.MapComplexType<ProjectState, int>(controllerContext, "ProjectState_1", int.Parse, c => c.IDProjectState),
StartDate = DateTime.Now,
Name = "XXX1",
Brand = estimate.Brand,
ProjectTypes = new ProjectTypes { IDProjectType = 1 },
Consuntivo = 0,
Order = 1
});
estimate.Projects.Add(new Project
{
StrategicPlanner = ModelBinderHelper.MapComplexType<User, int>(controllerContext, "StrategicPlanner_2", int.Parse, c => c.IDUser),
Activity = ModelBinderHelper.MapComplexType<Activity, int>(controllerContext, "Activity_2", int.Parse, c => c.IDActivity),
ProjectState = ModelBinderHelper.MapComplexType<ProjectState, int>(controllerContext, "ProjectState_2", int.Parse, c => c.IDProjectState),
StartDate = DateTime.Now,
Name = "XXX2",
Brand = estimate.Brand,
ProjectTypes = new ProjectTypes { IDProjectType = 1 },
Consuntivo = 0,
Order = 2
});
estimate.Projects.Add(new Project
{
StrategicPlanner = ModelBinderHelper.MapComplexType<User, int>(controllerContext, "StrategicPlanner_3", int.Parse, c => c.IDUser),
Activity = ModelBinderHelper.MapComplexType<Activity, int>(controllerContext, "Activity_3", int.Parse, c => c.IDActivity),
ProjectState = ModelBinderHelper.MapComplexType<ProjectState, int>(controllerContext, "ProjectState_3", int.Parse, c => c.IDProjectState),
StartDate = DateTime.Now,
Name = "XXX3",
Brand = estimate.Brand,
ProjectTypes = new ProjectTypes { IDProjectType = 1 },
Consuntivo = 0,
Order = 3
});
return estimate;
}
When I try to simply attach my father entity, declare it and the child properties as Added, I get the "there is already an object in the objectstatemanager with the same key". If I try to insert the father entity without childs, it works.
I tried another "way". The following code:
public Estimate CreateEstimate(Estimate toCreate)
{
var brand = Brands.First(c => c.IDBrand == toCreate.Brand.IDBrand);
var estimateType = EstimateTypes.First(c => c.Id == toCreate.EstimateType.Id);
var account = Users.First(c => c.IDUser == toCreate.FromAccount.IDUser);
var manager = Users.First(c => c.IDUser == toCreate.ManagerDirector.IDUser);
var owner = OwnerSocieties.First(c => c.IDOwnerSociety == toCreate.OwnerSociety.IDOwnerSociety);
toCreate.Brand = brand;
toCreate.EstimateType = estimateType;
toCreate.FromAccount = account;
toCreate.ManagerDirector = manager;
toCreate.OwnerSociety = owner;
foreach (var project in toCreate.Projects)
{
project.Activity = Activities.First(c => c.IDActivity == project.Activity.IDActivity);
project.ProjectState = ProjectStates.First(c => c.IDProjectState == project.ProjectState.IDProjectState);
project.StrategicPlanner = Users.First(c => c.IDUser == project.StrategicPlanner.IDUser);
project.ProjectTypes = _entities.ProjectTypes.First();
}
_entities.EstimateSet.AddObject(toCreate);
return toCreate;
}
But it doesn't work, telling me that "INSERT on table "Activities" fails because column "Name" can't be null". But it really shouldn't be inserting anything in the Activity table, in fact I'm retrieving the Activity items from the DB and using them.
Funny, the following code works:
public Estimate CreateEstimate(Estimate toCreate)
{
var estimate = new Estimate();
var brand = Brands.First(c => c.IDBrand == toCreate.Brand.IDBrand);
var estimateType = EstimateTypes.First(c => c.Id == toCreate.EstimateType.Id);
var account = Users.First(c => c.IDUser == toCreate.FromAccount.IDUser);
var manager = Users.First(c => c.IDUser == toCreate.ManagerDirector.IDUser);
var owner = OwnerSocieties.First(c => c.IDOwnerSociety == toCreate.OwnerSociety.IDOwnerSociety);
estimate.Id = toCreate.Id;
estimate.Brand = brand;
estimate.EstimateType = estimateType;
estimate.FromAccount = account;
estimate.ManagerDirector = manager;
estimate.OwnerSociety = owner;
estimate.Date = toCreate.Date;
estimate.Subject = toCreate.Subject;
estimate.Status = toCreate.Status;
estimate.Language = toCreate.Language;
foreach (var project in toCreate.Projects)
{
var project1 = new Project();
project1.Activity = Activities.First(c => c.IDActivity == project.Activity.IDActivity);
project1.ProjectState = ProjectStates.First(c => c.IDProjectState == project.ProjectState.IDProjectState);
project1.StrategicPlanner = Users.First(c => c.IDUser == project.StrategicPlanner.IDUser);
project1.StartDate = project.StartDate;
project1.Name = project.Name;
project1.Brand = brand;
project1.ProjectTypes = _entities.ProjectTypes.First();
estimate.Projects.Add(project1);
}
_entities.EstimateSet.AddObject(estimate);
return toCreate;
}
But this means recreating the father entity, copying over the values, then recreating every child entity, copying the values, assigning it to the father entity, etc. That's a pain, and I don't want to write verbose code like this.
ORM should ease me of the pain of writing tons of code for CRUD operations but it seems like this is not the case.
Anyone can help me?
I had the exact same problem, and had the exact same pains when trying to use EF in a disconnected scenario. And I solved it the same way as you did: reload the object, and 'replay' the changes on the domain object (you have to apply a trick to make optimistic concurrency work though). This was the only way I found to make it work.
Another way to make this work is to send the original entity together with the modified entity. Then you don't have to reload it, but just attach the original entity and replay the changes. But I'm not sure about the overhead if you send all data twice, don't really like that.
Any other way, just fails, crashes, because it seems to mix up contexts, even if they are disposed already.

Categories

Resources