Linq query to retrieve collection from one class to another class - c#

I have 2 classes SiteConfig, SiteConfigView. One is tightly coupled with my EF and one class to expose it to View models. Both the classes holds to a collection of type 'Brands'
I struck at writing a linq query to fetch the records from db to view model.
As I am exposing a different class to view model, I have to get the records of type 'SiteConfigView'. So I am writing a linq query but I am bit confused how to get the collection from SiteConfig to SiteConfigView.
There are my classes
public partial class SiteConfig
{
public SiteConfig()
{
this.SiteBrands = new HashSet<SiteBrand>();
}
public int IdSiteConfig { get; set; }
public string Name { get; set; }
public byte[] SiteLogo { get; set; }
public string Brands { get; set; }
public string LinkColour { get; set; }
public virtual ICollection<SiteBrand> SiteBrands { get; set; }
}
public class SiteConfigView
{
public SiteConfigView()
{
}
public int IdSiteConfig { get; set; }
public string Name { get; set; }
public byte[] SiteLogo { get; set; }
public string Brands { get; set; }
public string LinkColour { get; set; }
public IEnumerable<SiteBrandView> SiteBrands { get; set; }
}
And this is the query I am trying
var db = new SampleMVCEntities();
IQueryable<SiteConfig> test = db.SiteConfigs.Select(a => new SiteConfigView{Name = a.Name,LinkColour = a.LinkColour,SiteLogo = a.SiteLogo});
Can comebody guide me how to get the collection from SiteConfig to SiteConfigView.
Thanks

You're going on the right direction tried like this
var siteConfigs = db.SiteConfigs.AsEnumerable().Select(a => new SiteConfigView()
{
Name = a.Name,
LinkColour = a.LinkColour,
SiteLogo = a.SiteLogo,
SiteBrands = a.SiteBrands.AsEnumerable().Select(a => new SiteBrandView()
{
//Do the projection
}).ToList()
}).ToList();

Related

Lookup for an Entity Column ID

I have an Entity Framework Core project that uses generic repositories and UnitOfWork and is working as expected.
The database is one to many and related by IDs.
The RTCTrials entity contains a FK CourseID related to RTCCourses PK. When loading trials I am trying to get the course name in the datagrid and only achieved by using a union. Is this inefficient and a simpler approach. Ideally I would add a dropdownlist column populated with RTCCourses in the trials grid template and the CourseID in the trials table would select the correct id and show the ValueMember course name.
This is what the current method looks like:
using (var context = new RTCContext())
{
var factory = new EntityFrameworkUnitOfWorkFactory(context);
var unit = factory.Create();
var festivals = unit.RTCFestivals.All().ToList();
var trials = unit.RTCTrials.All().ToList();
var courses = unit.RTCCourses.All().ToList();
var trialcourses = trials.Join(courses, courses => courses.CourseID, trials => trials.CourseID, (trials, courses) => new
{
TrialID = trials.TrialID,
FestivalID = trials.FestivalID,
CourseID = trials.CourseID,
Trial = trials.Trial,
Course = courses.CourseName,
TrialGrade = trials.TrialGrade,
TrialDistance = trials.TrialDistance,
TrialAge = trials.TrialAge,
TrialHurdles = trials.TrialHurdles,
TrialAllowances = trials.TrialAllowances,
TrialMonth = trials.TrialMonth,
TrialActualDate = trials.TrialActualDate,
TrialActualTime = trials.TrialActualTime,
TrialRaceCard = trials.TrialRaceCard,
TrialQualifiers = trials.TrialQualifiers
}).ToList();
this.radGridViewFestivalDestinations.DataSource = festivals;
this.radGridViewFestivalDestinations.Templates[0].DataSource = trialcourses;
foreach (GridViewDataColumn column in radGridViewFestivalDestinations.MasterTemplate.Columns)
{
column.BestFit();
}
foreach (GridViewDataColumn column in radGridViewFestivalDestinations.Templates[0].Columns)
{
column.BestFit();
}
}
RTCTrial Entity
public partial class RTCTrial {
public RTCTrial()
{
this.RTCResults = new List<RTCResult>();
this.RTCWeathers = new List<RTCWeather>();
OnCreated();
}
public virtual int TrialID { get; set; }
public virtual int FestivalID { get; set; }
public virtual int CourseID { get; set; }
public virtual string Trial { get; set; }
public virtual string TrialGrade { get; set; }
public virtual string TrialDistance { get; set; }
public virtual string TrialAge { get; set; }
public virtual int? TrialHurdles { get; set; }
public virtual string TrialAllowances { get; set; }
public virtual string TrialMonth { get; set; }
public virtual DateTime? TrialActualDate { get; set; }
public virtual TimeSpan? TrialActualTime { get; set; }
public virtual string TrialRaceCard { get; set; }
public virtual int TrialQualifiers { get; set; }
public virtual RTCCourse RTCCourse { get; set; }
public virtual RTCFestival RTCFestival { get; set; }
public virtual IList<RTCResult> RTCResults { get; set; }
public virtual IList<RTCWeather> RTCWeathers { get; set; }
#region Extensibility Method Definitions
partial void OnCreated();
#endregion
}
RTCCourse Entity
public partial class RTCCourse {
public RTCCourse()
{
this.RTCTrials = new List<RTCTrial>();
OnCreated();
}
public virtual int CourseID { get; set; }
public virtual string CourseName { get; set; }
public virtual string CourseCountry { get; set; }
public virtual string CourseDirection { get; set; }
public virtual string CourseCharacteristics { get; set; }
public virtual string CourseAlternateName { get; set; }
public virtual double CourseLat { get; set; }
public virtual double CourseLong { get; set; }
public virtual IList<RTCTrial> RTCTrials { get; set; }
#region Extensibility Method Definitions
partial void OnCreated();
#endregion
}
Regards, Neil
Suggestion would be on the returned courses you would want each course to have its associated trials. In the unit of work that returns all courses - possibly have an option to include them. Your dropdown would bind to each course and your grid would bind to the list of trials in the selected course.
public IEnumerable<RTCCourse> All(bool includeTrials = false)
{
var q = context.RTCCourses;
if (includeTrials)
{
q = q.Include(c => c.RTCTrials)//.ThenInclude(t => t.RTCResults)
;
}
return q.AsEnumerable(); // assuming that is the returned type
}
That should allow your courses to have the list of trials set. Then there is no need to get all trials. And you can bind to courses (and list of trials within each) directly instead of doing the join and binding to the anonymous.
Of 'course' -- this is merely a suggestion ;)

MVC - Unable to set field/property on entity

I have 2 tables in my entity framework:
INATIVOS (Employees)
EMPRESAS (Companies)
When registering an employee I select a company in a #Html.DropDownListFor (List).
The registration is ok, the company is saved correctly. However, when trying to edit a registered employee shows the error "Unable to set field/property on entity" in the Companies list.
INATIVO.cs
public partial class INATIVOS
{
public decimal ID { get; set; }
public string COD_EMPRESA { get; set; }
public string CHAPA { get; set; }
public string NOME { get; set; }
public System.DateTime DATA_NASC { get; set; }
public string PLANO { get; set; }
public short LEI { get; set; }
public short APOSENTADO { get; set; }
public short ESTADO_VIDA { get; set; }
public short ISENTO { get; set; }
public Nullable<System.DateTime> INICIO_VIGENCIA { get; set; }
public Nullable<System.DateTime> FIM_VIGENCIA { get; set; }
public string CPF { get; set; }
public string EMAIL { get; set; }
public string ENDERECO { get; set; }
public string NUMERO { get; set; }
public string COMPLEMENTO { get; set; }
public string BAIRRO { get; set; }
public string CIDADE { get; set; }
public string ESTADO { get; set; }
public string CEP { get; set; }
public string TELEFONE { get; set; }
public string CELULAR { get; set; }
public string OBSERVACAO { get; set; }
public List<DEPENDENTES> DEPENDENTES { get; set; }
public List<EMPRESAS> EMPRESAS { get; set; }
public List<PLANOS_MEDICO> PLANOS_MEDICO { get; set; }
}
InativoController.cs
public ActionResult Index(int? id)
{
INATIVOS inaModel = new INATIVOS();
using (Entidades db = new Entidades())
{
if (id != null)
{
inaModel = db.INATIVOS.Where(x => x.ID == id).FirstOrDefault();
}
inaModel.EMPRESAS = db.EMPRESAS.ToList<EMPRESAS>();
inaModel.PLANOS_MEDICO = db.PLANOS_MEDICO.ToList<PLANOS_MEDICO>();
}
return View(inaModel);
}
If these are navigation properties:
public List<DEPENDENTES> DEPENDENTES { get; set; }
public List<EMPRESAS> EMPRESAS { get; set; }
public List<PLANOS_MEDICO> PLANOS_MEDICO { get; set; }
Then (1) they need to be virtual and (2) they need to be something like IList or ICollection:
public virtual ICollection<DEPENDENTES> DEPENDENTES { get; set; }
public virtual ICollection<EMPRESAS> EMPRESAS { get; set; }
public virtual ICollection<PLANOS_MEDICO> PLANOS_MEDICO { get; set; }
Though, as an aside, what you're doing here is very strange:
inaModel.EMPRESAS = db.EMPRESAS.ToList<EMPRESAS>();
inaModel.PLANOS_MEDICO = db.PLANOS_MEDICO.ToList<PLANOS_MEDICO>();
Essentially what you have in the database is, for a given Employee (INATIVOS) there are relationships to specific Companies (EMPRESAS) and specific Medical Plans (PLANOS_MEDICO). But you're ignoring whatever is in that data and replacing it with all companies and all medical plans in the entire database.
So every time you use this controller action to fetch an existing employee record, it's going to look like that employee has every company and every medical plan. Even though that's not what's in the database. I strongly suspect that's not what you want.
UPDATE: Based on comments on this answer, it sounds like those aren't navigation properties. They're not even properties of the model at all. They're just lists of data needed for the view to populate (presumably) <select> elements.
If they're not part of the data model then remove them from the model. Instead, consider using a view model. For example:
public class InativosViewModel
{
public INATIVOS Inativos { get; set; }
public List<EMPRESAS> EMPRESAS { get; set; }
public List<PLANOS_MEDICO> PLANOS_MEDICO { get; set; }
}
Then in the controller return an instance of the view model, which is a composite object of the model and the data needed for the view:
public ActionResult Index(int? id)
{
InativosViewModel result = new InativosViewModel();
using (Entidades db = new Entidades())
{
if (id != null)
{
result.Inativos = db.INATIVOS.Where(x => x.ID == id).FirstOrDefault();
}
result.EMPRESAS = db.EMPRESAS.ToList<EMPRESAS>();
result.PLANOS_MEDICO = db.PLANOS_MEDICO.ToList<PLANOS_MEDICO>();
}
return View(result);
}
And of course change the model binding in the view itself to now expect and use an instance of InativosViewModel. The resulting POST action can still accept an instance of INATIVOS if it needs to, or it can accept an instance of InativosViewModel just as well. That all depends on what the form structure is and what's being posted to that action.
Alternatively, if you want to keep using the INATIVOS model then still remove those lists from it but use something like ViewBag to send them to the view. Something like this:
public ActionResult Index(int? id)
{
INATIVOS inaModel = new INATIVOS();
using (Entidades db = new Entidades())
{
if (id != null)
{
inaModel = db.INATIVOS.Where(x => x.ID == id).FirstOrDefault();
}
ViewBag.Empresas = db.EMPRESAS.ToList<EMPRESAS>();
ViewBag.PlanosMedico = db.PLANOS_MEDICO.ToList<PLANOS_MEDICO>();
}
return View(inaModel);
}
Then in your view you would populate the <select> elements from there:
#Html.DropDownListFor(
model => Model.COD_EMPRESA,
new SelectList(ViewBag.Empresas, "CODIGO", "DESCRICAO"),
htmlAttributes: new { #class = "form-control"
})

Correct way to set up a 1 to Many So I can Query Attributes on the Included table with Entity Framework 6

I currently have a 2 table set up. I can include the Associated table with ease but I would like to be able to use a condition in the Where Clause and not sure where I need to change it. The relationship is a People can have Many Address
public partial class People
{
public People()
{
Address = new HashSet<Address>();
}
public int Id { get; set; }
public int Name { get; set; }
public virtual ICollection<Address> Address { get; set; }
}
public partial class Address
{
public int id { get; set; }
public string State { get; set; }
public int PeopleId { get; set; }
public People People { get; set; }
}
I can currently do this and it includes the Associated Data, but this returns more data than I need.
using (DBContextdb = new DBContext())
{
var oooo = db.People.IncludeOptimized(x => x.Address).ToListAsync();
}
I would like to do something along these lines but I need to get my relations set up correctly. This Doesn't actually let me select the property of State on the Address class.
using (DBContext db = new DBContext())
{
var oooo = db.People.IncludeOptimized(x => x.Address).Where(x => x.Address.State == "Florida").ToListAsync();
}
You can't access the 'state' Property because 'x.Address' is a collection.
Also the State-Property is of the type int?. But you try to compare it with the string "Florida". So you should also change that.
So if you need all Addresses which have the State 'Florida' you can use something like this:
A simplified Model class:
public class Model1
{
public IQueryable<People> People { get; set; }
public IQueryable<Address> Addresses { get; set; }
public Model1()
{
People = new List<People>().AsQueryable() ;
Addresses = new List<Address>().AsQueryable();
}
}
The new Address/People classes:
public partial class People
{
public People()
{
Address = new HashSet<Address>();
}
public int Id { get; set; }
public int Name { get; set; }
public virtual ICollection<Address> Address { get; set; }
}
public partial class Address
{
public int Id { get; set; }
public string State { get; set; }
public int PeopleId { get; set; }
public People People { get; set; }
}
And then you can use the following code:
Model1 model = new Model1();
var queryResult = model.Addresses.Where(a => a.State == "Florida");
EDIT
Here is the query you are looking for:
IQueryable<People> queryResult = model.Addresses.Where(a => a.State == "Florida").Select(a => a.People);

removing certain objects from list of objects in c#

i have a list of objects
public int id { get; set; }
public string device_code { get; set; }
public string device_type { get; set; }
public string authentication_token { get; set; }
public string Status { get; set; }
while returning the list i want to remove "device_code" and "device_type" from the list and return the list only with "id","authentication_token" and "status".
How can I delete certain objects?
You must cast your object to another type that will contain only needed properties.
You can do this easy with linq:
var result = yourCollection.Select(x => new YourTempClass(){property1=x.property1});
You seem to not want to remove objects, but properties of the objects.
public class ClassWithAllProperties
{
public int id { get; set; }
public string device_code { get; set; }
public string device_type { get; set; }
public string authentication_token { get; set; }
public string Status { get; set; }
}
var allInstances = new List<ClassWithAllProperties>();
// populate list
var allInstancesButNotAllProperties = allInstances.Select(x => new { id = x.id, authentication_token = x.authentication_token, Status = x.Status }).ToList();
Now this list contains only the properties you want. However it obviously also does not contain instances of ClassWithAllProperties. It contains so-called anonymous classes. Classes the compiler builds in the background for you, based on your description in the new.
It's simple, create another class containing properties that are required and then return its object using the list you have created.
Original Class
public class Data {
public int id { get; set; }
public string device_code { get; set; }
public string device_type { get; set; }
public string authentication_token { get; set; }
public string Status { get; set; }
}
Class That will be returned
public class DataTobeReturned {
public int id { get; set; }
public string authentication_token { get; set; }
public string Status { get; set; }
}
Suppose you have list like
List<Data> list = // some data;
You can do
List<DataTobeReturned> list2 = list.Select(x => new DataTobeReturned { x.id, x.Status, x.authentication_token}).ToList();
Simply return the list2 object.
If you have this class:
class MyClass
{
public int id { get; set; }
public string device_code { get; set; }
public string device_type { get; set; }
public string authentication_token { get; set; }
public string Status { get; set; }
}
...and you have a list of them...
List<MyClass> list;
You can extract just the properties you want into an anonymous type by using LINQ:
var justWhatIWant = list.Select( a => new
{
id = a.id,
authentication_token = a.authentication_token,
Status = a.Status
});
The anonymous type isn't interface-compatible with anything, but you could use it to, say, create some JSON.

Querying Child Collections 2 levels deep in LINQ

i currently have a linq to entities model set up as follows
each Sample has a collection Of Tests
each Test has a collection of Results
Each Result has Status property valuing whether it is Available or Completed
how would i write a linq query that would:
get the samples that have available Results
retaining only the tests that have available results
and only the results in each test that are available
having trouble getting my head around this problem and help with getting this
written would really help alot
Classes:
public class Sample
{
public Sample()
{
Tests = new List<Test>();
}
public int Id { get; set; }
public string IdText { get; set; }
public DateTime SampleDate { get; set; }
public DateTime LoginDate { get; set; }
public string Container { get; set; }
public string Product { get; set; }
public string Name { get; set; }
public string Status { get; set; }
public virtual SamplePoint SamplingPoint { get; set; }
public virtual SampleTemplate SampleTemplate { get; set; }
public virtual Customer ForCustomer { get; set; }
public virtual ICollection<Test> Tests { get; set; }
public class Test
{
public Test()
{
Results = new List<Result>();
}
public string Id { get; set; }
public string Status { get; set; }
public string Analysis { get; set; }
public string ComponentList { get; set; }
public virtual Instrument InstrumentUsed { get; set; }
public virtual ICollection<Result> Results { get; set; }
public virtual Sample ForSample { get; set; }
}
public class Result
{
public string Id { get; set; }
public string TestNumber { get; set; }
public string Status { get; set; }
public string Analysis { get; set; }
public string ComponentName { get; set; }
public string Text { get; set; }
public string Units { get; set; }
public double Value { get; set; }
public int OutOfRange { get; set; }
public DateTime SampledDate { get; set; }
public DateTime SampleLoginDate { get; set; }
public string SamplePoint { get; set; }
public virtual Sample ForSample { get; set; }
public virtual Test ForTest { get; set; }
}
If I understand your table structure then it's fairly easy to query down to get the results that you're interested in.
I put together a simple set of classes to test the results.
public static class db
{
public static List<Sample> Samples = new List<Sample>();
}
public class Sample
{
public string Name;
public List<Test> Tests = new List<Test>();
}
public class Test
{
public string Name;
public List<Result> Results = new List<Result>();
}
public class Result
{
public string Name;
public string Status;
}
And I created this set of test data:
From here it is easy to query the data down to just available results:
var query =
from s in db.Samples
from t in s.Tests
from r in t.Results
where r.Status == "Available"
select new { Sample = s.Name, Test = t.Name, Result = r };
Which gives me this data:
But that doesn't group the data by Sample and Test properly.
One way to do it properly is to create new Sample & Test objects that contain only the available results, like so:
var query =
from s in db.Samples
from rt in (
from t in s.Tests
from r in t.Results
where r.Status == "Available"
group r by t into rts
select new Test()
{
Name = rts.Key.Name,
Results = rts.ToList()
})
group rt by s into srts
select new Sample()
{
Name = srts.Key.Name,
Tests = srts.ToList()
};
This produces this result:
However, it might not be possible, or desirable, to create new instance of objects that look like actual entities but are not actually from the database. It might be possible to accidentally persist one of these objects back to the database and wipe out correct records!
So, an alternative, which I think is the best, is to create a nested structure that contains the unmodified database entities and includes the available tests in an extra properly all while keeping the nested structure!!
Here's how:
var query =
from s in db.Samples
from rt in
(from t in s.Tests
from r in t.Results
where r.Status == "Available"
group r by t into rts
select new
{
Test = rts.Key,
AvailableResults = rts.ToArray()
})
group rt by s into srts
select new
{
Sample = srts.Key,
AvailableTests = srts.ToArray()
};
And this produces:
With these results you still have access to the unchanged Sample and Test objects, but all filtered by the available results.
Let me know if this helps.
Without seeing your actual class structure, I'm hoping this can help in some way:
var testsWithAvailableResults = from test in dbContext.Tests
select new {
Results = (from result in test.Results where result.Status == "Available")
};

Categories