Linq - Create new object which contains a collection - entity-framework

I am trying to get data from Entity Framework using linq to populate a custom object. The problem is the object contains a list collection where I am having problems populating.
I have the following
public class Person
{
public string FirstName { get; set; }
public string ListName { get; set; }
public IList<Address> ContactAddresses v{ get; set; }
}
public class Address
{
public string FirstLine { get; set; }
public string SecondLine { get; set; }
public string Town { get; set; }
}
I am trying to populate the Person object but having trouble populating the list of addresses. The situation I have at the moment one address is stored in the Person table and work and other addresses are stored in another. Right now I am only trying to get the address stored in the Person table. Below is my attempt at this but syntax is not working.
Person details = context.Person.Where(p => p.Id == id)
.Select(p => new Person)
{
FirstName = p.FirstName,
LastName = p.LastName,
ContactAddresses = new List<Address>()
.add(new Address
(
FirstLine = m.FirstLineAddress,
SecondLine = m.SecondLine,
Town = m.Town
))
}.FirstOrDefault();
UPDATE:
I worked out how to do this. The following linq is my solution.
Person details = context.Person.Where(p => p.Id == id)
.Select(p => new Person)
{
FirstName = p.FirstName,
LastName = p.LastName,
ContactAddresses = (new List<Address>()
{
new Address()
{
FirstLine = p.FirstLine ,
SecondLine = p.SecondLine,
Town = p.Town
}
}).ToList(),
}.FirstOrDefault();

This is not really a problem of LINQ. It is more about object initialization. First rule (for me) if an object has a collection, it should never be null. It can be empty, but never null. (Another question would be, if a collection property should be settable or not, but that's another story.)
public class Person
{
public string FirstName { get; set; }
public string ListName { get; set; }
public IList<Address> ContactAddresses { get; set; } = new List<Address>();
}
And then you can initialize the object like this:
var person = new Person
{
FirstName = "Foo",
ListName = "Bar",
ContactAddresses = { new Address { FirstLine = "A", SecondLine = "B", Town = "C" } }
};
As you can see, the contact addresses has an initializer that doesn't use new. So the list of objects will be added to the existing list.
If you want to replace the list instead of changing it, you can also provide a new list:
var person = new Person
{
FirstName = "Foo",
ListName = "Bar",
ContactAddresses = new List<Address> { new Address { FirstLine = "A", SecondLine = "B", Town = "C" } }
};
More about object initialization can be found at Microsoft.

You wrote:
The situation I have at the moment one address is stored in the Person table and work and other addresses are stored in another.
So you have a class Person, which has a contact Address, and also a collection of zero or more alternative Addresses. This might be a one-to-many relation, or a many-to-many relation, for a Person class this does not really matter:
class Person
{
public int Id {get; set;}
public Address MainAddress {get; set;}
... // other properties
// every Person has zero or more Alternative Addresses:
public virtual ICollection<AlternativeAddress> AlternativeAddresses {get; set;}
}
class AlternativeAddress
{
public int Id {get; set;}
public Address Address {get; set;}
... // other properties
// if this Address belongs to exactly one Person (one-to-many)
public int PersonId {get; set;}
public virtual Person Person {get; set;}
// or if this address may be the address of several Persons (many-to-many)
public virtual ICollection<Person> Persons {get; set;}
}
In entity framework the columns of a table are represented by the non-virtual properties. The virtual properties represent the relations between the tables (one-to-many, many-to-many, ...)
Address is a class. Because I didn't declare MainAddress virtual, the properties of Address will be columns within Person. Similarly, the properties of the Address in AlternativeAddress will be columns within an AlternativeAddress.
If you didn't use the Class Address to make sure that your MainAddress has exactly the same structure as the AlternativeAddresses you'll have to do a Select.
This is all that entity framework needs to know to detect your tables and the relations between the tables, the primary and foreign keys. Only if you want different identifiers you need attributes or fluent API.
Requirement Given an input parameter personId, Give me (some properties of) the Person with all his Addresses.
var result = dbContext.Persons
.Where(person => person.Id == personId)
.Select(person => new
{
// select only the properties you plan to use:
Id = person.Id,
FirstName = person.FirstName,
...
// For the contact Addresses, concat the MainAddress and the Alternative Addresses
ContactAddresses = new Address[] {person.MainAddress}
.Concat(person.AlternativeAddresses.Select(address => address.Address)
// use a Select if you don't need all Address properties:
.Select(address => new
{
Street = address.Street,
City = address.City,
PostCode = address.PostCode,
...
});
If classes Person and AlternativeAddress don't have an Address Properties, you'll have to select the properties you want:
ContactAddresses =
// Main Address
new []
{
Street = person.Street,
City = person.City,
PostCode = person.PostCode,
...
}
.Concat(person.AlternativeAddresses.Select(address => new
{
Street = address.Street,
City = address.City,
PostCode = address.PostCode,
...
}),

Related

Linq query to join three tables and return a object along with a list of another object in Entity Framework Core

I have three tables, Organization, Department, and OrganizationDepartments. here is the relationship between them.
Now I would like to join these three tables and create another object for a DTO class. This DTO object has some properties and a list of other DTOs. Here is the DTO Class.
Organization DTO:
public class OrganizationDto
{
public string Id { get; set; }
public string OrganizationName { get; set; }
public string Logo { get; set; }
public bool? IsActive { get; set; }
public IList<OrganizationDepartmentDto> OrganizationDepartments { get; set; }
}
OrganizationDepartment DTO:
public class OrganizationDepartmentDto
{
public string OrganizationId { get; set; }
public string OrganizationName { get; set; }
public string DepartmentId { get; set; }
public string DepartmentName { get; set; }
}
Now I would like to write a LINQ query to get a Organization object along with all the departments related to that organization. The query is imcomplete because I don't know how can I get all the department information as list in a single query. The code is below:
var organizationInfo = (from org in _dbContext.Organizations
join orgDept in _dbContext.OrganizationDepartments on org.Id equals orgDept.OrganizationId
join dept in _dbContext.Departments on orgDept.DepartmentId equals dept.Id
where org.Id.ToUpper() == id.ToUpper()
orderby org.CreatedOn ascending
select new OrganizationDto
{
Id = org.Id,
OrganizationName = org.OrganizationName,
Logo = org.Logo,
IsActive = org.IsActive,
OrganizationDepartments = //TODO:..
}
);
Can anyone help me to get the department lists of that organization's object (see the TODO:)?
If your entities are mapped correctly, and the relationships are correctly configured.
you can use .Include("OrganizationDepartment") and .ThenInclude("Department")to ensure relations are included into the generated Query.
If you insist on using Query Syntax. e.g from org in context.Organization
you can write out the query like this.
var q = (from org in _dbContext.Organizations
where org.Id.ToUpper() == id.ToUpper()
orderby org.CreatedOn ascending
select new OrganizationDto
{
Id = org.Id,
OrganizationName = org.OrganizationName,
Logo = org.Logo,
IsActive = org.IsActive,
OrganizationDepartments = org.OrganizationDepartments.ToList()
}
Depending on your usecase. Sometimes you are not interested in actually showing the "many to many" table outside of the scope of your database.
so it might make more sense to actually flatten the Dto.
that query would look like
var q = (from org in _dbContext.Organizations
where org.Id.ToUpper() == id.ToUpper()
orderby org.CreatedOn ascending
select new OrganizationDto
{
Id = org.Id,
OrganizationName = org.OrganizationName,
Logo = org.Logo,
IsActive = org.IsActive,
Departments= org.OrganizationDepartments.Select(t => t.Departments).ToList()
}

How to not assign an id when id is a fk with a custom id generator in ef

I have a project where I'm using EF5, I made a custom Guid Generator and I have an override of the SaveChanges method to assign the ids of my entities.
Everything is working fine except in one case: when the ID of one entity is a FK to another ID of another entity.
A little bit of code to explain the problem:
I have two entities I cannot change:
public class FixedEntityA
{
public Guid Id { get; set;}
public string SomeText { get; set; }
}
public class FixedEntityB
{
public Guid Id { get; set;}
public int OneInt { get; set; }
}
In my project I have an entity defined like this:
public class ComposedEntity
{
public Guid Id { get; set;}
public FixedEntityA FixedA { get; set; }
public FixedEntityB FixedB { get; set; }
public double OneDouble { get; set; }
}
The relationships are:
ComposedEntity may have 0 or 1 FixedEntityA
ComposedEntity may have 0 or 1 FixedEntityB
The constraints on the id are:
The Id of FixedEntityA is a FK pointing to the Id of ComposedEntity
The Id of FixedEntityB is a FK pointing to the Id of ComposedEntity
The mapping class are:
public ComposedEntity(): EntityTypeConfiguration<ComposedEntity>
{
HasOptional(fea => fea.FixedA).WithRequired();
HasOptional(feb => feb.FixedB).WithRequired();
}
Here is my SaveChanges override:
foreach (var entry in ChangeTracker.Entries<IEntity>().Where(e => e.State == EntityState.Added))
{
Type t = entry.Entity.GetType();
List<DatabaseGeneratedAttribute> info = t.GetProperty("Id")
.GetCustomAttributes(typeof (DatabaseGeneratedAttribute), true)
.Cast<DatabaseGeneratedAttribute>().ToList();
if (!info.Any() || info.Single().DatabaseGeneratedOption != DatabaseGeneratedOption.Identity)
{
if (entry.Entity.Id == Guid.Empty)
entry.Entity.Id = (Guid) _idGenerator.Generate();
}
}
return base.SaveChanges();
This code works fine everywhere for all kind of relationships except in this case, I am missing a test to make sure I'am not setting an id on id that are foreign keys, and I have no clue on how to check if an Id is a FK...
Here is a sample object where this code fails:
var fea = new FixedEntityA();
var feb = new FixedEntityB();
var composedEntity = new ComposedEntity();
composedEntity.FixedA = fea;
composedEntity.FixedB = feb;
If you insert the whole graph, all three objects are marked as Added and all Ids are default.
The problem is, with the current SaveChanges method, I will go through all object with the Added state in the change tracker and I will assign an Id to all entity with a default Guid and break my FK constraints.
Thanks in advance guys!
Here is some code that will get the FK properties for a given type (it's horrible I know). Should be simple enough to plug this into your code.
var typeName = "Category";
var fkProperties = ((IObjectContextAdapter)db)
.ObjectContext
.MetadataWorkspace
.GetItems<AssociationType>(DataSpace.CSpace)
.Where(a => a.IsForeignKey)
.Select(a => a.ReferentialConstraints.Single())
.Where(c => c.FromRole.GetEntityType().Name == typeName)
.SelectMany(c => c.FromProperties)
.Select(p => p.Name);

Searching the Entity Framework domain model utilising Code First

Got a very difficult EntityFramework Code First question. I'll keep this as simple as possible.
Imagine we have n number of classes, lets start with 2 for now
public class Person
{
public string Name { get; set; }
}
public class Address
{
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
}
Now then, what I want to do is be able to search the domain model with a single string, i.e. something like DbContext.Search( "Foo" ). The call would search both the person and address tables for a string match and would return a list populated with both Person and Address entities.
Have to say I am not entirely clear how to go about it but I am considering using DataAnnotations to do something like this
public class Person
{
**[Searchable]**
public string Name { get; set; }
}
public class Address
{
**[Searchable]**
public string AddressLine1 { get; set; }
**[Searchable]**
public string AddressLine2 { get; set; }
}
Am I on the right track?
Should I use the Fluent API instead?
Reflection?
Any and all thoughts massively appreciated.
the Find method searches only in the Primary Key column. If we don't make any column explicitly primary key column then find method will throw error. Generally EF convention takes propertyName+id as the primary key in the class. But if you want to search with Name then Make add [Key] to the property. it will become primary key and u will be able to find properties.
dbContext.Addresses.find("Foo");
Create a new object type onto which you'll project 2 types of search results:
public class Result
{
public string MainField { get; set; }
// you may have other properties in here.
}
Then find entities of each type that match your criteria, projecting them onto this type:
var personResults = DbContext.Persons
.Where(p => p.Name == "Foo")
.Select(p => new Result{MainField = p.Name});
// don't forget to map to any other properties you have in Result as well
var addressResults = DbContext.Adresses
.Where(a =>
a.AddressLine1 == "Foo" ||
a.AddressLine2 == "Foo"
).
.Select(a => new Result{MainField = a.AddressLine1 + ", " + a.AddressLine2 });
// again, don't forget to map to any other properties in Result
Then merge the lists:
var allResults = personResults.Union(addressResults).ToList();
...at which point you can sort the list however you like.
"Result" and "MainField", are rather generic; just using them because I am not thoroughly aware of your domain model.

LINQ/EF: conceptual/mechanics of doing multiple joins that return both an entity and a computed value

Im hoping someone can help me with a brain block I'm having about how to do multiple joins with EF that returns an entity, where one of the fields is calculated by a query. Ive contrived a (pretty much useless) example in the hopes somebody can help me understand how this should be done.
Id like to return a list of ISPs entities from a DBContext with the "TotalUsed" property filled in by the LINQ query. Ive successfully done the joins (trivial), and have played with grouping and sum, but cant seem to get it quite right.
The general idea to this contrived example is that NICs are connected to one and only one router, and multiple routers are connected to a single ISP each.
How to write a LINQ query that sums the bandwidth needed for each ISP, along with the other properties in that ISP?
Expected output would be a list of ISP that might look like
{{1, "foo", 52}, {2, "bar", 345}, {3, "foobar", 621}}, where the 3rd property is the summation of the BandwidthNeeded property on all NICs transitively related to ISP.
The classes:
public class ISP
{
public int ISPid {get; set;}
public int ISPName {get; set;}
public int TotalUsed {get; set;} // not mapped to DB -> should populate via LINQ
}
public class Router
{
public int RouterId {get; set;}
public string RouterName {get; set;}
public int ISPId {get; set;} // foreign key for ISP
}
public class Nic
{
public int NicId { get; set; }
public string NicLocation { get; set; }
public int BandwidthUsed { get; set; }
public int RouterId {get; set; } // foreign key for router
}
If you move the TotalUsed property into a separate type, you could do this (please excuse any typos):
public class ISP // mapped
{
public int ISPid {get; set;}
public int ISPName {get; set;}
}
public class ISPWithTotalUsed : ISP // not mapped
{
public int TotalUsed { get; set; }
}
var query = (from ISP in context.ISPs
select new ISPWithTotalUsed
{
ISPid = ISP.ISPid,
ISPName = ISP.ISPName,
TotalUsed = (from router in context.Routers
where router.ISPid == ISP.ISPid
from nic in context.Nics
where nic.RouterId == router.RouterId
select nic.BandwidthUsed).Sum()
});
If you add navigation properties, it could be made somewhat more readable by removing the links on ID.
var context = new IspContext();
var groups = context.Nics.Include("Router.Isp").GroupBy(n => n.Router.ISPId).ToList();
var result = groups.Select(g => new()
{
Key = g.Key,
Name = g.FirstOrDefault().Router.Isp.IspName,
Total = g.Sum(n => n.BandwithUsed)
});
Does this work for you? The name thing is not pretty and you might get it all into one query. It's hard to tell if it runs faster though.
Something like this should work:
var query = context.ISPS.Select(isp => new{ ISP : isp, Used : isp.Routes.Sum(r => r.Nics.Sum(n => n.BandwidthUsed))}).ToList();
var result = query.Select(item => {
item.ISP.TotalUsed = item.Used;
return item.ISP;
}).ToList();
I would hoverwer remove the TotalUsed propery from ISP and instead create a wrapper class that holds both an ISP and the TotalUsed. Then you could remove the second query. (EF will not let you contruct an entity inside an entity query).

Parse string to poco class enum in linq query MVC 2.0

I have the following enum and POCO class
public enum Gender
{
Male,
Female,
Unknown
}
public class Person
{
public int PersonId { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public Gender? Gender { get; set; }
}
I would like to perform a "get all people" query in my repository such that it would look something like this:
return from p in _db.People
select new Model.Person
{
PersonId = p.PersonId,
LastName = p.LastName,
FirstName = p.FirstName,
Gender = p.Gender,
};
Unfortunately I get an error "Cannot implicitly convert type 'string' to 'Model.Gender'"
I would like to convert the string which is being queried from the entity framework to my Gender enum and assign it to my POCO class.
Enums are not supported in Entity Framework. There is a workaround by Alex James, but it's quite involved.
Instead, i prefer to do this:
public enum Gender : byte
{
Male = 1,
Female,
Unknown
}
public class Person
{
public int PersonId { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public byte Gender { get; set; } // this is the EF model property
public Gender GenderType // this is an additional custom property
{
get { return (Gender) Gender; }
set { Gender = (byte)value; }
}
}
It's basically a hook/wrapper for the actual value. In your database, store Gender as a tinyint (which maps to byte on the conceptual side).
Then you can use a byte enum to map to and from the model property:
return from p in _db.People
select new Model.Person
{
PersonId = p.PersonId,
LastName = p.LastName,
FirstName = p.FirstName,
Gender = p.Gender, // sets byte
};
But then if you access that ViewModel, because your setting the byte field for Gender, you will also have access to the enum property GenderType.
Does that solve your problem?
The Entity Framework that I am familiar with does not provide support for enums. EF uses your query expression to create an SQL statement that it then sends to the server, if it cannot create the SQL equivalent of some operation it will throw a NotSupportedException for that operation. If you are expecting to return a small set of data you can separate from the Entity Framework by creating an object in memory using the ToArray method.
var myEntities = (from entity in _db.Entities
where /* condition */
select entity)
.ToArray();
This will create a sequence of entities in memory. Any further query statements will then be in the realm of LINQ to Objects which allows parsing of strings into enums:
return from myEntity in myEntities
select new MyDataContract
{
ID = myEntity.ID,
Gender g = (Gender)Enum.Parse(typeof(Gender), myEntity.Gender, true)
};
Or you could even break it out into a foreach loop:
List<MyDataContract> myDataContracts = new List<MyDataContract>();
foreach (var myEntity in myEntities)
{
var dataContract = new MyDataContract { ID = myEntity.ID };
if (Enum.IsDefined(typeof(Gender), myEntity.Gender))
dataContract.Gender = (Gender)Enum.Parse(typeof(Gender), myEntity.Gender, true);
myDataContracts.Add(dataContract);
}
return myDataContracts.AsEnumerable();
if (Enum.IsDefined(typeof(Gender), genderstring))
Gender g = (Gender) Enum.Parse(typeof(Gender), genderstring, true);
else
//Deal with invalid string.
try
Gender = p.Gender != null ? (Gender)Enum.Parse(typeof(Gender), p.Gender) : (Gender?)null;
To parse the string as one of the enums
here's a workaround but it means changing your nice and clean POCO
http://blogs.msdn.com/b/alexj/archive/2009/06/05/tip-23-how-to-fake-enums-in-ef-4.aspx