I have a linq to entity expression:
entities = new zdmEntities();
var reltables = (from r in entities.relations
orderby r.id
select new Relation
{
Id = r.id,
Devices = r.devices.device_name,
Systems = r.systems.system_name,
Models = r.models.name,
Functions = r.functions.function_name
}).ToList();
ultraGrid1.DataSource = reltables.ToList();
class Relation
{
public int Id { get; set; }
public string Devices { get; set; }
public string Systems { get; set; }
public string Models { get; set; }
public string Functions { get; set; }
}
As you can see the relation table contains a link to other tables.
The class Relation contains my columns for the datagrid.
But there is one problem... can't be posssible two way databinding between grid and database. I wrote all the updates manually but it's very difficult.
I understand that this is because in linq expression there is 'new'. But how do you make it without 'new'?
How I can display columns that I need with a two-way databinding and without own class like 'Relation'.
Windows Form. Not wpf)
Thanx, Alex.
When you write entities.Relations.Select(r => new ...) you are making a projection of each Relation EF object into a new non-EF object. By EF object I mean a class which is known by and tracked by EntityFramework.
Making changes to a EF-known class instance would propagate the changes back to DB when you save changes in your db/entity context. In contrast, making changes to a EF-unknown projection (or any projection) has no effect on the original object.
There are two ways you can achive what you want: If your DataGrid (NetAdvantage UltraGrid?) supports binding to subobjects (such as relation.device) you can then use ultraGrid.DataSource = entities.relations and define grid columns to bind to field devices.device_name. The other way would be something like this:
class Relation
{
private readonly EfRelation _originalRelation;
public Relation(EfRelation originalRelation)
{
this._originalRelation = originalRelation;
}
public string Devices
{
get { return this._originalRelation.devices.device_name; }
set { this._originalRelation.devices.device_name = value; }
}
// Repeat for other properties
}
...
var reltables = entities.relations.ToList().Select(r => new Relation(r)).ToList();
Then you just save changes to your db/object context. The EfRelation is the name of your EF Relation class, change it to the name of your EF class which represents a relation.
Related
Let's say I have an entity PersonEntity with a property EyeColorId and navigation property of type EyeColorEntity. Is there a way to prefetch the EyeColorEntity without saving the entity and without just querying for the EyeColor.
Ex.
public class PersonEntity
{
public int Id { get; set; }
public int EyeColorId { get; set; }
public EyeColorEntity EyeColor { get; set; }
}
public void FillFromDbExample(DbContext context)
{
var personEntity = new PersonEntity()
{
EyeColorId = 5
};
context.SetNavigationProperties(personEntity);
}
Theoretically this would fill the navigation property on the entity. The result would look something like this.
personEntity =
{
EyeColorId = 5,
EyeColor =
{
Id = 5,
Color = "Blue"
}
}
The trick here is that I don't want to have to individually query for each property and I don't want to have to save the entity to the database to pull these properties back. Does something like the described functionality exist in EnityFramework 6.2.0.
You can pre-load all properties in the context to make relationship fixup work. For example:
context.EyeColors.Load();
var personEntity = new PersonEntity()
{
EyeColorId = 5
};
context.Persons.Attach(personEntity);
In the last statement EF automatically populated personEntity.EyeColor.
Alternatively, you can rely on lazy loading by initializing the entity as a lazy-loading proxy. The property must be virtual to enable proxy creation:
public virtual EyeColorEntity EyeColor { get; set; }
Then:
var personEntity = context.Persons.Create(); // Creates a proxy
personEntity.EyeColorId = 5;
context.Persons.Attach(personEntity);
Now EF will query the matching EyeColor from the database when personEntity.EyeColor is accessed (= lazy loading).
I don't want to have to individually query for each property
Lazy loading does query properties individually, but it's not you who has to do it.
Note that in both cases personEntity must be attached to the context.
i am struggeling for a while now to understand how EF loads / updates entities.
First of all i wanna explain what my app (WPF) is about. I am developing
an application where users can store Todo Items in Categories, these categories are predefined by the application. Each user can read all items but can only delete / update his own items. It's a multiuser system, means the application is running multiple times in the network accessing the same sql server database.
When a user is adding/deleting/updating items the UI on all the other running apps has to update.
My model looks like this:
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
public List<Todo> Todos { get; set; }
}
public class Todo
{
public int Id { get; set; }
public string Content { get; set; }
public DateTime LastUpdate { get; set; }
public string Owner { get; set; }
public Category Category { get; set; }
public List<Info> Infos { get; set; }
}
public class Info
{
public int Id { get; set; }
public string Value { get; set; }
public Todo Todo { get; set; }
}
I am making the inital load like this, which works fine:
Context.dbsCategories.Where(c => c.Id == id).Include(c => c.Todos.Select(t => t.Infos)).FirstOrDefault();
Now i was trying to load only the Todos which are from the current user therefore i tried this:
Context.dbsCategories.Where(c => c.Id == id).Include(c => c.Todos.Where(t => t.Owner == Settings.User).Select(t => t.Infos)).FirstOrDefault();
This does not work because it's not possible to filter within include, so I tried this:
var cat = Context.dbsCategories.Where(c => c.Id == id).FirstOrDefault();
Context.dbsTodos.Where(t => t.Category.Id == cat.Id && t.Owner == Settings.User).Include(t=>t.Infos);
After executing the second line where i look for the Todo Items, these Items were automatically added to cat's Todos collection. Why? I would have expected that i have to add them manually to cat's Todos collection.
Just for my understanding what is EF doing here exactly?
Now to my main problem -> the synchronization of the data between database and client. I am using a long running Context which lives as long as the application is running to save changes to the database which are made on owned items. The user does not have the possibility to manipulate / delete data from other users this is guarantee by the user interface.
To synchronize the data i build this Synch Method which will run every 10 second, right now it's triggere manually.
Thats my synchronization Code, which only synchronizes Items to the client that do not belong to it.
private async Task Synchronize()
{
using (var ctx = new Context())
{
var database = ctx.dbsTodos().Where(x => x.Owner != Settings.User).Select(t => t.Infos).AsNoTracking();
var loaded = Context.dbsTodos.Local.Where(x => x.Owner != Settings.User);
//In local context but not in database anymore -> Detachen
foreach (var detach in loaded.Except(database, new TodoIdComparer()).ToList())
{
Context.ObjectContext.Detach(detach);
Log.Debug(this, $"Item {detach} detached");
}
//In database and local context -> Check Timestamp -> Update
foreach (var update in loaded.Intersect(database, new TodoIdTimeStampComparer()))
{
await Context.Entry(update).ReloadAsync();
Log.Debug(this, $"Item {update} updated");
}
//In database but not in local context -> Attach
foreach (var attach in database.ToList().Except(loaded, new TodoIdComparer()))
{
Context.dbsTodos().Attach(attach);
Log.Debug(this, $"Item {attach} attached");
}
}
}
I am having following problems / issues of unknow origin with it:
Detaching deleted Items seems to work, right now i am not sure if only the Todo Items are detached or also the Infos.
Updating Items works only for the TodoItem itsself, its not reloading the Infos within? How can i reload the whole entity with all it's relations?
I am thankful for every help on this, even if you are saying it's all wrong what i am doing here!
Attaching new Items and Infos does not work so far? What am i doing wrong here?
Is this the right approach to synchronize data between client and database?
What am i doing wrong here? Is there any "How to Sync" Tutorial? I have not found anything helpful so far?
Thanks!
My, you do like to deviate from entity framework code-first conventions, do you?
(1) Incorrect class definitions
The relations between your tables are Lists, instead of ICollections, they are not declared virtual and you forgot to declare the foreign key
There is a one-to-many relation between Todo and Category: every Todo belongs to exactly one Category (using a foreign key), every Category has zero or more Todos.
You choose to give Category a property:
List<Todo> Todos {get; set;}
Are you sure that category.Todos[4] has a defined meaning?
What would category.Todos.Insert(4, new Todo()) mean?
Better stick to an interface where you can't use functions that have no proper meaning in your database: use ICollection<Todo> Todos {get; set;}. This way you'll have only access to functions that Entity Framework can translate to SQL.
Besides, a query will probably be faster: you give entity framework the possibility to query the data in its most efficient way, instead of forcing it to put the result into a List.
In entity framework the columns of a table are represented by non-virtual properties; the virtual properties represent the relations between the tables (one-to-many, many-to-many)
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
... // other properties
// every Category has zero or more Todos (one-to-many)
public virtual ICollection<Todo> Todos { get; set; }
}
public class Todo
{
public int Id { get; set; }
public string Content { get; set; }
... // other properties
// every Todo belongs to exactly one Category, using foreign key
public int CategoryId { get; set }
public virtual Category Category { get; set; }
// every Todo has zero or more Infos:
public virtual ICollection<Info> Infos { get; set; }
}
You'll probably guess Info by now:
public class Info
{
public int Id { get; set; }
public string Value { get; set; }
... // other properties
// every info belongs to exactly one Todo, using foreign key
public int TodoId {get; set;}
public virtual Todo Todo { get; set; }
}
Three major improvements:
ICollections instead of Lists
ICollections are virtual, because it is not a real column in your table,
foreign key definitions non-virtual: they are real columns in your tables.
(2) Use Select instead of Include
One of the slower parts of a database query is the transport of the selected data from the Database Management System to your local process. Hence it is wise to limit the amount of transported data.
Suppose Category with Id [4] has a thousand Todos. Every Todo of this Category will have a foreign key with a value 4. So this same value 4 will be transported 1001 times. What a waste of processing power!
In entity framework use Select instead of Include to query data and select only the properties you actually plan to use. Only use Include if you plan to update the Selected data.
Give me all Categories that ... with their Todos that ...
var results = dbContext.Categories
.Where(category => ...)
.Select(category => new
{
// only select properties that you plan to use
Id = category.Id,
Name = category.Name,
...
Todos = category.Todos
.Where(todo => ...) // only if you don't want all Todos
.Select(todo => new
{
// again, select only the properties you'll plan to use
Id = todo.Id,
...
// not needed, you know the value:
// CategoryId = todo.CategoryId,
// only if you also want some infos:
Infos = todo.Infos
.Select(info => ....) // you know the drill by now
.ToList(),
})
.ToList(),
});
(3) Don't keep DbContext alive for such a long time!
Another problem is that you keep your DbContext open for quite some time. This is not how a dbContext was meant. If your database changes between your query and your update, you'll have troubles. I can hardly imagine that you query so much data that you need to optimize it by keeping your dbContext alive. Even if you query a lot of data, the display of this huge amount of data would be the bottle-neck, not the database query.
Better fetch the data once, dispose the DbContext, and when updating fetch the data again, update the changed properties and SaveChanges.
fetch data:
RepositoryCategory FetchCategory(int categoryId)
{
using (var dbContext = new MyDbContext())
{
return dbContext.Categories.Where(category => category.Id == categoryId)
.Select(category => new RepositoryCategory
{
... // see above
})
.FirstOrDefault();
}
}
Yes, you'll need an extra class RepositoryCategory for this. The advantage is, that you hide that you fetched your data from a database. Your code would hardly change if you'd fetch your data from a CSV-file, or from the internet. This is way better testable, and also way better maintainable: if the Category table in your database changes, users of your RepositoryCategory won't notice it.
Consider creating a special namespace for the data you fetch from your database. This way you can name the fetched Category still Category, instead of RepositoryCategory. You even hide better where you fetched your data from.
Back to your question
You wrote:
Now i was trying to load only the Todos which are from the current user
After the previous improvements, this will be easy:
string owner = Settings.User; // or something similar
var result = dbContext.Todos.Where(todo => todo.Owner == owner)
.Select(todo => new
{
// properties you need
})
Below is a class I have used to generate a table in my database using Entity Framework. I'd like to be able to link this table to another table, Property. However, the way my code is set up there is not an Id column in the Instruction table, there is a Property property within the class, which then generates a PropertyId column in the actual database, but since the Property property is not an Id I am unable to using Linq to join these tables.
Instruction table
[Table("Instruction")]
public class Instruction
{
[Key]
public int Id { get; set; }
public InstructionTypes InstructionType { get; set; }
public Property Property { get; set; } //Generates the EF property FK, but is not an ID so therefore cannot be used in linq.
}
Property table
[Table("Property")]
public partial class Property
{
[Key]
public int Id { get; set; }
public Address Correspondence { get; set; }
}
Join Query
var instruction =
from instructions in _context.Instructions
join properties in _context.Properties on instructions.Property equals properties.Id
where ...
The above query gives a compiler error of: `The type of one of the expressions in the join clause is incorrect.
This error is being generated as I'm attempting to use a property object to join with a propertyId.
How can I alter this query so that I am able to join these two tables?
In 99% of all cases, you do not want to use the join operator. Entity Framework automatically generates SQL JOINS for you when you are using Navigation Properties.
var instruction = await _context.Instructions.Where(i => i.Property...).FirstOrDefaultAsync().ConfigureAwait(false);
Note, that depending on whether you are using EF6 or EF Core or with different configuration, Lazy Loading may be disabled (if not, I strongly encourage you to disable it as it is a massive performance bottleneck).
So you have to use the Include Method to eagerly load the related entity.
var instruction = await _context.Instructions.Include(i => i.Property).Where(i => i.Property...).FirstOrDefaultAsync().ConfigureAwait(false);
But before doing this, think if you really need the Instruction. If not, your code could become:
var property = await _context.Properties.Where(p => p.Instructions.Any(i => ...)).FirstOrDefaultAsync().ConfigureAwait(false);
Please note that you have to extend your Property class for this to work to have a back-reference
public partial class Property
{
// No need for the Key attribute, as this is convention
public int Id { get; set; }
public Address Correspondence { get; set; }
public int CorrespondenceId { get; set; } // Not needed in this scenario, but good practice
public ICollection<Instruction> Instructions { get; } = new HashSet<Instruction>();
}
You seems to be a newcomer to linq. As such you are still thinking as if you still are in an sql world.
With linq to entities, the use of join is the exception. SQL join are generated silently by EF using the navigation properties.
So your query can be:
var instruction =
from instruction in _context.Instructions
where instruction.Porperty.Correspondence.Contains("abc");
then you can access
instruction.First().Property.Correspondence
As a good practice you can delclare the foreign keys as class members and use the fluent API to bind them.
To test you can use the following code,
//assuming that Instructions is a DbSet<Instruction>
using (var context = new MyContext() ) {
context.Instructions.Add(
new instruction {
Property = new Property {
Correspondence = new Address {}
}
});
}
using (var context = new MyContext() ) {
var c = context.Instructions.First();
console.WriteLine($"{c.Id}, {c?.Property.Id}, {c?.Property?.Correspondence.Id}");
});
I need to create a part to attach to an existing content type. The model associated to this part has no variables except a list of items, corresponding to a 1-N relationship with the data contained in a table I created. If I use the Record approach, creating a Model containing only the list, it will generate a "fake" table with no other data except its Id. This is the current code:
**Model**
public class MyPartRecord : ContentPartRecord
{ public virtual IList<OtherRecord> MyList { get; set; } }
public class MyPart : ContentPart<MyPartRecord>
{
public IList<OtherRecord> MyList {
get { return Record.MyList; }
}
public class OtherRecord
{
public virtual string Var1 { get; set; }
public virtual int Var2 { get; set; }
}
**Migration**
SchemaBuilder.CreateTable("MyPartRecord", table => table
.ContentPartRecord());
SchemaBuilder.CreateTable("OtherRecord", table => table
.Column<int>("Id", column => column.PrimaryKey().Identity())
.Column<string>("Var1")
.Column<int>("Var2")
.Column<int>("MyPartRecord_Id")
);
I'm not convinced by this approach, so I'd like to know if I can create the 1-N relationship using only the Infoset approach and eliminating the need to create the "fake" table in the migration, or if there is some smarter way to do it using the Record approach.
UPDATE
I tried to use the infoset approach but I can't make it work. I changed my files like this:
**Model**
public class MyPart : ContentPart
{
public IList<OtherRecord> MyList {
get { return this.Retrieve(x => x.MyList, new List<OtherRecord>()); }
}
**Migration**
SchemaBuilder.CreateTable("OtherRecord", table => table
.Column<int>("Id", column => column.PrimaryKey().Identity())
.Column<string>("Var1")
.Column<int>("Var2")
.Column<int>("MyPart_Id")
);
But when the driver calls the get method I always receive an empty list, so I don't think that the relations are built properly.
I am new to EF. I am trying to get Entity Framework 4.2 to do a sort by a calculated property (not mapped).
Here is what my entity look like:
public class Site : Entity
{
public Site()
{
Equipments = new HashSet<Equipment>();
Forecasts = new HashSet<Forecast>();
}
[StringLength(8)]
public string Number { get; set; }
[StringLength(50)]
public string EquipmentShortCLLI { get; set; }
[StringLength(50)]
public string Location { get; set; }
public virtual Central Central { get; set; }
public virtual ICollection<Equipment> Equipments { get; set; }
public virtual ICollection<Forecast> Forecasts { get; set; }
#region Calculated Items
public bool IsEmbargo {
get { return Equipments.Count > 0 && Equipments.SelectMany(x => x.EquipmentDetails).Any(e => e.IsEmbargo); }
}
//...
public int PortsCapacity
{
get
{
return Equipments.Count > 0
? Equipments.SelectMany(x => x.Slots).Sum(x => x.PortsCapacity)
: 0;
}
}
#endregion
//...
By trying to order using any of my readonly properties I am getting the exception:
The specified type member 'PortsCapacity' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.
Which makes sense because EF is trying to build an sql orderby with a field that does not exist in the database (my understanding..).
Now, by using some dynamic linq code I was able to make this work for my many-to-one columns by passing "Central.SomeField" (as opposed to making a ReadOnly Property that returns Central.SomeField).
I.E.:
query.OrderBy("Central.SomeField");
However, I still face the same issue when it comes to a collection of items (Equipments). I am trying to make this as dynamic as possible by using a string coming from the client side and avoiding a long switch case, but at this point I will accept any ideas, so long as the sorting happens on the database side.
Edit 1:
Following what Ladislav Mrnka says, how would one execute an OrderBy clause on one-to-many child items using lambdas or expression?
I don't think that Dynamic Linq is capable of this. You need a real Linq subquery to compute aggregations on Equipements so it will simply not work. If the user selects ordering by IsEmbargo or PortsCapacity you must have some switch / if block to handle this case by appending special part of the query - no other way.