VS2013, code first EF6, VB
Elsewhere on SO I found a post that led me to add this to my Context class:
Public Sub New()
Me.Configuration.LazyLoadingEnabled = False
End Sub
However, in order to load a secondary table into my context for a view to find data to list in a For Each loop I had to add:
Dim myQuery = db.Questions.Include("PossibleAnswers").Where(Function(x) x.QuestionID = 6).Single()
Without that query in one form or another, my view does not find any data in the property 'PossibleAnswers' and nothing is displayed during the For Each loop. But with the query above, the For Each finds the PossibleAnswers data.
I checked the value of
db.Configuration.LazyLoadingEnabled
just before my view was called and it was false. But I still had to make a query with the Include() method to force the data to be brought into the context.
This is the full definition of the table in question:
Public Class Question
Public Enum qType
TrueFalse
MultipleChoice
ShortAnswer
End Enum
Public Property QuestionID As Integer
Public Property Text As String
Public Property Type As qType
Public Property PossibleAnswers As New List(Of qAnswer)
Public Property UsedBySurveys As New List(Of qSurvey)
End Class
Can anybody suggest what I may not be understanding about this?
Thanks.
Best Regards,
Alan
If you want all results from your context to automatically load the navigation properties, you should remove the Me.Configuration.LazyLoadingEnabled = False line from your context class.
You can also explicitly set lazy loading to false for an instance of your context: ie.
Using db As New MyContext
db.Configuration.LazyLoadingEnabled = True
Dim myQuery = db.Questions.Where(Function(x) x.QuestionID = 6).Single()
End Using
With Lazy Loading set to false, you have to explicitly tell the EF to include navigation properties along with the result set. You do that by using the .Include function (as you did). Calling the .Include function is called Eager Loading.
If you set the lazy loading property to true, the navigation properties will be pulled back from the database automatically. Here's a quick run down of Lazy Loading from MSDN:
http://msdn.microsoft.com/en-us/library/vstudio/dd456846(v=vs.100).aspx
Related
I am coding an application that performs various measurements on 3D objects, using expensive API calls to extract geometric info from another application. These measurements and how they are defined by the user are stored in a Study class. This class must be serializable, so that the user can save a particular study with all of it's data to a hard drive, and then load it on another session.
Properties of the 3D object might be Length, Width, Height, BoundingBoxDimensions, Weight, etc. However, they are not just plain numbers - each property must also store flags if they are requested to be calculated by the user, if they have been evaluated, an error flag, etc.
Some of these properties depend on other properties to be evaluated first (for example, if user requests the value of BoundingBoxDimensions, then Length, Width and Height must be calculated first. Therefore, each object property also stores a list of properties it depends on. If it is requested, and it sees that it's dependents are not yet evaluated, then it calls these dependents to be evaluated first.
The goal of this strategy is to minimize the number of API calls and save computation time. For example, if user requests Length and Width to be evaluated, only 2 API calls would be made (there is no reason to evaluate Height). If user calls for BoundingBoxDimensions, then 3 API calls have to be made, but no more.
However, this approach also means that I have to use delegate methods to allow different evaluation methods to be assigned to each object property.
This is my code so far.
'Main class for a specific 3D object measurement study. It must be serializable.
Public Class Study
'3D Object properties. They are bound to in WPF, but that part is omitted in this example.
Public Property Length As New ObjectProperty() With {.Name = "Length", .DependencyProperties = Nothing, .EvaluatingMethod = AddressOf GetLength}
Public Property Width As New ObjectProperty() With {.Name = "Width", .DependencyProperties = Nothing, .EvaluatingMethod = AddressOf GetWidth}
Public Property Height As New ObjectProperty() With {.Name = "Height", .DependencyProperties = Nothing, .EvaluatingMethod = AddressOf GetHeight}
Public Property BoundingBoxDimensions As New ObjectProperty() With {.Name = "BoundingBoxDimensions", .DependencyProperties = New List(Of ObjectProperty) From {Length, Width, Height}, .EvaluatingMethod = AddressOf GetBoundingBoxDimensions}
'Container for all properties to enable looping.
Public Property ObjectProperties As New List(Of ObjectProperty) From {Length, Width, Height, BoundingBoxDimensions}
'Called by the UI to set Requested flags on the object properties that the user requires to be evaluated.
Public Sub SetObjectPropertyRequests(requestedObjectPropertyNames As List(Of String))
For Each requestedObjectPropertyName In requestedObjectPropertyNames
For Each ObjectProperty In ObjectProperties
If requestedObjectPropertyName = ObjectProperty.Name Then ObjectProperty.Requested = True
Next
Next
End Sub
'Check Requested flags and evaluate.
Public Sub CalculateRequestedProperties()
For Each ObjectProperty In ObjectProperties
If ObjectProperty.Requested Then Dim tempValue As Object = ObjectProperty.Value
Next
End Sub
'Actual evaluating functions attached to the object properties, making complicated and expensive API calls. This part is simplified in this example.
Private Function GetLength() As Double
Return APIConnector.MeasureLength()
End Function
Private Function GetWidth() As Double
Return APIConnector.MeasureWidth()
End Function
Private Function GetHeight() As Double
Return APIConnector.MeasureHeight()
End Function
Private Function GetBoundingBoxDimensions() As Double()
Return {Length.Value, Width.Value, Height.Value}
End Function
End Class
'Template class for the object properties. Also must be serializable.
Public Class ObjectProperty
Public ReadOnly Property Value As Object
Get
'Check if dependency properties are not evaluated yet; if not, evaluate them first.
If DependencyProperties IsNot Nothing Then
For Each DependencyProperty In DependencyProperties
If DependencyProperty.State = ObjectPropertyStates.NotEvaluated Then Dim tempValue As Object = DependencyProperty.Value
Next
End If
'Once dependencies are evaluated, evaluate the value of this object property.
Dim evaluatedValue = EvaluatingMethod()
State = ObjectPropertyStates.Evaluated
Return evaluatedValue
End Get
End Property
Public Property Name As String
Public Property Requested As Boolean = False
Public Property State As ObjectPropertyStates = ObjectPropertyStates.NotEvaluated
Public Property DependencyProperties As List(Of ObjectProperty)
Public Property EvaluatingMethod As EvaluatingMethodDelegate
Public Delegate Function EvaluatingMethodDelegate() As Object
Public Enum ObjectPropertyStates
Evaluated
NotEvaluated
End Enum
End Class
A lot of actual measurement/evaluating code was simplified in this example to make the code more readable, but this should be a reproducible code.
Now, the issue with this strategy is that I cannot serialize Study class, as it contains ObjectProperty, which contains EvaluatingMethodDelegate, and to my knowledge delegates cannot be serialized.
If I set up the serializer to ignore EvaluatingMethodDelegate, then serialization succeeds, but upon deserialization, EvaluatingMethod pointers to the respective methods (GetLength, GetWidth, etc.) on each object are lost.
My question is, how do I solve this issue? Is my whole strategy wrong if I need to use serialization and deserialization? If so, is there a better way of implementing something like this? Or is there some way to simply avoid delegate methods with the current approach? I feel like I'm re-inventing the wheel here.
I have some model class:
Public Class MyViewModel
Public Property MyID() As Integer
Public ReadOnly Property FirstList As IEnumerable(Of SelectListItem)
Get
Using dbContext As New MyContext
Dim itemQuery = (From t In dbContext.ItemSet Select t)
Dim item As IEnumerable(Of Item) = itemQuery.ToList()
Return item.Select(Function(o) New SelectListItem() With {.Text = o.ItemDesc, .Value = o.ID})
End Using
End Get
End Property
Public ReadOnly Property SecondList As IEnumerable(Of SelectListItem)
Get
Using dbContext As New MyContext
Dim _Query = (From t In dbContext.FrameworkSet Select t)
Dim _list As IEnumerable(Of Item2) = _Query.ToList()
Return _list.Select(Function(o) New SelectListItem() With {.Text = o.Item2Desc, .Value = o.ID})
End Using
End Get
End Property
End Class
Basically, I'm calling MyContext twice. This instantiates EF repeatedly, correct? So my thought is just have a class global
Dim dbContext as New MyContext
Aside from Code Analysis telling me I need to implement IDisposable (which according to this: http://blog.jongallant.com/2012/10/do-i-have-to-call-dispose-on-dbcontext.html#.U6WdzrGEeTw I needn't worry about?)
I'm confused - what's the accepted best practice?
In addition to Phil Soady's comments (which, briefly, are not to store the context in a global variable and instead prefer short lived disposed contexts) I'd like to point out that much of the context initialization is not done per construction of the object but rather once for the lifetime of the application. This is mainly the process of building its internal model, which it does and then caches.
Check out more detail here: http://blog.oneunicorn.com/2011/04/15/code-first-inside-dbcontext-initialization/
Using block is ideal for EF.
Global variable for context is a recipe for nightmares. The Context is not threadsafe and is intended for short use.
Keeping the context for several operations in a logical flow is common.
Since the context content can be reused. Change detection , unit of work commit control are all part of EF. But dont try a keep the context for an extended period. You may have more performance problems with the context that way as the context may grow. You also have the multi user and concurrency issues to consider. Even a simple standalone APP on PC i would Create and dispose (using) the content on each "button" click in an app.
I'm using a Code First Entity Framework approach, and in my OnModelCreating function I have the following code:
With modelBuilder.Entity(Of FS_Item)()
.HasKey(Function(e) e.ItemKey)
.Property(Function(e) e.ItemRowVersion).IsConcurrencyToken()
.HasMany(Function(e) e.ItemInventories) _
.WithRequired(Function(e) e.Item).HasForeignKey(Function(e) e.ItemKey)
End With
Elsewhere I have a Web API Get implementation with some diagnostic code I'm looking at in a debugger:
Public Function GetValue(ByVal id As String) As FS_Item
GetValue = If(data.FS_Item.Where(Function(i) i.ItemNumber = id).SingleOrDefault(), New FS_Item())
Dim c = GetValue.ItemInventories.Count
End Function
I expect that c should get a non-zero value by looking up rows in the FS_Inventory view where ItemKey matches the retrieved FS_Item row's ItemKey. But I'm getting 0 even though there are matching rows. Am I calling .HasMany, .WithRequired and .HasForeignKey properly?
Note that .WithRequired is operating on the return value from the previous line whereas the other lines are operating on the With block expression.
Edit This model for FS_Item has been requested. Here it is:
Partial Public Class FS_Item
Public Property ItemNumber As String
Public Property ItemDescription As String
Public Property ItemUM As String
Public Property ItemRevision As String
Public Property MakeBuyCode As String
' Many many more properties
Public Property ItemRowVersion As Byte()
Public Property ItemKey As Integer
Private _ItemInventories As ICollection(Of FS_ItemInventory) = New HashSet(Of FS_ItemInventory)
Public Overridable Property ItemInventories As ICollection(Of FS_ItemInventory)
Get
Return _ItemInventories
End Get
Friend Set(value As ICollection(Of FS_ItemInventory))
_ItemInventories = value
End Set
End Property
End Class
Edit Learned something interesting. If I change Dim c = GetValue.ItemInventories.Count to this:
Dim c = data.FS_ItemInventory.ToList()
Dim correctCount = GetValue.ItemInventories.Count
Then correctCount gets the value of 3. It's like it understands the association between the objects, but not how to automatically query them as I'm used to coming from LINQ-to-SQL. Is EF different somehow in this regard?
Edit I have determined that I can make the associated objects load using this explicit loading code:
data.Entry(GetValue).Collection(Function(e) e.ItemInventories).Load()
What I want to understand now is what exactly determines whether an entity will load lazily or not? From all indications I can find, it should have loaded lazily. I even tried changing the declaration of ItemInventories to this, but then I got a NullReferenceException when trying to access it:
Public Overridable Property ItemInventories As ICollection(Of FS_ItemInventory)
It turns out that code which I thought was unrelated had disabled lazy loading. I have this in the constructor of FSDB:
DirectCast(Me, IObjectContextAdapter).ObjectContext.ContextOptions.ProxyCreationEnabled = False
Thanks to EF 4 - Lazy Loading Without Proxies I see that this will also disable lazy loading. The reason that code had been added was due to another error:
Type
'System.Data.Entity.DynamicProxies.FS_Item_64115A45C642902D6044AFA1AFD239E7DCB82FD000A10FE4F8DE6EA26A2AB418'
with data contract name
'FS_Item_64115A45C642902D6044AFA1AFD239E7DCB82FD000A10FE4F8DE6EA26A2AB418:http://schemas.datacontract.org/2004/07/System.Data.Entity.DynamicProxies'
is not expected. Consider using a DataContractResolver or add any
types not known statically to the list of known types - for example,
by using the KnownTypeAttribute attribute or by adding them to the
list of known types passed to DataContractSerializer.
And according to Serialization of Entity Framework objects with One to Many Relationship, the easy solution for that was to disable proxies.
I'm having some issues and it's all explained in some simplified code
I have a structure
Public Structure myStruct
Public Property name As String
Public Property span As Double
Public Property offs As Double
End Structure
Which is instantiated in a Singleton object as follow
Public myValues(10) As myStruct
Then on a form, I'm using the structure as the DataSource for a SourceBinder and the binder as DataSource for a DataGridView.
On loading the form, I get all the values into the binder
For i As Integer = 0 To 9
binder.Add(singleton.getRef.myValues(i))
Next i
All the values are shown on the grid.
User is supposed to be able to change the values, which should reflect on myValues but no matter, what code I put on CellValueChanged or CurrentChanged. I can't make it reflect the changes.
Using breakbpoints on those events, I noticed that grid.row().cell().values and binder.current never changes.
I tried also placing a button and directly changing myValues and reseting the binder binder.ResetBindings(False) and nothing happens.
As far as I saw all ReadOnly properties are set to false.
I've also tried setting VirtualMode to true on the grid and catching CellValuePushed and CellValueNeeded events but those are never called. (that was on a guessing base)
I'm really out of ideas on this one, if anyone can help me!
thanks.
Try using a class instead:
Public Class myStruct
Public Property name As String
Public Property span As Double
Public Property offs As Double
End Class
I'm writing a custom DataGridView object for a large project to hand out to a bunch of developers to make our app sections look consistent.
I want to set defaults for many of the properties of the DataGridView, and I can set many of them like this:
<System.ComponentModel.Browsable(True), System.ComponentModel.DefaultValue(DataGridViewAutoSizeColumnsMode.Fill)>_
Public Overloads Property AutoSizeColumnsMode() As DataGridViewAutoSizeColumnMode
Get
Return MyBase.AutoSizeColumnsMode
End Get
Set(ByVal value As DataGridViewAutoSizeColumnMode)
MyBase.AutoSizeColumnsMode = value
End Set
End Property
These properties overload with their defaults just fine. Its when I started trying to make default Cell styles that I ran into the issue. Since the DataGridViewCellStyle is a class, I cannot make a constant out of it. I've tried changing all of the settings to what I want them to be in the class constructor, and that works great, except that changes made in the designer properties just get set back as soon as the app runs. So putting the changes in the constructor won't do.
Is there anywhere else I can put code that only runs when the control is first dropped on the designer? or any other way of setting a default?
I ran into this problem too. My solution works around the requirement for the DefaultValue argument to be a compile-time constant. I thought, wouldn't it be sufficient to set the value in the class constructor (defined by the static constructor in C#, and the shared constructor in VB) instead?
This seems to be an good work-around in my case, though there are probably instances where it might break since it's not actually present in the meta-data until the class constructor is called upon loading the class, but for a Designer attribute that should be acceptable. Because DefaultValueAttribute.SetValue is protected, I had to define a derived class that makes it public.
This works fine in the designer, it recognizes when the value is the same as the default and omits it from the generated code when possible, and only generates the differences from the default.
Here's the code in C#, this should work in VB too but I'm not overly familiar with its syntax so I'll have to leave that up to you.
public partial class HighlightGrid : DataGridView
{
// Class constructor
static MethodGrid()
{
// Get HighlightStyle attribute handle
DefaultValueSettableAttribute attr =
TypeDescriptor.GetProperties(typeof(HighlightGrid))["HighlightStyle"]
.Attributes[typeof(DefaultValueSettableAttribute)]
as DefaultValueSettableAttribute;
// Set default highlight style
DataGridViewCellStyle style = new DataGridViewCellStyle();
style.BackColor = Color.Chartreuse;
attr.SetValue(style);
}
[DefaultValueSettable, Description("Cell style of highlighted cells")]
public DataGridViewCellStyle HighlightStyle
{
get { return this.highlightStyle; }
set { this.highlightStyle = value; }
}
// ...
}
// Normally the value of DefaultValueAttribute can't be changed and has
// to be a compile-time constant. This derived class allows setting the
// value in the class constructor for example.
public class DefaultValueSettableAttribute : DefaultValueAttribute
{
public DefaultValueSettableAttribute() : base(new object()) { }
public new void SetValue(Object value) { base.SetValue(value); }
}
Actually, I thought about it a while longer and came across a simpler solution for my issue. This does not work for all cases because it relies on the fact that the person using the custom component will likely never want to revert an entire CellStyle back to windows defaults. I ended up comparing a new CellStyle to the current one in the constructor, and only setting the style if they matched. This way it won't overwrite changes, but it will set it up the first time.
Public Class CustomDataGridView
Inherits System.Windows.Forms.DataGridView
Private RowStyle As New DataGridViewCellStyle
Public Sub New()
RowStyle.BackColor = Color.FromArgb(223, 220, 200)
RowStyle.Font = New Font("Arial", 12.75, FontStyle.Bold, GraphicsUnit.Point)
RowStyle.ForeColor = Color.Black
RowStyle.SelectionBackColor = Color.FromArgb(94, 136, 161)
If MyBase.RowsDefaultCellStyle.ToString = (New DataGridViewCellStyle).ToString Then
MyBase.RowsDefaultCellStyle = RowStyle
End If
End Sub
End Class
Just goes to show, Just because you have a golden hammer, doesn't mean that every problem is a nail.