efcore / efcore.fsharp Goto Github PK
View Code? Open in Web Editor NEWAdds F# design-time support to EF Core
License: MIT License
Adds F# design-time support to EF Core
License: MIT License
Is your feature request related to a problem? Please describe.
Is there any way to avoid requiring users to manually add their own IDesignTimeServices implementation?
Describe the solution you'd like
The EFCore.VisualBasic project includes an MSBuild file in the nupkg that adds a source file to the project which enables dotnet ef
to discover the design-time services automatically. Can this approach be replicated here?
Describe alternatives you've considered
None. This is the recommended pattern for design-time extensions.
Additional context
Available upon request. 😏
Describe the bug
A string literal array is generated as System.String[]
not as the actual values, e.g. [| "A", "B" |]
.
To Reproduce
Scaffold from an existing database that will require a string literal array, e.g. enumeration.
Expected behavior
Array values generated correctly and able to be compiled.
The Travis CI build is linked to bricelam/EFCore.FSharp, to restore build tests for it we can create a new build pointing to efcore/EFCore.FSharp but I don't have the necessary permissions to do that
Attempting to register optional properties automatically (#24) results in the following error in the Northwind database
System.InvalidOperationException:
The relationship from 'Products.Category' to 'Categories.Products' with foreign key properties {'CategoryId' : FSharpOption<short>} cannot target the primary key {'CategoryId' : short} because it is not compatible.
Configure a principal key or a set of compatible foreign key properties for this relationship.
Optional navigation properties need to be supported, mapping from int option
to int
and TEntity option
to TEntity
will need to be added
Hi @bricelam may I know any plan to create some sample projects?
Thanks
When generating migrations for sqlite, some columns have a "type" parameter. Since the function is called using named parameters, you end up with: table.Column<decimal>(nullable = false,type = "decimal(10, 0)", defaultValueSql = "('0')")
. This generates a FS0010: Incomplete structured construct error, since type is a reserved keyword.
The only thing I can think of is just to call the function with all arguments. Per MSDN, "Note that for nullable parameters a null value means not-specified". However when I pass nulls for the arguments I didnt need, I get a "no overloads match" error. So I had to come up with values.
Column: member val SynsetId:decimal = 0.0m with get, set
Generated: table.Column<decimal>(nullable = false,type = "decimal(10, 0)", defaultValueSql = "('0')")
No overloads match: synset_id = table.Column<decimal>("decimal(10, 0)", null, null, false, "synset_id", false, null, null, null)
Compiles: table.Column<decimal>("decimal(10, 0)", Nullable(false), Nullable(10), false, "synset_id", false, "('0')", "('0')", "('0')")
After migration we should change namespaces and package names from Bricelam.EntityFrameworkCore.FSharp to EFCore.FSharp
What are the things that still need to happen before this could be published to NuGet, and do you need any help with that? Would love to use this in projects in the future and if you could use it I would be happy to help out :)
An option
type indicates nullability, it is not to be interpreted as an entity type by itself
System.InvalidOperationException: The entity type 'FSharpOption<string>' requires a primary key to be defined.
at Microsoft.EntityFrameworkCore.Infrastructure.ModelValidator.ValidateNonNullPrimaryKeys(IModel model)
at Microsoft.EntityFrameworkCore.Infrastructure.ModelValidator.Validate(IModel model)
at Microsoft.EntityFrameworkCore.Infrastructure.RelationalModelValidator.Validate(IModel model)
at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ValidatingConvention.Apply(InternalModelBuilder modelBuilder)
at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.ImmediateConventionScope.OnModelBuilt(InternalModelBuilder modelBuilder)
at Microsoft.EntityFrameworkCore.ModelBuilder.FinalizeModel()
at System.Lazy`1.ViaFactory(LazyThreadSafetyMode mode)
at System.Lazy`1.ExecutionAndPublication(LazyHelper executionAndPublication, Boolean useDefaultConstructor)
at System.Lazy`1.CreateValue()
at Microsoft.EntityFrameworkCore.Internal.DbContextServices.CreateModel()
at Microsoft.EntityFrameworkCore.Internal.DbContextServices.get_Model()
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitFactory(FactoryCallSite factoryCallSite, ServiceProviderEngineScope scope)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(IServiceCallSite callSite, TArgument argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScoped(ScopedCallSite scopedCallSite, ServiceProviderEngineScope scope)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(IServiceCallSite callSite, TArgument argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, ServiceProviderEngineScope scope)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(IServiceCallSite callSite, TArgument argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScoped(ScopedCallSite scopedCallSite, ServiceProviderEngineScope scope)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(IServiceCallSite callSite, TArgument argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine.<>c__DisplayClass1_0.<RealizeService>b__0(ServiceProviderEngineScope scope)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngine.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.GetService(Type serviceType)
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)
at Microsoft.EntityFrameworkCore.DbContext.get_DbContextDependencies()
at Microsoft.EntityFrameworkCore.DbContext.get_InternalServiceProvider()
at Microsoft.EntityFrameworkCore.Internal.InternalAccessorExtensions.GetService[TService](IInfrastructure`1 accessor)
at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.CreateContext(Func`1 factory)
at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.CreateContext(String contextType)
at Microsoft.EntityFrameworkCore.Design.Internal.MigrationsOperations.AddMigration(String name, String outputDir, String contextType)
at Microsoft.EntityFrameworkCore.Design.OperationExecutor.AddMigrationImpl(String name, String outputDir, String contextType)
at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.<>c__DisplayClass3_0`1.<Execute>b__0()
at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.Execute(Action action)
The entity type 'FSharpOption<string>' requires a primary key to be defined.
With PostgreSQL an open statement for Npgsql.EntityFrameworkCore.PostgreSQL.Metadata
is required or the generated code will not build.
A newly-created migration has an open
statement at the top of the file but no namespace or module. This isn't valid F# and must be deleted before it compiles.
I'll set up the initial project to create the NuGet package and stub out the implementation.
After you create a migration, you have to manually add it to the solution in order for the dotnet ef migration command to work, otherwise it doesn't detect it.
A simple type:
[<CLIMutable>]
type Country =
{
[<Key>]
[<MaxLength(3)>]
IsoCode: string
EnglishName: String
NativeName: String
}
Given a list of values, I add them in DbContext:
override _.OnModelCreating(builder: ModelBuilder): unit =
// Add all countries to the DB
for country in CountryUtils.getAllCountries do
builder.Entity<Country>().HasData(country) |> ignore
base.OnModelCreating(builder)
The generated migrations code looks invalid:
modelBuilder.Entity("Omnicv.Common.Db.Country", (fun b ->
b.Property<string>("IsoCode")
.HasMaxLength(3)
.HasColumnType("TEXT") |> ignore
b.Property<string>("EnglishName")
.HasColumnType("TEXT") |> ignore
b.Property<string>("NativeName")
.HasColumnType("TEXT") |> ignore
b.HasKey("IsoCode") |> ignore
b.HasIndex("IsoCode")
.IsUnique() |> ignore
b.ToTable("Countries") |> ignore
b.HasData([|
({
,
IsoCode = "abw"EnglishName = "Aruba"NativeName = "Aruba"} :> obj)
|> ignore
({
,
IsoCode = "afg"EnglishName = "Afghanistan"NativeName = "Afghanistan"} :> obj)
|> ignore
({
,
IsoCode = "ago"EnglishName = "Angola"NativeName = "Angola"} :> obj)
|> ignore
({
,
Describe the bug
open System
is not included in new migrations. This results in uses of Nullable
, DateTime
, and other common types to lack definition references and compilation to fail.
To Reproduce
Steps to reproduce the behavior:
Nullable
type will be included).Expected behavior
open System
should be included in any autogenerated file that requires the System
namespace to compile.
Additional context
This bug affects version 5.0.3-alpha2
The project currently has poor unit test coverage and this has to be improved
Adding this ticket to track this work
Should this package start at version 1.0.0 or should the version somehow indicate that it's compatible with EF Core 2.1+
What does design time support
means in terms of EF.Core?
Could you add some explanation, please?
What can I do with this package?
Create migrations and scaffold database?
Trying to define a foreign key relationship as option causes the exception below. Is using option the right way to define nullable foreign key?
System.InvalidOperationException: The entity type 'FSharpOption<OmnicvUser>' requires a primary key to be defined. If you intended to use a keyless entity type, call 'HasNoKey' in 'OnModelCreating'. For more information on keyless entity types, see https://go.microsoft.com/fwlink/?linkid=2141943.
at Microsoft.EntityFrameworkCore.Infrastructure.ModelValidator.ValidateNonNullPrimaryKeys(IModel model, IDiagnosticsLogger`1 logger)
at Microsoft.EntityFrameworkCore.Infrastructure.ModelValidator.Validate(IModel model, IDiagnosticsLogger`1 logger)
at Microsoft.EntityFrameworkCore.Infrastructure.RelationalModelValidator.Validate(IModel model, IDiagnosticsLogger`1 logger)
at Microsoft.EntityFrameworkCore.Sqlite.Internal.SqliteModelValidator.Validate(IModel model, IDiagnosticsLogger`1 logger)
at Microsoft.EntityFrameworkCore.Metadata.Conventions.ValidatingConvention.ProcessModelFinalized(IModel model)
at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.ImmediateConventionScope.OnModelFinalized(IModel model)
at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.OnModelFinalized(IModel model)
at Microsoft.EntityFrameworkCore.Metadata.Internal.Model.FinalizeModel()
at Microsoft.EntityFrameworkCore.ModelBuilder.FinalizeModel()
See PR dotnet/efcore#13440. Languages will need to be able to generate code for a few prescribed Expression
patterns.
Describe the bug
Scaffolding from an existing database causes strange indentation:
open System
open System.Collections.Generic
open Microsoft.EntityFrameworkCore
open Microsoft.EntityFrameworkCore.Metadata
open EntityFrameworkCore.FSharp.Extensions
open TestDbDomain
type TestDbContext =
inherit DbContext
new() = { inherit DbContext() }
new(options : DbContextOptions<TestDbContext>) =
{ inherit DbContext(options) }
override this.OnConfiguring(optionsBuilder: DbContextOptionsBuilder) =
if not optionsBuilder.IsConfigured then
optionsBuilder.UseSqlServer("Initial Catalog=TestDatabase") |> ignore
()
override this.OnModelCreating(modelBuilder: ModelBuilder) =
base.OnModelCreating(modelBuilder)
modelBuilder.RegisterOptionTypes()
To Reproduce
View the source code that is passing the test in FSharpDbContextGeneratorTest
Expected behavior
Code to look like:
open System
open System.Collections.Generic
open Microsoft.EntityFrameworkCore
open Microsoft.EntityFrameworkCore.Metadata
open EntityFrameworkCore.FSharp.Extensions
open TestDbDomain
type TestDbContext =
inherit DbContext
new() = { inherit DbContext() }
new(options : DbContextOptions<TestDbContext>) =
{ inherit DbContext(options) }
override this.OnConfiguring(optionsBuilder: DbContextOptionsBuilder) =
if not optionsBuilder.IsConfigured then
optionsBuilder.UseSqlServer("Initial Catalog=TestDatabase") |> ignore
()
override this.OnModelCreating(modelBuilder: ModelBuilder) =
base.OnModelCreating(modelBuilder)
modelBuilder.RegisterOptionTypes()
When creating a column, the unicode, maxLength, and fixedLength parameters need to be wrapped in Nullable(). I am generating migrations for sqlite.
Column: member val Gloss:string = "" with get, set
Example before: table.Column<string>(unicode = false)
after: table.Column<string>(unicode = Nullable(false))
Describe the bug
Scaffold project from an existing database schema causes un-compilable HasIndex
calls.
To Reproduce
Steps to reproduce the behavior:
dotnet ef dbcontext scaffold <connection string> <ef provider>
HasIndex(...)
:entity
.HasIndex(e.EntityId)
Expected behavior
Generated code should be:
entity
.HasIndex("EntityId")
or
entity
.HasIndex(fun e -> e.EntityId :> obj)
Create a basic readme that explains:
This would greatly assist people who come across this repository.
Looks like the timestamped file from migrations has incorrect indentations, starting from the second table, and that causes to compilation errors, naturally.
First CreateTable is at column 9, but the second is at 13:
The next CreateTable is already at column 17 and so on. I could edit the file manually, but it would be better to fix the generator :)
Adding new types to DbContext, your intro suggests this type of syntax
[<DefaultValue>] val mutable countries : DbSet<Country>
member __.Countries with get() = __.countries and set v = __.countries <- v
Wouldn't it be the same to use this syntax from practical point of view?
member val Countries: DbSet<Country> = null with get, set
As far as migrations generation, both seem to work fine...
We'll need to implement IMigrationsCodeGenerator
to support adding a new migration.
On checking out master, doing a dotnet build
gives the following errors:
error MSB4184: The expression "registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0@InstallationFolder" cannot be evaluated.
warning NU1503: Skipping restore for project 'C:\Users\Isaac\Source\Repos\EFCore.FSharp\EFCore.FSharp\EFCore.FSharp.fsproj'. The project file may be invalid or missing targets required for restore. [C:\Users\Isaac\Source\Repos\EFCore.FSharp\EFCore.FSharp.sln]
error MSB4184: The expression "registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0@InstallationFolder" cannot be evaluated.
Restore completed in 46.54 ms for C:\Users\Isaac\Source\Repos\EFCore.FSharp\EFCore.FSharp.Test\EFCore.FSharp.Test.fsproj.
error MSB4184: The expression "registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0@InstallationFolder" cannot be evaluated.
error MSB4184: The expression "registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0@InstallationFolder" cannot be evaluated.
How can I fix this?
To Reproduce
I have a rather simple entity, defined in project A. I am trying to create migrations in project B, which has A as a reference. I am also trying use ASP.NET Identity with a custom user implementation, if that affects anything.
Entity:
[<CLIMutable>]
type Module =
{
Id: int
Name: string
Version: string
CreatedAt: DateTime
LastUpdated: DateTime
}
DbContext:
type ApplicationDbContext(options: DbContextOptions<ApplicationDbContext>) =
inherit IdentityDbContext<OmnicvUser, IdentityRole<int>, int>(options)
override _.OnConfiguring(options: DbContextOptionsBuilder) : unit =
base.OnConfiguring(options)
options
.UseSqlite("Data Source=db.sqlite3")
|> ignore
override _.OnModelCreating(builder: ModelBuilder): unit =
let types = [typedefof<Module>]
let assemblies =
types |> List.map (fun t -> Assembly.GetAssembly(t))
|> List.distinct
List.fold (fun (b: ModelBuilder) a -> b.ApplyConfigurationsFromAssembly(a)) builder assemblies
|> ignore
base.OnModelCreating(builder)
[<DefaultValue>] val mutable modules : DbSet<Module>
member __.Modules with get() = __.modules and set v = __.modules <- v
Seems quite straightforward. Id should be Module's key by convention, according to the docs. But when running migrations I get this exception:
System.InvalidOperationException: The entity type 'CustomAttributeData' requires a primary key to be defined. If you intended to use a keyless entity type, call 'HasNoKey' in 'OnModelCreating'. For more information on keyless entity types, see https://go.microsoft.com/fwlink/?linkid=2141943.
at Microsoft.EntityFrameworkCore.Infrastructure.ModelValidator.ValidateNonNullPrimaryKeys(IModel model, IDiagnosticsLogger`1 logger)
at Microsoft.EntityFrameworkCore.Infrastructure.ModelValidator.Validate(IModel model, IDiagnosticsLogger`1 logger)
at Microsoft.EntityFrameworkCore.Infrastructure.RelationalModelValidator.Validate(IModel model, IDiagnosticsLogger`1 logger)
at Microsoft.EntityFrameworkCore.Sqlite.Internal.SqliteModelValidator.Validate(IModel model, IDiagnosticsLogger`1 logger)
at Microsoft.EntityFrameworkCore.Metadata.Conventions.ValidatingConvention.ProcessModelFinalized(IModel model)
at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.ImmediateConventionScope.OnModelFinalized(IModel model)
at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.OnModelFinalized(IModel model)
at Microsoft.EntityFrameworkCore.Metadata.Internal.Model.FinalizeModel()
at Microsoft.EntityFrameworkCore.ModelBuilder.FinalizeModel()
at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.CreateModel(DbContext context, IConventionSetBuilder conventionSetBuilder, ModelDependencies modelDependencies)
at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.GetModel(DbContext context, IConventionSetBuilder conventionSetBuilder, ModelDependencies modelDependencies)
at Microsoft.EntityFrameworkCore.Internal.DbContextServices.CreateModel()
at Microsoft.EntityFrameworkCore.Internal.DbContextServices.get_Model()
at Microsoft.EntityFrameworkCore.Infrastructure.EntityFrameworkServicesBuilder.<>c.<TryAddCoreServices>b__7_3(IServiceProvider p)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitFactory(FactoryCallSite factoryCallSite, RuntimeResolverContext context)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite singletonCallSite, RuntimeResolverContext context)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite singletonCallSite, RuntimeResolverContext context)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine.<>c__DisplayClass1_0.<RealizeService>b__0(ServiceProviderEngineScope scope)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngine.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.GetService(Type serviceType)
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)
at Microsoft.EntityFrameworkCore.DbContext.get_DbContextDependencies()
at Microsoft.EntityFrameworkCore.DbContext.get_InternalServiceProvider()
at Microsoft.EntityFrameworkCore.DbContext.Microsoft.EntityFrameworkCore.Infrastructure.IInfrastructure<System.IServiceProvider>.get_Instance()
at Microsoft.EntityFrameworkCore.Infrastructure.Internal.InfrastructureExtensions.GetService[TService](IInfrastructure`1 accessor)
at Microsoft.EntityFrameworkCore.Infrastructure.AccessorExtensions.GetService[TService](IInfrastructure`1 accessor)
at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.CreateContext(Func`1 factory)
at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.CreateContext(String contextType)
at Microsoft.EntityFrameworkCore.Design.Internal.MigrationsOperations.AddMigration(String name, String outputDir, String contextType, String namespace)
at Microsoft.EntityFrameworkCore.Design.OperationExecutor.AddMigrationImpl(String name, String outputDir, String contextType, String namespace)
at Microsoft.EntityFrameworkCore.Design.OperationExecutor.AddMigration.<>c__DisplayClass0_0.<.ctor>b__0()
at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.<>c__DisplayClass3_0`1.<Execute>b__0()
at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.Execute(Action action)
Expected behavior
I would expect the migration to succeed
We'll need to implement IScaffoldingCodeGenerator
to support scaffolding a model from the database.
I was under impression that by default fields like string, byte[] etc would be automatically flagged as IsRequired. However, a simple type like this:
[<CLIMutable>]
type CoreEventAttachment =
{
Id: int64
CoreEventInfoId: int64
CoreEventInfo: CoreEventInfo
CreatedAt: DateTime
LastUpdatedAt: DateTime
[<Required>]
MimeType: string
[<Required>]
Attachment: byte[]
}
Has Attachment and MimeType fields migration code without IsRequired - when no "Required" attribute is stamped, of course. Interestingly, all DateTime fields are flagged as IsRequired automatically.
I looked at the code, I can see that the decision to flag is made here, but I'm not entirely sure what the logic is exactly.
Created a basic EF context (blogs and posts). Trying to create a DB from it:
build.cmd
dotnet ef migrations add InitialCreate
.What am I doing wrong?
A migration was generated with the inconsistent code below:
migrationBuilder.AlterColumn<int>(
name = "exchange_transaction_id"
, table = "exchange_transactions1", nullable = false, oldType = "typedefof<int>", oldType = "Nullable(integer)", oldNullable = false)
.OldAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn) |> ignore
Notice there is two 'oldType' arguments. I searched in Microsoft docs and found this method signature. Than I found a similar argument called 'oldClrType' and changed oldType = "typedefof<int>"
to oldClrType = typedefof<int>
and the migration code could be executed.
I don't know why. It was pure intuition. I'm just reporting to let you know.
Set up Travis build and configure config
This was brought to my attention in this thread: https://forums.fsharp.org/t/how-to-work-with-byref-and-pipe-operator/417/7
In the migrations branch, it appears there is usage of byref
. Of that, there are 14 places where that usage is invalid; i.e., it produces code that may run, but is not necessarily going to run. These seem to all be of the following variety:
let foo (x: int byref) s =
x <- x + 1
printfn "%s %d" s x
let mutable x = 0
"hello" |> foo &x
This is explicitly disallowed, because |>
is a generic function. It is not allowable to parameterize a type with a byref
, i.e., foo<byref<Bar>>
, yet this would occur if it were allowed.
So, there are two options here:
"hello" |> foo &x
equivalent is made to foo &x "hello"
, which will not attempt to specialize |>
to a byref<Foo>
ref
cell.Both are equally as invasive, though I would say that a ref
cell is probably easier. For example, this:
let private prependLine (addLineBreak: bool byref) (text:string) (sb:IndentedStringBuilder) =
if addLineBreak then
sb |> appendEmptyLine |> ignore
else
addLineBreak <- true
sb |> append text |> ignore
let appendLines (lines: string seq) skipFinalNewLine (sb:IndentedStringBuilder) =
let mutable addLineBreak = false
lines |> Seq.iter(fun l -> sb |> prependLine &addLineBreak l)
if skipFinalNewLine then
sb
else
sb |> appendEmptyLine
Becomes this:
let private prependLine (addLineBreak: bool ref) (text:string) (sb:IndentedStringBuilder) =
if addLineBreak.Value then
sb |> appendEmptyLine |> ignore
else
addLineBreak := true
sb |> append text |> ignore
let appendLines (lines: string seq) skipFinalNewLine (sb:IndentedStringBuilder) =
let addLineBreak = ref false
lines |> Seq.iter(fun l -> sb |> prependLine addLineBreak l)
if skipFinalNewLine then
sb
else
sb |> appendEmptyLine
As far as I can tell, the other places this pattern is used could pass a reference cell instead, but a rewrite to not use pipelines with byref
s is also possible.
Both scaffolding and creating migrations results in new files being added to the project
Unlike C# the compilation order of F# is important so we will need to add them to the fsproj in the appropriate place within the project
Currently any generated files have to be added to the fsproj file manually
Set up Appveyor build and configure config
How do you use this to generate migrations in an Fsharp project? I built the code (Edited global.json to use dotnet core 3.0) but I don't see an assembly in the resultant artifacts.
First of all, thank you very much for this project.
Very useful.
I'm a newcomer in the F# world and I'm trying to use EF along with F# models. I could make trough every step in the Getting Started section successfully. But I do not have enough knowledge to put pieces together yet. So I would like to ask you to add a simple CRUD example with the created Post model in the Getting Started article.
Thank you.
Need to add <DevelopmentDependency>true</DevelopmentDependency>
to the project, or use a nuspec if that doesn't work.
Describe the bug
Unable to access record fields via lambdas via EntityTypeBuilder - fails at run time during migration.
To Reproduce
Steps to reproduce the behavior:
Create a IEntityTypeConfiguration class for a record EF Core F# type.
Inside Configure(builder: EntityTypeBuilder try to access a property via lambda:
builder.Property(fun x -> x.FieldName)
Register this new IEntityTypeConfiguration in DbContext
Compile the projects (success)
Try to add a new migration
Observe the following exception:
System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
---> System.ArgumentException: The expression 'delegateArg0 => value(Omnicv.DataModule.CoreData+userIdColumn@45).Invoke(delegateArg0)' is not a valid member access expression. The expression should represent a
simple property or field access: 't => t.MyProperty'. (Parameter 'memberAccessExpression')
at Microsoft.EntityFrameworkCore.Infrastructure.ExpressionExtensions.GetInternalMemberAccess[TMemberInfo](LambdaExpression memberAccessExpression)
at Microsoft.EntityFrameworkCore.Infrastructure.ExpressionExtensions.GetMemberAccess(LambdaExpression memberAccessExpression)
at Microsoft.EntityFrameworkCore.Metadata.Builders.EntityTypeBuilder1.Property[TProperty](Expression
1 propertyExpression)
Expected behavior
A new migration created successfully
Describe the bug
Module opening is not generated for an entities for migrations
To Reproduce
Steps to reproduce the behavior:
dotnet new -i "giraffe-template::*"
dotnet new giraffe -o smthProject
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<AssemblyName>Giraffe.App</AssemblyName>
<EnableDefaultContentItems>false</EnableDefaultContentItems>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="EntityFrameworkCore.FSharp" Version="5.0.3-alpha9" />
<PackageReference Include="Giraffe" Version="5.0.0-rc-6" />
<PackageReference Include="Giraffe.ViewEngine" Version="1.3.*" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.5" />
<PackageReference Include="Ply" Version="0.3.*" />
</ItemGroup>
<ItemGroup>
<Compile Include="DesignTimeServices.fs" />
<Compile Include="Entities.fs" />
<Compile Include="BloggingContext.fs" />
<Compile Include="Migrations\20210507180335_Initial.fs" />
<Compile Include="Migrations\BloggingContextModelSnapshot.fs" />
<Compile Include="Program.fs" />
</ItemGroup>
<ItemGroup>
<None Include="web.config" CopyToOutputDirectory="PreserveNewest" />
<Content Include="WebRoot\**\*">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>
module Giraffe.Entities
open System.ComponentModel.DataAnnotations
open Microsoft.AspNetCore.Identity
[<AllowNullLiteral>]
type StoreUser() =
inherit IdentityUser()
member val FirstName = "" with get, set
member val LastName = "" with get, set
type [<CLIMutable>] Order =
{
[<Key>] Id : int
OrderNumber : string
[<DefaultValue>] User : StoreUser
}
module Giraffe.BloggingContext
open Giraffe.Entities
open Microsoft.EntityFrameworkCore
type BloggingContext () =
inherit DbContext()
member _.Orders : DbSet<Order> = Unchecked.defaultof<DbSet<Order>>
override _.OnConfiguring (options: DbContextOptionsBuilder) =
options.UseSqlite("Data Source=blogging.db") |> ignore
override _.OnModelCreating (modelBuilder : ModelBuilder) =
base.OnModelCreating(modelBuilder)
modelBuilder
.Entity<Order>()
.HasData({
Id = 1
OrderNumber = "12345" })
|> ignore
Expected behavior
dotnet ef database update
will execute successfully
I'm trying to build this repo in Ubuntu 20.04 but I get the following error:
/usr/share/dotnet/sdk/3.1.402/Sdks/NuGet.Build.Tasks.Pack/build/NuGet.Build.Tasks.Pack.targets(198,5): error MSB4181: The "PackTask" task returned false but did not log an error.
Can you help me?
Describe the bug
The constraints
argument is missing in most if not all instances of generated migrationBuilder.CreateTable
calls. Especially table.PrimaryKey
types.
I believe #68 may be related to this. That deals with conventional detection of PK, but it seems to be all PK constraints are actually missing in the 5.0.3-alpha2
release, even those that are manually demarked with the [<Key>]
annotation.
To Reproduce
Steps to reproduce the behavior:
[<Key>]
in type definitionconstraints
argument in CreateTable
callKey
was int
type observe failure due to the inclusion of AUTO INCREMENT
that is illegal in many databases engines outside of PK.Expected behavior
Inclusion of a clause similar to that seen in older 3.1.12
release
//EntityFrameworkCore.FSharp version 3.1.12
override this.Up(migrationBuilder:MigrationBuilder) =
migrationBuilder.CreateTable(
name = "OrderNumbers"
,columns = (fun table ->
{
ID = table.Column<int>(nullable = false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn)
OrderNumber = table.Column<string>(nullable = true, maxLength = Nullable(10))
TSO = table.Column<bool>(nullable = false)
Treated = table.Column<bool>(nullable = false)
Items = table.Column<int>(nullable = false)
PiecesReceived = table.Column<int>(nullable = false)
PiecesTreated = table.Column<int>(nullable = false)
PiecesUnReleased = table.Column<int>(nullable = false)
Memo = table.Column<string>(nullable = true)
})
,constraints =
(fun table ->
table.PrimaryKey("PK_OrderNumbers", (fun x -> (x.ID) :> obj)) |> ignore
)
) |> ignore
These models were related types are all stored in one database table using a discrimator column would traditionally be modelled in C# using inheritance
They should be modelled as Discrimated Unions in F#
Example:
C#
public abstract class Person {}
public class Employee : Person {}
public class Manager : Person {}
F#
type Person =
| Employee of Employee
| Manager of Manager
I have tried setting up a simple two way nav property between some tables.
All the tables are created and indexes are added to the foreign key columns, however no foreign key contraint is added.
I can see some stuff to do with foreign keys in the generated migration file, but it isn't getting applied to the database.
When you check-out the EFCore.FSharp code through Git, Git will by default convert the Unix-style line endings (LF) from the code in the repository to Windows-style line endings (CRLF).
This behaviour can be configured globally in Git (also per repository). See Github doc on configuring Git to handle line endings.
When you have Git configured to leave the line endings as-is — or when you have downloaded the code as a .zip-file from the GitHub website and you have unzipped the .zip-file without converting line endings, as .zip-unpacking software tends not to do —
then .\build.cmd
fails. The .nupkg file in the dist directory is not built.
Build log:
Build Time Report
---------------------------------------------------------------------
Target Duration
------ --------
Clean 00:00:00.0293730
DotnetRestore 00:00:11.4812587
DotnetBuild 00:00:24.2832148
DotnetTest 00:00:23.6683834 (Exception of type 'Fake.DotNet.MSBuildException' was thrown.)
GenerateCoverageReport 00:00:00 (skipped)
DotnetPack 00:00:00 (skipped)
Total: 00:00:59.5815281
Status: Failure
---------------------------------------------------------------------
Script reported an error:
-> BuildFailedException: Target 'DotnetTest' failed.
-> One or more errors occurred. (Exception of type 'Fake.DotNet.MSBuildException' was thrown.)
-> MSBuildException: Exception of type 'Fake.DotNet.MSBuildException' was thrown.
In fact, it is the tests in FSharpMigrationsGeneratorTest.Migrations
(tests\EFCore.FSharp.Tests\Migrations\Design\FSharpMigrationsGeneratorTest.fs) that fail.
The reason is that the tests contain multi-line strings describing what content generated files should have. The generated files are generated with native line endings; and the line endings in the expected value described by the multi-line strings are those of the FSharpMigrationsGeneratorTest.fs file.
Working workaround:
unix2dos tests\EFCore.FSharp.Tests\Migrations\Design\FSharpMigrationsGeneratorTest.fs
before .\build.cmd
Even only changing the line endings in the multi-line strings to CRLF suffices.
Solutions:
The F# Discriminated Union (DU) is a sum type where a type can contain a value of (e.g.) type A
or B
, but not both, for instance
type UserType =
| User
| Admin
The constituent types can be empty like above, or can have associated values, including plain types, complex types like records, or even other union types
type Person = {Id: int; First:string; Last:string} // define a record type
type IntOrBool = I of int | B of bool
type MixedType =
| Tup of int * int // a tuple
| P of Person // use the record type defined above
| L of int list // a list of ints
| U of IntOrBool // use the union type defined above
EFCore.FSharp needs to be able to handle these types and
Each DU should be mapped to a table with a discriminator column and each associated type modelled aa linked entity in its own right. This approach will result in separate tables in the example above that model each field type
CREATE TABLE PersonId
(
Id INT,
First NVARCHAR,
Last NVARCHAR
)
CREATE TABLE IntOrBool
(
Id INT,
Discriminator
Int1 INT,
Bool1 BIT
)
CREATE TABLE IntOrBool_Int
(
Id INT,
Int1 INT
)
CREATE TABLE IntOrBool_Bool
(
Id INT,
Bool1 BIT
)
CREATE TABLE MixedType
(
Id INT,
Discriminator NVARCHAR
)
CREATE TABLE MixedType_Tup
(
Id INT PRIMARY KEY,
MixedTypeId INT FOREIGN KEY, -- UNIQUE
Int1 INT,
Int2 INT
)
CREATE TABLE MixedType_P
(
Id INT PRIMARY KEY,
MixedTypeId INT FOREIGN KEY,
PersonId INT FOREIGN KEY
)
CREATE TABLE MixedType_L
(
Id INT PRIMARY KEY,
MixedTypeId INT FOREIGN KEY, -- Duplicates allowed as it models a list
Int1 INT
)
CREATE TABLE MixedType_U
(
Id INT PRIMARY KEY,
MixedTypeId INT FOREIGN KEY,
IntOrBool INT FOREIGN KEY
)
This results in a lot of tables being generated but is I believe the most flexible system allowing for further expansion of the domain in future, for instance, if a new property was added to Person
, or a new entry included in MixedType.Tup
It also allows the union type to be extended easily, just adding a new table instead of adding additional columns to existing tables
We will need to be able to support scaffolding code from existing tables that would also include types such multiple types per table
Example from #21:
Where a many-types-per-table relationship from a Table Person
with Discrimator values of Employee
and Manager
would be modelled in C# as
public abstract class Person
{
public int Id {get; set;}
public string First {get; set;}
public string Last {get; set;}
}
public class Employee : Person
{
public string JobTitle {get; set;}
}
public class Manager : Person
{
public string Department {get; set;}
}
we should model it as
type Employee = { Id:int; First:string; Last:string; JobTitle:string }
type Manager = { Id:int; First:string; Last:string; Department:string }
type Person =
| Employee of Employee
| Manager of Manager
A potential issue here will be mapping columns to multiple types
Is your feature request related to a problem? Please describe.
Is support for EF Core 5 planned? Seems like some of the interfaces have changed.
Output of running against EF Core 5.0.3 project:
→ dotnet ef migrations add Initial
Build started...
Build succeeded.
System.MissingMethodException: Method not found: 'System.String Microsoft.EntityFrameworkCore.Design.ICSharpHelper.Lambda(System.Collections.Generic.IReadOnlyList`1<System.String>)'.
at EntityFrameworkCore.FSharp.EFCoreFSharpServices.Microsoft-EntityFrameworkCore-Design-IDesignTimeServices-ConfigureDesignTimeServices(IServiceCollection services)
at DesignTimeServices.DesignTimeServices.Microsoft.EntityFrameworkCore.Design.IDesignTimeServices.ConfigureDesignTimeServices(IServiceCollection serviceCollection) in /home/tomiaijo/also/eol-pricing-tool/src/Server/DesignTimeServices.fs:line 13
at Microsoft.EntityFrameworkCore.Design.Internal.DesignTimeServicesBuilder.ConfigureDesignTimeServices(Type designTimeServicesType, IServiceCollection services)
at Microsoft.EntityFrameworkCore.Design.Internal.DesignTimeServicesBuilder.ConfigureUserServices(IServiceCollection services)
at Microsoft.EntityFrameworkCore.Design.Internal.DesignTimeServicesBuilder.Build(DbContext context)
at Microsoft.EntityFrameworkCore.Design.Internal.MigrationsOperations.AddMigration(String name, String outputDir, String contextType, String namespace)
at Microsoft.EntityFrameworkCore.Design.OperationExecutor.AddMigrationImpl(String name, String outputDir, String contextType, String namespace)
at Microsoft.EntityFrameworkCore.Design.OperationExecutor.AddMigration.<>c__DisplayClass0_0.<.ctor>b__0()
at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.<>c__DisplayClass3_0`1.<Execute>b__0()
at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.Execute(Action action)
Method not found: 'System.String Microsoft.EntityFrameworkCore.Design.ICSharpHelper.Lambda(System.Collections.Generic.IReadOnlyList`1<System.String>)'.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.