I have the following 3 tables ( 1 base table and other 2 sattelite tables for each vehicle type.)
Vehicles
ID VehicleType
-----------------------------
1 Car
2 Truck
Cars
ID Make Model
-------------------------
1 Toyota Camry
2 Honda Accord
Trucks
ID Make Model
--------------------
1 Ford F150
2 Dodge Ram
Then i have corresponding DTO
public class VehicleDTO
{
public int ID {get;set;}
public int VehicleType {get;set;}
public IEnumerable<CarDTO> Cars {get;set;}
public IEnumerable<TruckDTO> Trucks {get;set;}
}
public class CarDTO
{
public int ID {get;set;}
public string Make {get;set;}
public string Model {get;set;}
}
public class TruckDTO
{
public int ID {get;set;}
public string Make {get;set;}
public string Model {get;set;}
}
Then i have list of Vehicle DTO as an argument to the method. I want find vehicles from DTO list that does not exists in the database by matching Make and Model for that Vehicle Type.
Idea is to then insert the missing vehicles into database.
I have the following query
public void FindMissingVehicles(IEnumerable<VehicleDTO> dtos)
{
var cars = (from dto in dtos
where !(from c in dbcontext.Cars
select new { c.Make, c.Model })
.Any(x => dto.VehicleType == 'Car' && dto.Car.Make == x.Make && dto.Car.Model == x.Model)
select dto).ToList();
var trucs = (from dto in dtos
where !(from t in dbcontext.Trucks
select new { t.Make, t.Model })
.Any(x => dto.VehicleType == 'Truck' && dto.Truck.Make == x.Make && dto.Truck.Model == x.Model)
select dto).ToList();
//insert missing cars and trucks into db here
}
The query above throws exception
Message "Non-static method requires a target." string
Questions
1> How do i construct this query.
2> Can i make this query async by using AnyAsync and ToListAsync. (I know i have to make method async with Task, and use await inside howevere i could not figure out the async query syntax)
Moreover, your approach has performance issue - you perform N queries - one for each of dto, instead of doing only two queries: one for cars and one for trucks:
var allCars = dtos.Where(x => x.VehicleType == "Car").ToList()
.SelectMany(x => x.Cars.Select(y => y.Make + "-" + y.Model).ToList()).ToList();
var existedCars = await dbcontext.Cars.Where(x => allCars.Contains(x.Make + "-" + x.Model))
.Select(x => x.Make + "-" + x.Model).ToListAsync();
var newCars = allCars.Except(existedCars).Select(x =>
{
var temp = x.Split('-');
return new CarDTO
{
Make = temp[0],
Model = temp[1]
};
}).ToList();
//exactly same code for Trucks
This is because of you can't have nested query in linq when one table is from dbContext and the other one is from in-memory enumerable, so if dbcontext.Cars and dbcontext.Trucks don't have a lot of rows, it's a good idea to load them in memory and use nested query like below:
var listCars = dbcontext.Cars.ToList();
var listTrucks = dbcontext.Trucks.ToList();
var cars = (from dto in dtos
where !(from c in listCars
select new { c.Make, c.Model })
.Any(x => dto.VehicleType == 'Car' && dto.Car.Make == x.Make && dto.Car.Model == x.Model)
select dto).ToList();
var trucs = (from dto in dtos
where !(from t in listTrucks
select new { t.Make, t.Model })
.Any(x => dto.VehicleType == 'Truck' && dto.Truck.Make == x.Make && dto.Truck.Model == x.Model)
select dto).ToList();
Related
I have two object tables and a joining table like so:
public class Item
{
public int ItemID
public ICollection<ItemAttribute> ItemAttributes
}
public class Attribute
{
public int AttributeID
public ICollection<ItemAttribute> ItemAttributes
public AttributeType Type
public int ValueID
}
public class ItemAttribute
{
public int ItemID;
public Item Item
public int AttributeID;
public Attribute Attribute
public int? Order
}
In my OnCreateModelBuilding function, I have the following:
modelBuilder.Entity<Item>()
.HasKey(i => i.ItemID);
modelBuilder.Entity<Attribute>()
.HasKey(a => a.AttributeID);
modelBuilder.Entity<ItemAttribute>()
.HasKey(ia => new { ia.ItemID, ia.AttributeID });
modelBuilder.Entity<ItemAttribute>()
.HasOne<Item>(ia => ia.Item)
.WithMany(i => i.ItemAttributes)
.HasForeignKey(ia => ia.ItemID);
modelBuilder.Entity<ItemAttribute>()
.HasOne<Attribute>(ia => ia.Attribute)
.WithMany(a => a.ItemAttributes)
.HasForeignKey(ia => ia.AttributeID);
While I know most joining tables I see in examples and tutorials do not have an Order column, I would like to have one. Here's an example of what I would be trying to do.
Imagine that I saved an Item of Class Picture and I wanted to list in order (from left to right) the students that were in that picture. Here's what I'm thinking as far as the tables:
Items
ItemID
1
Attributes
AttributeID Type ValueID
1 Student 1
2 Student 2
3 Student 3
ItemAttribute
ItemID AttributeID Order
1 3 1
1 1 2
1 2 3
In this example, the ValueID is going to point be used to pull the Student information from the StudentRepository. Ideally, I'd like to add the Attributes to the Item and not the other way around. Hence I have this function in the Item class:
public bool AddAttribute(Attribute attribute, int? order)
{
ItemAttribute itemAttribute = new ItemAttribute() { Item = this, Attribute = attribute, Order = order != null ? order : null };
if (ItemAttributes.Contains(itemAttribute))
return false;
ItemAttributes.Add(itemAttribute);
return ItemAttributes.Contains(itemAttribute);
}
Is this the best way to handle this?
In the entity framework, I have a main class with ICollection definitions for 2 sub classes.
public partial class Class1{
public virtual ICollection<Class2> Class2 {get;set;}
public virtual ICollection<Class3> Class3 {get;set;}
}
public partial class Class2 : ITotal {
public double Total {get;}
}
public partial class Class3 {
}
Class2 implements the ITotal interface...Class3 does not.
In total Class1 has about 30 instances of ICollections where the base object implements the ITotal interface. It also has 10+ ICollections that do not implement the interface.
Within Class1, I need to be able to dynamically get all ICollections whose base type implement the ITotal interface. Then I will add the "Total" fields to get an overall total. The reason I need it to be dynamic is because I will be adding many more ICollections to class1 and I don't want/need to have to remember to go to multiple places to get the totals to be accurate.
Below is a sample of what I have so far...this bit of code gives me all the ICollection classes, but now I am stuck. Ideally, I could add another where clause after the last select, but I am open to scrapping it altogether.
var value1 = t.GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance)
.Where(x => x.CanWrite && x.GetGetMethod().IsVirtual)
.Select(x => x.GetValue(this, null))
.Where(x => x != null)
.Where(x => x.GetType().GetInterfaces().Any(y => y.IsGenericType && y.GetGenericTypeDefinition() == typeof(ICollection<>)))
.Select(x=> ((IEnumerable)x))
.ToList()
;
Any thoughts?
Like this:
var c = new Class1();
//. . .
var q = from p in typeof(Class1).GetProperties()
where p.PropertyType.IsConstructedGenericType
&& p.PropertyType.GetGenericTypeDefinition().Equals(typeof(ICollection<>))
&& typeof(ITotal).IsAssignableFrom(p.PropertyType.GetGenericArguments()[0])
select p;
var ITotalCollections = q.ToList();
var q2 = from p in ITotalCollections
from i in (IEnumerable<ITotal>)p.GetValue(c)
select i.Total;
var total = q2.Sum();
I'm using EF6
this is my class
public partial class tbl_Persons
{
public int ID { get; set; }
public string Name { get; set; }
public byte Age { get; set; }
}
How much (bytes) will be transferred from the server to the client when used this code ?
using (var db = new testEntities())
{
var q = db.tbl_Persons.FirstOrDefault(a => a.ID == 1234).Age;
}
Just transferred {(Age)(1 byte)} or transferred all properties {(ID + Name + Age)(10 bytes)} then select Age on client ?
How can I transfer only (Age)(1 byte) ? (I need to transfer minimum data from server)
The expression
var q = db.tbl_Persons.FirstOrDefault(a => a.ID == 1234).Age;
is equivalent of
var person = db.tbl_Persons.FirstOrDefault(a => a.ID == 1234);
var age = person.Age;
So you first retrieve and materialize a whole object (with all properties) from the database, and then take a single property (byte in your case) from the result.
In order to fetch just the property in question, you should use a not so concise, but more efficient Where + Select + FirstOrDefault (no predicate version):
var age = db.tbl_Persons.Where(p => p.ID == 1234).Select(p => p.Age).FirstOrDefault();
or with query syntax
var age = (from p in db.tbl_Persons where p.ID == 1234 select p.Age).FirstOrDefault();
Most of my entities (not all) have two properties called CompanyId and Deleted. How would be possible to auto insert these two properties for all select requests instead of setting manually on every single query I have along the whole app.
Example:
db.MyEntity.Where(me => me.Id == 1).Select(me => me.Description)
Check dynamically it the entity has the props CompanyId and Deleted.
Case affirmative, transform it like this
db.MyEntity.Where(me => me.Id == 1 && Deleted == false && CompanyId == 1).Select(me => me.Description)
Case negative, keep the same select query.
It would help me having to set these conditions to all my queries in which has the properties available.
You can add filter with the help of nuget package: EntityFramework.Filters. Also, good idea is to create common interface instead of dynamically checking for properties existence:
public interface IMyEntityInterface
{
public int Id {get;set;}
public bool Deleted {get;set;}
}
public class MyEntity : IMyEntityInterface
{
public int Id {get;set;}
public bool Deleted {get;set;}
//other stuff
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions
.Add(FilterConvention.Create<IMyEntityInterface, int, bool>("MyFilter", (entity, Id, Deleted) => entity.Id == Id && entity.Deleted == Deleted);
}
Usage:
var filter = db.EnableFilter("MyFilter");
filter.SetParameter("Id", 1);
filter.SetParameter("Deleted", false);
var data = db.MyEntity.Where(me => me.CompanyId == 1).Select(me => me.Description);
//select Description from MyEntities where Id = 1 and Deleted = false and CompanyId = 1
I have the following classes:
public class Customer {
public int Id {get;set;}
public string Name {get;set;}
public List<Order> Orders {get;set;}
//other attributes
}
public class Order{
public int Id {get;set;}
public string Name {get;set;}
public decimal Value {get;set;}
}
Given a customerId I wish to only select the customer name and the order Id using projection in EF.
I am doing the following:
IQueryable<Customer> customer = DataContextFactory.GetDataContext().Set<Customer>();
var tempCustomer = customer.Where(x => x.Id == customerId).Select( c=>
new
{
Name = c.Name
}
)
This gives me the customer name. Which I can then pass back to the entity like so:
var customerToReturn = tempCustomer.ToList().Select(x => new Customer
{ Name = x.Name});
If I include the order on the query like this:
var tempCustomer = customer.Where(x => x.Id == customerId).Select( c=>
new
{
Name = c.Name,
Orders = new {
Id = c.Orders.Id
}
}
)
Then I get a duplicate customer per order line (as per the SQL generated by EF). Is there a way I can inline this generation into a single SQL call?
Currently I am getting around this by calling each child object separately.