I have the following as part of my entity framework class:-
public CustomerDetails GetCustomer(int id) {
var customerDetails = entities.AccountDefinitions
.Where(a => a.ORG_ID == id)
.Select(cd => new CustomerDetails
{
AD = cd,
SD = cd.SDOrganization,
AA = cd.SDOrganization.AaaPostalAddresses,
SN = cd.SiteDefinitions,
Ar = cd.SiteDefinitions.SelectMany(a => a.DepartmentDefinitions.SelectMany(a2 => a2.SDUsers.Select(a3 => a3.AaaUser)))
})
.SingleOrDefault();
return customerDetails;
}
Inside the inner .Select I have five assignments operations, but since some assignments depend upon the other so how I can write something such as:-
Ar = SN.SelectMany(…….)
So in this way I will only query the database only one time to retrieve the cd.SiteDefenitions??
One option would be to do it like this:
var customerDetails = entities.AccountDefinitions
.Where(a => a.ORG_ID == id)
.Select(cd => new CustomerDetails
{
AD = cd,
SD = cd.SDOrganization,
AA = cd.SDOrganization.AaaPostalAddresses,
SN = cd.SiteDefinitions
})
.SingleOrDefault();
if (customerDetails != null)
{
customerDetails.Ar = customerDetails.SN...
}
return customerDetails;
I know it may not be quite what you were looking for, but you're not going to be able to get at the SN property during the parameterless constructor operation like that (AFAIK).
Related
Why am I getting only one entry in DownTimeDetails list even though in Data we have 3 entries.
VehicleEventDetails Res = dbEntity.DownTimeHeaders
.Join(dbEntity.DownTimeDetails, dth => dth.DownTimeHeaderID, dtd => dtd.DownTimeHeaderID, (dth, dtd) => new { dth, dtd })
.Where(x => x.dth.DownTimeHeaderID == 42)
.GroupBy(gx => gx.dtd.DownTimeDetailID)
.Select(t => new VehicleEventDetails()
{
BookingId = t.Select(a => a.dth.BookingId).FirstOrDefault(),
DownTimeDetails = t.Select(ab => new DownTimeDetails
{
LocalDTStartTime = (DateTime)ab.dtd.LocalDTStartTime,
LocalDTEndTime = (DateTime)ab.dtd.LocalDTEndTime,
CalculatedEventDTReason = ab.dtd.CalculatedEventDTReason,
CalculatedEventDTInMinutes = (int)ab.dtd.CalculatedEventDT,
}).ToList()
}).FirstOrDefault();
You are looking for something like this:
VehicleEventDetails Res = dbEntity.DownTimeHeaders
.Where(x => x.DownTimeHeaderID == 42)
.Select(x => new VehicleEventDetails
{
BookingId = x.BookingId,
DownTimeDetails = x.DownTimeDetails
.Select(dtd=> new DownTimeDetails
{
LocalDTStartTime = (DateTime)dtd.LocalDTStartTime,
LocalDTEndTime = (DateTime)dtd.LocalDTEndTime,
CalculatedEventDTReason = dtd.CalculatedEventDTReason,
CalculatedEventDTInMinutes = (int)dtd.CalculatedEventDT,
})
.ToList()
})
.FirstOrDefault();
Notes:
Using .Join is an anti-Entity Framework pattern. Always try to use navigation properties, they exist for a reason.
Don't use .GroupBy unless you actually need a group. You don't want any grouping in this query.
As a general note, try not to make the expression variable names so confusing.
This is my code that retrieves information from the database about a list of Printer Drivers. The table has a list of Printer Drivers, as well as what servers they were found on.
public List<PrinterDrivers> GetPrinterDriversFromCache()
{
using (dbPrintSimpleDataContext db = new dbPrintSimpleDataContext())
{
var q = from p in db.GetTable<tblPrinterDriverCache>()
where p.CacheGUID == mostRecentCacheID()
group p by p.PrinterDriver into g
select new PrinterDrivers
{
DriverName = g.Key,
InstalledOn = g.Where(x => x.PrinterDriver == g.Key).Select(x => x.PrinterServer).ToList(),
Usable = (g.Count() == Properties.Settings.Default.PrintServers.Count)
};
return q.ToList();
}
}
What I am trying to return is a List that contains a property that has a List in it that contains what servers that printer driver exists on. I think that I'm up against the limit of my current LINQ SQL knowledge :(
The resultant List should contain:
DriverName = Printer driver name, in this case the group key (string)
InstalledOn = List (containing the list of servers that this printer driver was found on)
Usable = A simple bool check if the servers that it was found on is the same amount as the servers we have in the preferences file.
Thanks for the help :)
Try this:
LINQ Lambda, Group by with list
The problem is that Linq does not know about ToList. Only part of the entire query is executed on the server as there is an extra ToList call before the final ToList call (Untested code below)
public List<PrinterDrivers> GetPrinterDriversFromCache()
{
using (dbPrintSimpleDataContext db = new dbPrintSimpleDataContext())
{
var q = (from p in db.GetTable<tblPrinterDriverCache>()
where p.CacheGUID == mostRecentCacheID()
group p by p.PrinterDriver.DriverName into g
select g
).ToList().Select(g => new PrinterDrivers
{
DriverName = g.Key,
InstalledOn = g.Where(x => x.PrinterDriver == g.Key).Select(x => x.PrinterServer).ToList(),
Usable = (g.Count() == Properties.Settings.Default.PrintServers.Count)
});
return q.ToList();
}
}
Translating the same pattern from the answer I linked, yours would be:
var q = db.GetTable<tblPrinterDriverCache>()
.Where(p => p.CacheGUID == mostRecentCacheID())
.Select(o => new { DriverName = o.DriverName, PrintServer = o.PrintServer })
.GroupBy(g => g.DriverName)
.ToList()
.Select(g => new PrinterDrivers
{
DriverName = g.Key,
InstalledOn = g.Select(p => p.PrinterServer).ToList(),
Usable = (g.Count() == Properties.Settings.Default.PrintServers.Count)
}
)
.ToList();
I have the following database code:
static IEnumerable<dynamic> GetData(bool withchildren) {
using (var model = new testEntities()) {
var res = default(IQueryable<dynamic>);
if (withchildren) {
res = model.UserSet
.Where(u => u.name != "")
.Select(u => new {
Name = u.name,
Email = u.email,
Groups = u.GroupSet.Select(g => new {
Name = g.name,
Id = g.Id
})
});
} else {
res = model.UserSet
.Where(u => u.name != "")
.Select(u => new {
Name = u.name,
Email = u.email
});
}
return res.ToList()
}
}
I would like to shrink the code and write it like this:
static IEnumerable<dynamic> GetData(bool withchildren) {
using (var model = new testEntities()) {
var res = default(IQueryable<dynamic>);
res = model.UserSet
.Where(u => u.name != "")
.Select(u => {
dynamic item = new {
Name = u.name,
Email = u.email
};
if(withchildren) {
item.Groups = u.GroupSet.Select(g => new {
Name = g.name,
Id = g.Id
});
}
return item;
});
return res.ToList();
}
}
But Visual Studio complains, that it cannot convert the lambda expression into an expression tree.
My question is, is there a way to accomplish that with the Entity Framework and Linq? I really wouldn't want to use ADO.net directly.
Maybe there is even a better version to shrink it, than the code that I imagine.
Here is a related question with Linq-To-Objects.
EDIT
Before someone asks, I use dynamic in the example code to make it a bit easier and faster.
EDIT 2
My goal with this approach is, to only query the fields I need to improve performance. Check http://www.progware.org/Blog/post/Slow-Performance-Is-it-the-Entity-Framework-or-you.aspx.
At the moment we use something like
static IEnumerable<dynamic> GetData(bool withchildren) {
using (var model = new testEntities()) {
var res = default(IQueryable<dynamic>);
res = model.UserSet
.Where(u => u.name != "")
.ToList();
return res;
}
}
And the performance is, according to Glimpse, horrible.
EDIT 3
Short side note, I made up some quick and dirty code. That is, why the foreach at the end was not needed. The actual code is not available at the moment.
Is there any reason you couldn't use:
res = model.UserSet
.Where(u => u.name != "")
.Select(u => new {
Name = u.name,
Email = u.email,
Groups = withchildren
? u.GroupSet.Select(g => new {
Name = g.name,
Id = g.Id
})
: null;
})
};
or perhaps:
res = model.UserSet
.Where(u => u.name != "")
.ToList() // ToList() here to enumerate the items
.Select(u => {
dynamic item = new {
Name = u.name,
Email = u.email
};
if(withchildren) {
item.Groups = u.GroupSet.Select(g => new {
Name = g.name,
Id = g.Id
});
}
return item;
});
One approach that would allow you to eliminate some code would be:
var res = model.UserSet.Where(u => u.name != "");
if (withchildren) {
res = res.Select(u => new {
Name = u.name,
Email = u.email,
Groups = u.GroupSet.Select(g => new {
Name = g.name,
Id = g.Id
})
});
} else {
res = res.Select(u => new {
Name = u.name,
Email = u.email
});
}
One of the most requested by community feature is support of multi-line expressions in EF,
however so far, you can use only conditional operator "?:" as well as wrap result in
one common type, so both your results will have to have "Groups" field.
Also there are an extensions to linq-providers such as https://www.nuget.org/packages/LinqKit/ ,
however they are based on own conventions so any developer should study it in depth before
taking advance in applying and supporting code written upon that extensions.
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.
The code below is what I currently have and works fine. I feel that I could do more of the work I am doing in Linq instead of C# code.
Is there is anyone out there who can accomplish the same result with more Linq code and less C# code.
public List<Model.Question> GetSurveyQuestions(string type, int typeID)
{
using (eMTADataContext db = DataContextFactory.CreateContext())
{
List<Model.Question> questions = new List<Model.Question>();
List<Linq.Survey_Question> survey_questions;
List<Linq.Survey> surveys = db.Surveys
.Where(s => s.Type.Equals(type) && s.Type_ID.Equals(typeID))
.ToList();
if (surveys.Count > 0)
{
survey_questions = db.Survey_Questions
.Where(sq => sq.Survey_ID == surveys[0].ID).ToList();
foreach (Linq.Survey_Question sq in survey_questions)
{
Model.Question q = Mapper.ToBusinessObject(sq.Question);
q.Status = sq.Status;
questions.Add(q);
}
}
else
{
questions = null;
}
return questions;
}
}
Here is my Mapper function from my Entity to Biz Object
internal static Model.Question ToBusinessObject(Linq.Question q)
{
return new Model.Question
{
ID = q.ID,
Name = q.Name,
Text = q.Text,
Choices = ToBusinessObject(q.Question_Choices.ToList())
};
}
I want my mapper funciton to map the Question Status like so.
internal static Model.Question ToBusinessObject(Linq.Question q)
{
return new Model.Question
{
ID = q.ID,
Name = q.Name,
Text = q.Text,
Choices = ToBusinessObject(q.Question_Choices.ToList()),
Status = q.Survey_Questions[?].Status
};
}
? the issue is this function does not know which survey to pull the status from.
Instead of creating the biz object then setting the Status property in a foreach loop like so
foreach (Linq.Survey_Question sq in survey_questions)
{
Model.Question q = Mapper.ToBusinessObject(sq.Question);
q.Status = sq.Status;
questions.Add(q);
}
I would like to somehow filter the EntitySet<Survey_Question> in the q object above in the calling method, such that there would only be one item in the q.Survey_Questions[?] collection.
below is my database schema and business object schema
What I needed to do was setup a join.
public List<Model.Question> GetSurveyQuestions(string type, int typeID)
{
using (eMTADataContext db = DataContextFactory.CreateContext())
{
return db.Survey_Questions
.Where(s => s.Survey.Type.Equals(type) && s.Survey.Type_ID.Equals(typeID))
.Join(db.Questions,
sq => sq.Question_ID,
q => q.ID,
(sq, q) => Mapper.ToBusinessObject(q, sq.Status)).ToList();
}
}
And then overload my Mapper Function
internal static Model.Question ToBusinessObject(Linq.Question q, string status)
{
return new Model.Question
{
ID = q.ID,
Name = q.Name,
Text = q.Text,
Status = status,
Choices = ToBusinessObject(q.Question_Choices.ToList()),
};
}
from question in db.Survey_Questions
let surveys = (from s in db.Surveys
where string.Equals(s.Type, type, StringComparison.InvariantCultureIgnoreCase) &&
s.Type_ID == typeID)
where surveys.Any() &&
surveys.Contains(s => s.ID == question.ID)
select new Mapper.Question
{
ID = question.Id,
Name = question.Name,
Text = question.Text,
Choices = ToBusinessObject(question.Question_Choices.ToList()),
Status = question.Status
}
Does that get you on the right track?
Why are you duplicating all your classes? You could just extend the LINQ to SQL classes with your business logic - they are partial classes. This is somewhat against the purpose of an OR mapper - persisting business entities.