entity framework retrieve data as list(of string) - entity-framework

I have the following code to select all counties given a stateid from a counties table.
Public Shared Function GetCountiesfromState(statename As String) As List(Of String)
Dim context As New Model.teckEntities()
Dim query = From c In context.counties Where c.stateId = 7 Select c
Return query.ToList()
End Function
I get any error that query is returning a list of model. any thoughts on where the error lies?

If there is a Name (or Title) field on the County entity, it should be as simple as:
Public Shared Function GetCountiesfromState(statename As String) As List(Of String)
Dim context As New Model.teckEntities()
' Here is the difference:
Dim query = From c In context.counties Where c.stateId = 7 Select c.Name
Return query.ToList()
End Function
In your code above you were selecting the c which, is a County entity, not necessarily a string property. By selecting c.Name (or c.Title) instead, you'll be building a list of strings instead of a list of county entities.
Cheers.

Related

Using AutoBogus to set member property value range

I have the following classes:
Class Person
Property ID As String = Nothing
Property Firstname As String = ""
Property Lastname As String = ""
End Class
Class Account
Property AccountNumber As String = ""
Property Owners As New List(Of Person)
End Class
I wish to you utilize https://github.com/nickdodd79/AutoBogus to set a range of values from 1,000 to 10,000 for Person.ID when I instantiate an instance of the Account class like so:
Dim fk = AutoFaker.Create()
Dim acct = fk.Generate(Of Account)
Please how may I do this using AutoBogus?
The original design of AutoBogus was to generate small object graphs for unit tests. Generating the numbers you require could have a performance impact. However, if the above is what you are trying to generate and nothing more complex, then it could be small enough to succeed.
To use AutoBogus out the box you can do the following:
Dim acct = AutoFaker.Generate(Of Account, 1000)
The second parameter should be the number of accounts you want to create.
AutoBogus uses Bogus under the hood and then leverages Reflection to populate unset properties. If you do see any performance issues, you could use Bogus directly with the caveat that you need define a RuleFor for each property. The Bogus docs provide in-depth details on how to achieve this.
Nick.
Solution modified from answer provided by Bogus author bchavez at https://github.com/bchavez/Bogus/issues/394.
Sub Main
Dim personFaker = New AutoFaker(Of Person)
personFaker.RuleFor(Function(p) p.Firstname, Function(f) f.Name.FirstName)
.RuleFor(Function(p) p.Lastname, Function(f) f.Name.LastName)
.RuleFor(Function(p) p.ID, Function(f) f.Random.Int(1000,10000).ToString)
Dim accountFaker = New AutoFaker(Of Account)
accountFaker.RuleFor(Function(a) a.AccountNumber, Function(f) f.Random.Replace("###############"))
.RuleFor(Function(a) a.Owners, Function(f) New List(Of Person)(personFaker.GenerateBetween(1,5)))
accountFaker.Generate().Dump()
End Sub
Class Person
Property ID As String = Nothing
Property Firstname As String = ""
Property Lastname As String = ""
End Class
Class Account
Property AccountNumber As String = ""
Property Owners As New List(Of Person)
End Class

Cannot drop a constraint in MS ACCESS

When using the SQL command :
ALTER TABLE [Sessions] DROP CONSTRAINT [SessionAttendance]
I get the exception error message "Could not find reference."
The constraint exists, and shows in the system table of constraints for this user table. How can I get this constraint to drop?
The database is in MS-ACCESS 2003 format. The application uses JET 4.0 I have several hundred instances which will need schema updates. I have a utility program to generate the SQL, but it falls over when attempting the DROP CONSTRAINT action.
Answered by implications of Gord Thompson in comment suggestions.
The ALTER statement was being applied to the wrong table in the relation.
The constraint was originally Added to the Attendance table. However it shows up as an attribute of the Sessions table when using the "GetOleDbSchemaTable" method to list.
Per the following code excerpt:
Structure Relation
Public Name As String
Public PrimaryTableName As String
Public PrimaryField As String
Public PrimaryIndex As String
Public ForeignTable As String
Public ForeignField As String
Public OnUpdate As String
Public OnDelete As String
Public Overrides Function ToString() As String
Dim msg As String = String.Format("Name:{0} PT:{1} PF:{2} PI:{3} FT:{4} FF:{5}", _
Name, PrimaryTableName, PrimaryField, PrimaryIndex, ForeignTable, ForeignField)
Return msg
End Function
End Structure
Private Function ListRelations(tableName As String) As List(Of Relation)
Dim relations As New List(Of Relation)
Dim MySchemaTable As DataTable
Dim dbConn As New OleDbConnection(connectionString)
dbConn.Open()
MySchemaTable = dbConn.GetOleDbSchemaTable(OleDbSchemaGuid.Foreign_Keys, _
New Object() {Nothing, Nothing, tableName})
Dim result As Boolean = False
'List the table name from each row in the schema table.
For Each row As DataRow In MySchemaTable.Rows
Dim r As New Relation
r.Name = row("FK_NAME")
r.PrimaryTableName = row("PK_TABLE_NAME")
r.PrimaryField = row("PK_COLUMN_NAME")
r.PrimaryIndex = row("PK_NAME")
r.ForeignTable = row("FK_TABLE_NAME")
r.ForeignField = row("FK_COLUMN_NAME")
r.OnUpdate = row("UPDATE_RULE")
r.OnDelete = row("DELETE_RULE")
Console.WriteLine(r.ToString)
relations.Add(r)
Next
MySchemaTable.Dispose()
dbConn.Close()
dbConn.Dispose()
Return relations
End Function

EF6 Database First making Stored Procedure Async

What's the right way to run a EF6 stored procedure (database-first) in async mode?
I read about ToListAsync() but I don't see that available on stored procedure.
Also not sure if there is a different way to call the stored procedure when the actual call returns (#1) an OUT param or a (#2) list of items:
Case #1
using (DBContext db = new DBContext())
{
ObjectParameter result = new ObjectParameter("Result",
typeof(global::System.Boolean));
db.Login("email#email.com", "password", result);
}
Case #2
using (DBContext db = new DBContext())
{
var result = db.Contact_GetList("New York");
}
Thanks for the help
As per this workitem you would need to use SqlQueryAsync. Feel free to upvote the work item on the EF Codeplex site.
To map stored procedures and start using it with out writing any initial code, this is how I did it.
create a new model with a new connection string that will generate the connection string automatically in the web.config file where the connection string is at (if you use a current connection string it may no work when you test the function for the SP on the model browser).
map your table and the stored procedures (you can test the stored procedures in the model browser).
create classes that represents the attributes retrieved by each stored procedure e.g if your stored procedure returns three columns A,B,C, then the class must also have these three columns as attribute with the [key()] on top of the column that is going to be the PK
now create your controller with the class created and a new DbContext
then copy the information in the data context generated for the model and pasted in the new context that you generate when creating the controller.
when you want to use the store procedures they will be ready on the db.context because you paste their code on you new db-context that you create when the controller was crated.
NOTE: I hope this is not confusing but I can use the stored procedures with out typing any code, please ask me if you need sample code or screen shots, your new db-context will not over write after you created
This is the stored procedure I map
'--------------------------------------------------------------------------
' <auto-generated>
' This code was generated from a template.
'
' Manual changes to this file may cause unexpected behavior in your application.
' Manual changes to this file will be overwritten if the code is regenerated.
' </auto-generated>
'-----------------------------------------------------------------------
Imports System
Imports System.Collections.Generic
Partial Public Class phone_CurrentConferences_Result
Public Property AppointmentID As Integer
Public Property AppTitle As String
Public Property DateTime As Nullable(Of Date)
Public Property [Date] As String
Public Property Time As String
Public Property Company As String
Public Property Contact As String
Public Property Phone As String
Public Property Office As String
Public Property Lead_Director As String
Public Property TBD As Nullable(Of Boolean)
Public Property conference As String
End Class
This is the same model with a primary key
Imports System
Imports System.Collections.Generic
Imports System.ComponentModel.DataAnnotations
Public Class Conferences
[Key]
Public Property AppointmentID As Integer
Public Property AppTitle As String
Public Property DateTime As Nullable(Of Date)
Public Property [Date] As String
Public Property Time As String
Public Property Company As String
Public Property Contact As String
Public Property Phone As String
Public Property Office As String
Public Property Lead_Director As String
Public Property TBD As Nullable(Of Boolean)
Public Property conference As String
End Class
This is the context generated by the EF
'--------------------------------------------------------------------------
' <auto-generated>
' This code was generated from a template.
'
' Manual changes to this file may cause unexpected behavior in your application.
' Manual changes to this file will be overwritten if the code is regenerated.
' </auto-generated>
'--------------------------------------------------------------------------
Imports System
Imports System.Data.Entity
Imports System.Data.Entity.Infrastructure
Imports System.Data.Entity.Core.Objects
Imports System.Linq
Partial Public Class DayMasterEntities
Inherits DbContext
Public Sub New()
MyBase.New("name=DayMasterEntities")
End Sub
Protected Overrides Sub OnModelCreating(modelBuilder As DbModelBuilder)
Throw New UnintentionalCodeFirstException()
End Sub
Public Overridable Function phone_CurrentConferences(number As String, [date] As Nullable(Of Date)) As ObjectResult(Of phone_CurrentConferences_Result)
Dim numberParameter As ObjectParameter = If(number IsNot Nothing, New ObjectParameter("number", number), New ObjectParameter("number", GetType(String)))
Dim dateParameter As ObjectParameter = If([date].HasValue, New ObjectParameter("date", [date]), New ObjectParameter("date", GetType(Date)))
Return DirectCast(Me, IObjectContextAdapter).ObjectContext.ExecuteFunction(Of phone_CurrentConferences_Result)("phone_CurrentConferences", numberParameter, dateParameter)
End Function
End Class
SO, when I create the controller I use the model with the <KEY()> and I create my own context that will look like this
Imports System.Data.Entity
Imports System.Data.Entity.Infrastructure
Imports System.Data.Entity.Core.Objects
Namespace Models
Public Class DayMasterContext
Inherits DbContext
' You can add custom code to this file. Changes will not be overwritten.
'
' If you want Entity Framework to drop and regenerate your database
' automatically whenever you change your model schema, please use data migrations.
' For more information refer to the documentation:
' http://msdn.microsoft.com/en-us/data/jj591621.aspx
Public Sub New()
MyBase.New("name=DayMasterEntities")
End Sub
Protected Overrides Sub OnModelCreating(modelBuilder As DbModelBuilder)
Throw New UnintentionalCodeFirstException()
End Sub
Public Property Conferences As System.Data.Entity.DbSet(Of Conferences)
End Class
End Namespace
Then I copy the info in the context generated by the EF to my context
Imports System.Data.Entity
Imports System.Data.Entity.Infrastructure
Imports System.Data.Entity.Core.Objects
Namespace Models
Public Class DayMasterContext
Inherits DbContext
' You can add custom code to this file. Changes will not be overwritten.
'
' If you want Entity Framework to drop and regenerate your database
' automatically whenever you change your model schema, please use data migrations.
' For more information refer to the documentation:
' http://msdn.microsoft.com/en-us/data/jj591621.aspx
Public Sub New()
MyBase.New("name=DayMasterEntities")
End Sub
Protected Overrides Sub OnModelCreating(modelBuilder As DbModelBuilder)
Throw New UnintentionalCodeFirstException()
End Sub
Public Overridable Function phone_CurrentConferences(number As String, [date] As Nullable(Of Date)) As ObjectResult(Of phone_CurrentConferences_Result)
Dim numberParameter As ObjectParameter = If(number IsNot Nothing, New ObjectParameter("number", number), New ObjectParameter("number", GetType(String)))
Dim dateParameter As ObjectParameter = If([date].HasValue, New ObjectParameter("date", [date]), New ObjectParameter("date", GetType(Date)))
Return DirectCast(Me, IObjectContextAdapter).ObjectContext.ExecuteFunction(Of phone_CurrentConferences_Result)("phone_CurrentConferences", numberParameter, dateParameter)
End Function
Public Property Conferences As System.Data.Entity.DbSet(Of Conferences)
End Class
End Namespace
So, now you can use this context to query
entConferences(number As String, [date] As Nullable(Of Date)) As ObjectResult(Of phone_CurrentConferences_Result)
or to get a DBSet(of conferences)
Here is a controller I have created with this technique
Look where I call my stored procedure
Dim conferences = db.phone_CurrentConferences(phoneNumber, currentDate)
Imports System.Data
Imports System.Data.Entity
Imports System.Data.Entity.Infrastructure
Imports System.Linq
Imports System.Net
Imports System.Net.Http
Imports System.Web.Http
Imports System.Web.Http.Description
Imports BIWEBAPI
Imports BIWEBAPI.Models
Namespace Controllers.DayMasterControllers
Public Class ConferencesController
Inherits System.Web.Http.ApiController
Private db As New DayMasterContext
' GET: api/Conferences
Function GetConferences() As IQueryable(Of Conferences)
Return db.Conferences
End Function
' GET: api/Conferences/3053742500
''' <summary>
''' Use to get the current conferences selected by date
''' </summary>
''' <param name="id">phone number and date separated by coma ",""</param>
''' <returns>conferences by date</returns>
''' <remarks></remarks>
<ResponseType(GetType(Conferences))>
Function GetConferences(ByVal id As String) As List(Of Conferences)
Dim conferencelist = New List(Of Conferences)
Dim dateAndPhoneNumber = Split(id, ",")
Dim currentDate = ""
Dim phoneNumber = dateAndPhoneNumber(0)
If dateAndPhoneNumber.Length > 1 Then
currentDate = DateTime.Parse(dateAndPhoneNumber(1))
Else : currentDate = DateTime.Today
End If
Dim conferences = db.phone_CurrentConferences(phoneNumber, currentDate)
For Each conferenceInQuery As Object In conferences
Dim conference = New Conferences()
conference.AppointmentID = conferenceInQuery.AppointmentID
conference.AppTitle = conferenceInQuery.AppTitle
conference.DateTime = conferenceInQuery.DateTime
conference.[Date] = conferenceInQuery.[Date]
conference.Time = conferenceInQuery.Time
conference.Company = conferenceInQuery.Company
conference.Contact = conferenceInQuery.Contact
conference.Phone = conferenceInQuery.Phone
conference.Office = conferenceInQuery.Office
conference.Lead_Director = conferenceInQuery.Lead_Director
conference.TBD = conferenceInQuery.TBD
conference.conference = conferenceInQuery.conference
conferencelist.Add(conference)
Next
Return conferencelist
End Function
' PUT: api/Conferences/5
<ResponseType(GetType(Void))>
Function PutConferences(ByVal id As Integer, ByVal conferences As Conferences) As IHttpActionResult
If Not ModelState.IsValid Then
Return BadRequest(ModelState)
End If
If Not id = conferences.AppointmentID Then
Return BadRequest()
End If
db.Entry(conferences).State = EntityState.Modified
Try
db.SaveChanges()
Catch ex As DbUpdateConcurrencyException
If Not (ConferencesExists(id)) Then
Return NotFound()
Else
Throw
End If
End Try
Return StatusCode(HttpStatusCode.NoContent)
End Function
' POST: api/Conferences
<ResponseType(GetType(Conferences))>
Function PostConferences(ByVal conferences As Conferences) As IHttpActionResult
If Not ModelState.IsValid Then
Return BadRequest(ModelState)
End If
db.Conferences.Add(conferences)
db.SaveChanges()
Return CreatedAtRoute("DefaultApi", New With {.id = conferences.AppointmentID}, conferences)
End Function
' DELETE: api/Conferences/5
<ResponseType(GetType(Conferences))>
Function DeleteConferences(ByVal id As Integer) As IHttpActionResult
Dim conferences As Conferences = db.Conferences.Find(id)
If IsNothing(conferences) Then
Return NotFound()
End If
db.Conferences.Remove(conferences)
db.SaveChanges()
Return Ok(conferences)
End Function
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
If (disposing) Then
db.Dispose()
End If
MyBase.Dispose(disposing)
End Sub
Private Function ConferencesExists(ByVal id As Integer) As Boolean
Return db.Conferences.Count(Function(e) e.AppointmentID = id) > 0
End Function
End Class
End Namespace

Explicit Loading of child navigation properties with criteria

Using DBContext in EF5 - after filtering and partial loading based on criteria like a date range.
I'm trying to produce a complete graph or tree of objects - Persons->Events where the only Events that are included are within a date range. All this whilst preserving the standard change tracking that one gets with the following:
Dim Repository As Models.personRepository = New Models.personRepository
Private Sub LoadData()
Dim personViewModelViewSource As System.Windows.Data.CollectionViewSource = CType(Me.FindResource("personViewModelViewSource"), System.Windows.Data.CollectionViewSource)
Repository.endDate = EndDate.SelectedDate
Repository.startDate = StartDate.SelectedDate
personViewModelViewSource.Source = Repository.collectionOfpersons
End Sub
A listbox and a datagrid are both bound as a proper datasource. The POCO template has been modified to put INotifyProperty events in the navigation property class, Events.
I've been fighting with this for days now and filtering whether on Lazy loading or Explicit loading does not function. After masses of blog/chaper reading, I'm aware of the rather unreal limitation relating to Include; instead I'm trying explicit loading. I'm using the DBContext book btw.
Being unable to bring back only a subset of the Event data from the database is 100% deal breaker as there is likely to be hundreds of thousands of Events per Person. It doesn't make sense to me or my boss that the Entity Framework doesn't make this functionality fairly obvious-are we missing something?
I've left in the commented code to try and illustrate some of the paths I've attempted. The class itself is a repository that this method belongs to. I'll edit this question further to clarify just how many routes I've tried as it's been a LOT. The View uses a repository layer and a ViewModel so the code behind on the XAML is rather minimal.
In advance for any help, thank you!
Public Overridable ReadOnly Property AllFiltered(startdate As Date, enddate As Date) As ObservableCollection(Of person) Implements IpersonRepository.AllFiltered
Get
Dim uow = New UnitOfWork
context = uow.Context
Dim personQuery = context.persons.Include(Function(p) p.events).AsQueryable.Where(Function(x) x.personID = 10).FirstOrDefault
'Dim eventQuery = From e In context.Notes
' Where e.eventDateTime >= startdate And e.eventDateTime <= enddate
' Select e
'Dim personQuery As person = From r In context.persons
' From e In eventQuery
' Where r.personID = e.personID
' Select r, e
Dim singleperson = personQuery
'For Each r As person In personQuery
' persons.Add(r)
'Next
' context.Entry(eventQuery).Collection()
' context.Entry(personQuery).Reference(personQuery).Load()
context.Entry(singleperson).Collection(Function(d) d.events).Query().Where(Function(x) x.eventDateTime > startdate And x.eventDateTime < enddate).Load()
Return context.persons.Local
End Get
End Property
Note: I'm using logging/exception handling via PostSharp rather than polluting the code.
Below are some of the errors I've generated with previous paths taken.
The entity type DbQuery`1 is not part of the model for the current context.
The Include path expression must refer to a navigation property defined on the type. Use dotted paths for reference navigation properties and the Select operator for collection navigation properties.
Parameter name: path
Unable to cast object of type 'System.Data.Entity.Infrastructure.DbQuery1[VB$AnonymousType_02
[Entity.Person,Entity.Notes]]' to type 'System.Collections.ObjectModel.ObservableCollection`1[Entity.Person]'.
UPDATE: Yet another route I've tried, still cannot get this to fly either:
Private Property _collectionOfPersons As ObservableCollection(Of Person)
Public ReadOnly Property collectionOfPersons As ObservableCollection(Of Person)
Get
For Each Person In context.Persons
_collectionOfPersons.Add(ReturnSinglePerson(startDate, endDate, Person.PersonID))
Next
Return _collectionOfPersons.Where(Function(x) x.events.Where(Function(e) e.eventDateTime > startDate And e.eventDateTime < endDate))
End Get
End Property
Public Overridable ReadOnly Property SinglePerson(startdate As Date, enddate As Date) As ObservableCollection(Of Person) Implements IPersonRepository.AllFiltered
Get
Dim PersonQuery = context.Persons.Include(Function(p) p.events).AsQueryable.Where(Function(x) x.PersonID = 10).Select(Function(x) x).FirstOrDefault
Dim Person = PersonQuery
context.Entry(Person).Collection(Function(d) d.events).Query().Where(Function(x) x.eventDateTime > startdate And x.eventDateTime < enddate).Load()
Return context.Persons.Local
End Get
End Property
Public Function ReturnSinglePerson(startdate As Date, enddate As Date, id As Integer)
Dim PersonQuery = context.Persons.Include(Function(p) p.events).AsQueryable.Where(Function(x) x.PersonID = id).Select(Function(x) x).FirstOrDefault
Dim Person = PersonQuery
Return Person
End Function
Another shot:
Public Overridable ReadOnly Property FilteredPersons(startdate As Date, enddate As Date) As ObservableCollection(Of Person) Implements IPersonRepository.AllFiltered
Get
context.Persons.Load()
Dim DateCriteria = Function(e) e.events.Where(Function(d) d.eventDateTime > startdate And d.eventDateTime < enddate)
Dim Res = New ObservableCollection(Of Person)
For Each Person In context.Persons.Local.Select(Function(x) x).Where(DateCriteria)
Res.Add(Person)
Next
Return Res
End Get
End Property
Gives:
Public member 'Where' on type 'ObservableCollection(Of DailyNotes)' not found.
Tantalisingly close, only I get lots of duplicate names on the listbox - but the navigation carries through and the Date criteria work.
<ExceptionAspect>
Public Overridable ReadOnly Property FilteredPersons(startdate As Date, enddate As Date) As ObservableCollection(Of Person) Implements IPersonRepository.AllFiltered
Get
context.Persons.Load()
Dim test = From r In context.Persons
From e In context.Notes
Where e.eventDateTime > startdate And e.eventDateTime < enddate
Join rr In context.Persons On e.PersonID Equals rr.PersonID
Select r, e
Dim Res = New ObservableCollection(Of Person)
For Each Person In test
Res.Add(Person.r)
Next
Return Res
End Get
End Property
Don't try this one :). It simply selects the child properties only.
Public ReadOnly Property collectionOfResidents As ObservableCollection(Of resident)
Get
For Each resident In context.residents
_collectionOfResidents.Add(ReturnSingleResident(startDate, endDate, resident.residentID))
Next
Return _collectionOfResidents.Select(Function(x) x.events.Where(Function(e) e.eventDateTime > startDate And e.eventDateTime < endDate))
End Get
End Property
I'm hoping that adding my other attempts to this question may prompt both other answers and help others see the circles they can get into when first tackling this!
You can use the Select clause for finer control than with Include
Something like this:
context
.Persons
.Where( ... some predicate on Person ... )
.Select( o => new
{
Person = o,
Events = o.Events.Where( ... some predicate on Event ... )
}
)
;
This will translate both predicates into SQL which execute on the database server.
Ok after a LOT of fiddling and misunderstanding of anonymous types this evening, I think I succeeded. Nicholas's answer just needed to be done in VB which took me a while - I've not used anonymous types before.
This is what appears to work fine in my repository layer:
<ExceptionAspect>
Public Overridable ReadOnly Property FilteredPersons(startdate As Date, enddate As Date) As ObservableCollection(Of Person) Implements IPersonRepository.AllFiltered
Get
context.Persons.Load()
Dim test = context.Persons.Where(Function(r) r.PersonActive).Select(Function(o) New With { _
.Person = o, _
.events = o.events.Where(Function(e) e.eventDateTime > startdate) _
})
Dim PersonList= New ObservableCollection(Of Person)
For Each t In test
PersonList.Add(t.person)
Next
Return PersonList
End Get
End Property
The crucial updating/saving in the wpf View is intact, I'm really happy and grateful to Nicholas for his help on here (and patience...re:cartesion product). So thank you. I hope this helps someone else!

How to do Multiple joins in EF, and load datagrid, using VB

I'm trying to load a grid using EF objects.
select * from
[session] inner join [sessionrep] on [session].ID = [sessionrep].repid
inner join [group] on [group].id = [session].groupid
where [sessionrep].repid = x
I created a partial class and added the properties I wanted to display in the grid in there:
Partial Public Class SessionRep
Public ReadOnly Property Name() As String
Get Return Session.Name
End Get
End Property
....
End Class
I tried doing this, but it's only loading the table's Active field, the other fields are blank, although the number of rows appear right.
grdSessions.DataSource = db.SessionReps.Include("Session").Include("Session.Group").Include("Session.Group.Program").Where(Function(r) r.RepID = repID).ToList()
What am I doing wrong ? Thanks.
No need for partial classes.
Dim query =
From s In db.Sessions
From d In db.SessionReps
Where s.ID = d.RepID And d.RepID = repID
Select s
grdSessions.DataSource = query.ToList()
grdSessions.DataBind()