Why use Entity Framework – Justifications

Learning EF takes a lot of time, changing code to use EF is risky, so what are the justifications for considering this.

  1. Entity Framework is listed in NuGet as Microsoft’s recommended data access technology.
  2. You need a lot less code to get and fill objects and reduces the amount of code that you need to write
  3. Closer to OO
  4. It appears to be here to stay
  5. It keeps getting better
  6. Data sets are not being developed
  7. Works well with WPF
  8. You will learn and get better at it in time

 

Advertisements

How to change Entity Framework context db model cache at runtime

I have two customer database tables, but want to use the same model and context, just be able to determine at runtime if i want to load from the live or the prospect customer table. The problem is that a context OnModelCreating runs only once by default, so you can’t leave parameters there to switch for you. One solution was to copy the context, but I found I needed a bit too much extra code in the usage to determine which to use. To understand a solution it is worth using the Visual Studio Object Browser DbContext remarks. “The protected OnModelCreating method can be overridden to tweak this model. More control over the model used for the Model First approach can be obtained by creating a System.Data.Entity.Infrastructure.DbCompiledModel explicitly from a System.Data.Entity.DbModelBuilder and passing this model to one of the DbContext constructors.” So you can create a DbCompliedModel (System.Data.Entity.Infrastructure) and store it if you may switch back to it, then use it to create an instance of your context. This can be done on the context using Shared / Static factory method.

    Public Class CustomerContext
        Inherits DbContext

        Property Customers() As DbSet(Of Customer)

        Private Shared dbCompiledModelLive As DbCompiledModel
        Private Shared dbCompiledModelProspect As DbCompiledModel

        Private Sub New(connection As DbConnection, model As DbCompiledModel)
            MyBase.New(connection, model, True)
        End Sub

        Shared Function CreateCustomerContext(LiveCustomer As Boolean) As CustomerContext

            Dim conStr = ConfigurationManager.ConnectionStrings("BusinessEntities").ConnectionString
            Dim DbConnection As New SqlConnection(conStr)
            If LiveCustomer Then
                If dbCompiledModelLive IsNot Nothing Then
                    Return New CustomerContext(DbConnection, dbCompiledModelLive)
                End If
            Else
                If dbCompiledModelProspect IsNot Nothing Then
                    Return New CustomerContext(DbConnection, dbCompiledModelProspect)
                End If
            End If

            Dim modelBuilder As New DbModelBuilder
            CustomerMap.LiveCustomerFlag1 = LiveCustomer
            modelBuilder.Configurations.Add(New CustomerMap)
            Dim model = modelBuilder.Build(DbConnection)

            If LiveCustomer Then
                dbCompiledModelLive = model.Compile
                Return New CustomerContext(DbConnection, dbCompiledModelLive)
            End If
            dbCompiledModelProspect = model.Compile
            Return New CustomerContext(DbConnection, dbCompiledModelProspect)

        End Function
    End Class

This all works

    <TestMethod()> _
    Public Sub CustomersTest()

        Dim targetLive = CustomerContext.CreateCustomerContext(True)
        Dim actual As DbSet(Of Customer) = targetLive.Customers
        Debug.Print("Live Customers")
        PrintResult(actual)
        Assert.IsTrue(actual.Count > 2)

        Dim targetProspect = CustomerContext.CreateCustomerContext(False)
        actual = targetProspect.Customers
        Debug.Print("")
        Debug.Print("Prospect Customers")
        PrintResult(actual)
        Assert.AreEqual(0, actual.Count)

        actual = targetLive.Customers
        Debug.Print("")
        Debug.Print("Live Customers")
        PrintResult(actual)
        Assert.IsTrue(actual.Count > 2)

        targetLive = CustomerContext.CreateCustomerContext(True)
        actual = targetLive.Customers
        Debug.Print("")
        Debug.Print("Live Customers")
        PrintResult(actual)
        Assert.IsTrue(actual.Count > 2)

    End Sub

    Private Sub PrintResult(actual As DbSet(Of Customer))
        For Each item As Customer In actual
            Debug.Print(String.Concat(item.ID, vbTab, item.Customer1))
        Next
    End Sub

Could be useful With thanks also to: http://romiller.com/2011/05/23/ef-4-1-multi-tenant-with-code-first/

Entity framework designer “Type ‘ObjectParameter is not defined” with old code generation items

Errors:

  1. “Namespace or type specified in the imports ‘System.Data.Objects’ doesn’t contain any public member or cannot be found. Make sure the namespace or the type is defined and contains at least one public member. Make sure that the imported element name doesn’t use any aliases.”
  2. “Type ‘ObjectParameter is not defined” with old code generation items

 

The code generation item was created some years ago. Now whenever the myModel.context.tt is executed it puts “Imports System.Data.Objects” at the top of the Model.Context.vb file.

 

Solution

It provides a suggestion of “Change ‘System.Data.Objects’ to ‘System.Data.Entity.Core.Objects’ which is easy enough to do, only you will have to do it next time as well.

So do the change then actually copy the “Imports System.Data.Entity.Core.Objects”

into the Model.Context.tt file, so that next time it will generate correctly.

 

End.

 

WPF EntityFramework DataGrid ComboBox

There may be several ways of doing this:

    <UserControl.Resources>
        <CollectionViewSource x:Key="originatorViewSource" d:DesignSource="{d:DesignInstance my:Originator, CreateList=True}" />
        <CollectionViewSource x:Key="customerLookup" />
    </UserControl.Resources>
                    <DataGridComboBoxColumn x:Name="cboCustomer"
                                            Width="200"
                                            Header="Customer"
                                            ItemsSource="{Binding Source={StaticResource customerLookup}}"
                                            SelectedValueBinding="{Binding Path=CustomerID}"
                                            SelectedValuePath="ID"
                                            DisplayMemberPath="Customer1">
                    </DataGridComboBoxColumn>

        private void LoadComboBox()
        {
            try
            {
                SalesContext contextCustomer = new SalesContext();
                var customerList = from c in contextCustomer.Customers
                                   orderby c.Customer1
                                   select new { ID = c.ID, Customer1 = c.Customer1 };

                CollectionViewSource CustomerViewSource = (CollectionViewSource)this.Resources["customerLookup"];
                CustomerViewSource.Source = customerList.ToList();
                //cboCustomer.ItemsSource = customerList.ToList();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

 

End

Entity Framework 6.x from 4.x DbExtensions is not declared. It maybe inaccessible due to its protection level.

After upgrade from Entity Framework 4.x to 6.x get various errors:

Error

DbExtensions.Load(context.myItems)
myBindingSource.DataSource = DbExtensions.ToBindingList(context.myItems)

Solution

context.myItems.Load()
me.myDataGridView.DataSource = context.myItems.Local.ToBindingList

 

End.

 

The model backing the ‘FilmContext’ context has changed since the database was created. Consider using Code First Migrations to update the database

This happened when the database connection string had not been setup in the App.config, so Code First went ahead and created a new database, which I then deleted and set up the connection string to my existing database. I then started getting this error.

Workaround by adding Entity.Database.SetInitializer in the context:

Protected Overrides Sub OnModelCreating(modelBuilder As System.Data.Entity.DbModelBuilder)
Entity.Database.SetInitializer(Of FilmContext)(Nothing) ‘To not initialise the database
modelBuilder.Configurations.Add(New FilmsMap)
End Sub

Entity Framework connection error: System.Data.ProviderIncompatibleException

For me this occurred when I used Entity Framework Power Tools Beta 3, and then used the “Entity Framework > View Entity Data Model (Read-Only)” functionality. The error was:

System.Data.ProviderIncompatibleException: An error occurred while getting provider information from the database. This can be caused by Entity Framework using an incorrect connection string. Check the inner exceptions for details and ensure that the connection string is correct. —> System.Data.ProviderIncompatibleException: The provider did not return a ProviderManifestToken string. —> System.Data.SqlClient.SqlException: A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: Named Pipes Provider, error: 40 – Could not open a connection to SQL Server)

This was because the Project containing the context was a class library and was not the startup project. I changed the App.config in the class library but this had no effect. To resolve you change the App.config file in the project marked as the startup project.

Might also be that a reference to the project containing the context is missing.

Solved.

Get DbContext entities and properties at runtime

The modelbuilder will first build all DbSets specified.
It will then build from any class objects that these DbSets refer to. This is more than you might think. Furthermore on some actions one of these other classes may have not been mapped correctly and cause the main program to crash, even if you were never interested in these classes.

To discover what classes and properties have been built at run-time use the following code:

    _
    Public Sub ProductsContextEntitiesBuiltList()
        Using context As New ProductsContext(ConnectionString.CurrentConnectionString)
            ContextEntitiesBuiltList(context)
        End Using
    End Sub

    Private Sub ContextEntitiesBuiltList(context As Entity.DbContext)
        Dim objContext = CType(context, IObjectContextAdapter).ObjectContext
        Dim workspace As MetadataWorkspace = objContext.MetadataWorkspace
        Dim tables As IEnumerable(Of EntityType) = workspace.GetItems(Of EntityType)(DataSpace.CSpace)
        For Each table In tables
            Debug.WriteLine(table.FullName)

            For Each p In table.Properties
                Debug.WriteLine("   " + p.Name)
            Next
        Next

        Debug.WriteLine("")

        'Or
        'For Each myEntity In objContext.MetadataWorkspace.GetItems(Of EntityType)(DataSpace.CSpace).ToList
        '    Debug.WriteLine(myEntity.FullName)
        'Next
    End Sub

Use modelBuilder.Ignore(Of <entity) 'to stop building those not required.

Entity Framework Error “The property ‘id’ is part of the object’s key information and cannot be modified”

If you load an entity then change its key you may get this message.

If you really must change the key, for instance on an entity which does not use identity, then you may set the entity’s state to Added then change the key, then change the state to Unchanged.

To change state you could use

    Sub SetEntityState(myObject As Object, myState As System.Data.Entity.EntityState)
        context.Entry(myObject).State = myState
    End Sub

— Original 8-Jul-2013 —

In my case this was because I was using context.Products.Find(123) and then manipulating the item, perhaps adding it again. So the Find method will cache it the first time. So if you want to do a subsequent Add/Modify then perhaps best to clear that store. You can do this with.

For each p as product in context.products
   context.enty(p).state = detached
next

It was not that the key was wrong.

 

Why Entity Framework is releasing on NuGet only – One Unicorn

Hmm

http://blog.oneunicorn.com/2012/02/11/why-entity-framework-is-releasing-on-nuget-only/

Might work with deployment.

Seems to encourage copying EntityFramework each and every time you copy the files across.