I have the following code which works and gets me the data for a single entity.
LoadOperation<TimeForm> loadoperation = _cc.Load(_cc.GetTimeFormsQuery()
.Where(o => o.Start>= weekrange.startdate
&& o.End<= weekrange.enddate
&& o.USERID== "TEST"));
However I have 3 tables which connect to this TimeForm table, in sql my query looks like this:
SELECT T* FROM TimeForm
INNER JOIN CLIENT ON TimeForm.CODEID= CLIENT.CODEID
INNER JOIN RATE ON TimeForm.RATEID= RATE.RATEID
INNER JOIN TASK ON TimeForm.TASKID = TASK.TASKID
How can this be possible with the above syntax? I need some values from these tables.
Try something like this:
var query = context.TimeForm.
Join(context.CLIENT,
t => t.CODEID, c => c.CODEID ,
(t, c) => new
{
PropertyA = t.ColumnA,
PropertyB = c.ColumnB
}).Join(context.RATE,
b => b.RATEID, r => r.RATEID,
(b, r) => new
{
PropertyC = b.ColumnC,
PropertyD = r.ColumnD
}).Join(context.TASK,
x => x.TASKID, t => t.TASKID,
(x,t) => new
{
PropertyE = x.ColumnE,
PropertyF = t.ColumnF
});
PropertyA, B, etc are just properties present in the type, which you use to store the data returned from the query. Whereas ColumnA, B, etc are columns present in the tables involved in the join. You can substitute actual values for these in your query.
You need to go to the Domain Services file (where the GetTimeFormsQuery() is defined). It'll look something like:
public IQueryable<TimeForm> GetTimeForms() {
return this.Context.TimeForm;
}
, and add to it so it is like this:
public IQueryable<TimeForm> GetTimeForms() {
return this.Context.TimeForm
.Include("Client") // Assuming your property to see the client is called "Client"
.Include("Rate") // Same for "Rate"
.Include("Task"); // and "Task
}
Or whatever the navigation properties are called in your TimeFrom entity.
Silverlight doesn't do lazy loading, so you'll have to explicitly include these properties in the query in the domain service. Also it's probably wise to create an extra method on the domain service that accepts the start and end date and userid so that you don't pull the entire table over the wire every time.
public IQueryable<TimeForm> GetTimeFormsWithStartAndEnd(DateTime start, DateTime end, string userId) {
return this.Context.TimeForm
.Include("Client") // Assuming your property to see the client is called "Client"
.Include("Rate") // Same for "Rate"
.Include("Task") // and "Task
.Where(o => o.Start>= start
&& o.End<= end
&& o.USERID== userId));
}
After you rebuild your web-project, you'll have a method called GetTimeFormsWithStartAndEndQuery in your silverlight with these 3 as parameters.
Goodluck!
Related
I have a question about selecting specific columns from table using entity framework. The problem is, that I'm using Find() method to get my desired table, by primary key, then taking from it some data.
I have one table with massive amounts of columns and if I call Find() method, it will return all columns of that row, but I want to use only, for example, the data from 2 columns.
MyTable table = context.MyTable.Find(id); //Get MyTable object from context, id = primary key
string p1 = table.Prop1;
string p2 = table.Prop2;
This will return single object with all (for example it has Prop1, Prop2,...,PropN) properties filled (if its filled in database).
So I know that I can use anonymous objects or data transfer objects (DTO), but [question1] is there any other (yet simple) method to get specific columns? [question2] Is it affecting on performance if I use Find() (or I should use Where()/Select())?
var items = context.MyTable.Where(x => x.Id == id)
.Select(x => new
{
P1 = table.Prop1,
P2 = table.Prop2
});
This will translate into a sql call like:
SELECT p.Prop1, p.Prop2 FROM mytable p WHERE p.Id = id
Use Data Transfer Objects: DTO, which is a recommened microsoft pattern.
Putting it simple, they are just objects that hold data.
Then do like someone suggested:
public class MyDto
{
public string Prop1 {get;set;} = String.Empty
public string Prop2 {get;set;} = String.Empty
}
MyDto x = new MyDto();
x = context.MyTable.Where(x => x.Id == id)
.Select(x => new MyDto
{
P1 = table.Prop1
//I don't want prop 2, for example
});
And pass around the object. Set defaults for Auto Properties (C# 6 and up) and initialize only the properties you want.
EDIT:
I've read you don't want to use anonymous and DTO, then how you want to do it. You either use objects or anonymous.
Other ways is just build a layered structure and call the query method directly where you need it. Patterns exists for a reason.
You can call queries against Dynamic objects. With these you may assign fields that will be resolved at runtime, at the cost of losing strong typing.
You might also want to check if it's performance-whorty to use dynamics.
Another option is to project the class back to itself, and only provide the columns you want.
var table = context.MyTable.Where(mt => mt.Id == id)
.Select(mt => new MyTable
{
Prop1 = mt.Prop1,
Prop2 = mt.Prop2
})
.FirstOrDefault();
string p1 = table.Prop1;
string p2 = table.Prop2;
Effectively, you get the strong typing of a DTO without having to create/maintain another class. All columns not specified will be populated with the default value of the column's type.
It translates to the following in SQL:
SELECT TOP(1) m.Prop1, m.Prop2 FROM MyTable m WHERE m.Id = #id
Which indeed gives a performance boost over Find() assuming you're not specifying all the columns.
EDIT: As Gert mentioned, use with caution, as it's not always obvious when a "partial entity" is being passed around.
You can use free AutoMapper's ProjectTo<> extension, so the query would look like this:
context.OrderLines
.Where(ol => ol.OrderId == orderId)
.ProjectTo<OrderLineDTO>(configuration)
.ToList();
I have this query:
var result = (from game in db.Games
join gameevent in db.Events
on game.GameId equals gameevent.GameId into events
from _event in events
join _targetObjects in db.TargetObjects
on _event.TargetObject equals _targetObjects.TargetObjectId into targetss
where game.userId == userId
select new ProfileViewModel
{
record = events.Where(s => s.TargetObjectId == _event.TargetObject && _event.EventType == 35).Select(/* Here I want to select a value from targetss, field called TargetName */).ToList()
}).First();
As you can see, I want to get value based on where clause from other table. Is that possible in the select new part?
I want to get the name of the targetObject based on the targetObjectId which matches the targetObjectId in events table and also the event type should be 35.
If the query starts to become a too complex one then it is worth splitting it into parts.
In my example below I use the extension method syntax of LINQ instead of query keywords just because it is easier for me to use.
// First we collect the relevant games.
var games =
db
.Games
.Where(game => game.UserId == userId);
// Then we collect the events of the collected games that have the specified event type.
var events =
db
.Events
.Join(
games,
gameEvent => gameEvent.GameId,
game => game.GameId,
(gameEvent, game) => gameEvent
)
.Where(gameEvent => gameEvent.EventType == 35);
// Then we collect the target objects based on the collected events.
var targetObjects =
db
.TargetObjects
.Join(
events,
targetObject => targetObject.TargetObjectId,
gameEvent => gameEvent.TargetObjectId,
(targetObject, gameEvent) => targetObject
);
// Last we select the target name from the collected target objects.
var records =
targetObjects
.Select(targetObject => targetObject.TargetName)
.ToList(); // The query will be executed at this point.
If this is not what you are looking for, please clarify what data the ProfileViewModel should have exactly and from which set should it be selected as I am not very familiar with this syntax.
I have a database that contains 3 tables:
Phones
PhoneListings
PhoneConditions
PhoneListings has a FK from the Phones table(PhoneID), and a FK from the Phone Conditions table(conditionID)
I am working on a function that adds a Phone Listing to the user's cart, and returns all of the necessary information for the user. The phone make and model are contained in the PHONES table, and the details about the Condition are contained in the PhoneConditions table.
Currently I am using 3 queries to obtain all the neccesary information. Is there a way to combine all of this into one query?
public ActionResult phoneAdd(int listingID, int qty)
{
ShoppingBasket myBasket = new ShoppingBasket();
string BasketID = myBasket.GetBasketID(this.HttpContext);
var PhoneListingQuery = (from x in myDB.phoneListings
where x.phonelistingID == listingID
select x).Single();
var PhoneCondition = myDB.phoneConditions
.Where(x => x.conditionID == PhoneListingQuery.phonelistingID).Single();
var PhoneDataQuery = (from ph in myDB.Phones
where ph.PhoneID == PhoneListingQuery.phonePageID
select ph).SingleOrDefault();
}
You could project the result into an anonymous class, or a Tuple, or even a custom shaped entity in a single line, however the overall database performance might not be any better:
var phoneObjects = myDB.phoneListings
.Where(pl => pl.phonelistingID == listingID)
.Select(pl => new
{
PhoneListingQuery = pl,
PhoneCondition = myDB.phoneConditions
.Single(pc => pc.conditionID == pl.phonelistingID),
PhoneDataQuery = myDB.Phones
.SingleOrDefault(ph => ph.PhoneID == pl.phonePageID)
})
.Single();
// Access phoneObjects.PhoneListingQuery / PhoneCondition / PhoneDataQuery as needed
There are also slightly more compact overloads of the LINQ Single and SingleOrDefault extensions which take a predicate as a parameter, which will help reduce the code slightly.
Edit
As an alternative to multiple retrievals from the ORM DbContext, or doing explicit manual Joins, if you set up navigation relationships between entities in your model via the navigable join keys (usually the Foreign Keys in the underlying tables), you can specify the depth of fetch with an eager load, using Include:
var phoneListingWithAssociations = myDB.phoneListings
.Include(pl => pl.PhoneConditions)
.Include(pl => pl.Phones)
.Single(pl => pl.phonelistingID == listingID);
Which will return the entity graph in phoneListingWithAssociations
(Assuming foreign keys PhoneListing.phonePageID => Phones.phoneId and
PhoneCondition.conditionID => PhoneListing.phonelistingID)
You should be able to pull it all in one query with join, I think.
But as pointed out you might not achieve alot of speed from this, as you are just picking the first match and then moving on, not really doing any inner comparisons.
If you know there exist atleast one data point in each table then you might aswell pull all at the same time. if not then waiting with the "sub queries" is nice as done by StuartLC.
var Phone = (from a in myDB.phoneListings
join b in myDB.phoneConditions on a.phonelistingID equals b.conditionID
join c in ph in myDB.Phones on a.phonePageID equals c.PhoneID
where
a.phonelistingID == listingID
select new {
Listing = a,
Condition = b,
Data = c
}).FirstOrDefault();
FirstOrDefault because single throws error if there exists more than one element.
How can I select / project values from a subquery from a different table into my main query?
I have an NH-model like this:
[Serializable]
public class MyModel
{
public virtual int Id {get; set;}
//more mapped values
....
//unmapped values
public virtual string ValueFromOtherTable {get;set;}
}
And I want to fill ValueFromOtherTable with a left join like this:
Select mt.*, ..., ot.ValueFromOtherTable from MyModelTable mt left
join OtherTable ot ON (somecondition)
where MyModelTable is the table mapped to MyModel-class. I want to fill ValueFromOtherTable (no NH-mapping) by selecting all values from mt (to fill the NH-mapped columns) and then by using OtherTable I want to fill ValueFromOtherTable.
I can´t join both tables via QueryOver as there exists no direct parent-child correlation in the model, so JoinAlias or JoinQueryOver won´t work. My MainQueryOver queries MyModelTable.
ALTERNATIVE:
The alternative is to first get all values from MyModelTable and then using the properties there to query OtherTable. However this will result in an SELECT N+1 problem (for each model from MyModel select some OtherTable...) and also makes the code very complicated.
Is there a good way to solve this problem or is the only way to fill MyModel by using described alternative
?
One way would be to use Projections, Subquery and DTO. So let's say, that we have DTO (almost the same as MyModel, but with new extern property ... e.g. Count). Then we can do it like this:
MyModel main = null;
MyModelDTO dto = null;
// the main query
var query = session.QueryOver<MyModel>(() => main);
// the subquery used for projection
var subquery = QueryOver.Of<OtherModel>()
// select something, e.g. count of the ID
.SelectList(selectGroup => selectGroup.SelectCount(o => o.ID))
// some condition
// kind of JOIN inside of the subquery
.Where(o => o.xxx == main.yyy); // just example
// now select the properties from main MyModel and one from the subquery
query.SelectList(sl => sl
.SelectSubQuery(subquery)
.WithAlias(() => dto.Count)
.Select(() => main.ID)
.WithAlias(() => dto .ID)
....
);
// we have to use transformer
query.TransformUsing(Transformers.AliasToBean<MyModelDTO >())
// and we can get a list of DTO
var list = query.List<MyModelDTO>();
I'm trying to understand how to use Entity Framework 6. The code below works. However, it appears to have four queries in it for a single write operation. It doesn't seem right to hit the database five separate times. I want a single database call that adds the appropriate item to each table as needed. Is there some better way to do the code below? Or is it really doing a single database hit in the SaveChanges call?
public bool Write(ILogEntry logEntry)
{
var log = logEntry as AssetStateLogEntry;
if (log == null) return false;
using (var db = _dbContextProvider.ConstructContext())
{
if (db != null)
{
var state = new VehicleStateLogEntryDbo
{
LogSource = db.LogSources.FirstOrDefault(l => l.Name == log.Source.ToString())
?? new LogSourceDbo {Name = log.Source.ToString()},
Message = log.Message,
TimeStamp = log.TimeStamp.ToUniversalTime(),
Vehicle = db.Vehicles.FirstOrDefault(v => v.Name == log.Asset.Name)
?? new VehicleDbo {Name = log.Asset.Name, VehicleIdentifier = log.Asset.ID},
VehicleState = db.VehicleStates.FirstOrDefault(v => v.Name == log.StateValue.ToString() && v.VehicleStateType.Name == log.StateType.ToString())
?? new VehicleStateDbo
{
Name = log.StateValue.ToString(),
VehicleStateType = db.VehicleStateCategories.FirstOrDefault(c => c.Name == log.StateType.ToString())
?? new VehicleStateTypeDbo {Name = log.StateType.ToString()},
}
};
db.VehicleStateLogEntrys.Add(state);
db.SaveChanges();
}
}
return true;
}
You are indeed making 4 queries to the database, as a result of these calls:
db.LogSources.FirstOrDefault
db.Vehicles.FirstOrDefault
db.VehicleStates.FirstOrDefault
db.VehicleStateCategories.FirstOrDefault
When you call FirstOrDefault, the LINQ query is executed and thus, the database is hit.
I don't know your schema, but maybe you could join some of them into a single LINQ query (at least the Vehicles* tables seem to be related).
EDIT: sample query using joins as requested by the OP
Take the following query as an starting point of what I suggested, you haven't provided your entities so this is just to give you and idea:
from l in db.LogSources
join v in db.Vehicles on l.Asset.ID equals v.VehicleIdentifier
join vs in db.VehicleStates on vs.VehicleIdentifier equals v.VehicleIdentifier
where l.Name == log.Source.ToString()
&& v.Name == log.Asset.Name
&& vs.Name == log.StateValue.ToString()
&& vs.VehicleStateType.Name == log.StateType.ToString()
select new VehicleStateLogEntryDbo
{
LogSource = l,
Message = log.Message,
TimeStamp = log.TimeStamp.ToUniversalTime(),
Vehicle = s,
VehicleState = vs
}
A couple considerations:
As #Gert suggested, you should probably use foreign keys instead of whole object references.
I haven't considered the possibilities of null values in the example, you can take them into account using left joins with DefaultIfEmpty.
In stead of setting object references you should set primitive foreign key values. From an object-oriented point of view this sounds like a heresy, but it's Entity Framework's recommended approach when it comes to setting associations efficiently.
Of course, there should be foreign key values to be set in the first place. In your VehicleStateLogEntryDbo this could look like:
public int VehicleIdentifier { get; set; } // or guid?
[ForeignKey("VehicleIdentifier")]
public VehicleDbo Vehicle { get; set }
The ForeignKey attribute tells EF that both properties belong together in a foreign key association. This can also be configured by the fluent API, e.g. in the OnModelCreating override:
modelbuilder.Entry<VehicleStateLogEntryDbo>()
.HasRequired(v => v.Vehicle)
.WithMany()
.HasForeignKey(v => v.VehicleIdentifier);
By the way, having a Vehicle property only is referred to as an independent association.
So when you have these foreign key associations in place you can simply set the FK values. Maybe you should modify your DTO to transfer these values in stead of names etc.