I'm having a one-to-many relationship between Dealer
who can have many Cars
.
I'm trying to convert my C# code thats used for EF to F#... The problem is just that in my F# code it can fine get a Dealer, but it wont for that Dealer... it just returns null, but in the C# version it works?
My code:
The C# version
public class Dealer
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<Car> Cars { get; set; }
public Dealer()
{
Cars = new List<Car>();
}
}
public class Car
{
public int ID { get; set; }
public string CarName { get; set; }
public Dealer Dealer { get; set; }
public int DealerId { get; set; }
public Car()
{
Dealer = new Dealer();
}
}
public class MyContext : DbContext
{
public DbSet<Dealer> Dealers { get; set; }
public DbSet<Car> Cars { get; set; }
public MyContext()
{
Database.Connection.ConnectionString = "server=some;database=dbname;user id=uid;password=pwd";
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Dealer>()
.HasMany(x => x.Cars)
.WithRequired(x => x.Dealer)
.HasForeignKey(x => x.DealerId);
modelBuilder.Entity<Car>()
.HasRequired(x => x.Dealer)
.WithMany()
.HasForeignKey(x => x.DealerId);
modelBuilder.Entity<Dealer>().ToTable("Dealers");
modelBuilder.Entity<Car>().ToTable("Cars");
}
}
... Program.cs which query for dealer and his cars:
static void Main(string[] args)
{
var ctx = new MyContext();
foreach (var s in ctx.Dealers.FirstOrDefault(x => x.ID == 1).Cars)
{
Console.WriteLine(s.CarName);
}
Console.Read();
}
The F# version
type Dealer() =
let mutable id = 0
let mutable name = ""
let mutable cars = List<Car>() :> ICollection<Car>
member x.ID with get() = id and set v = id <- v
member x.Name with get() = name and set v = name <- v
abstract member Cars : ICollection<Car> with get, set
override x.Cars with get() = cars and set v = cars <- v
and Car() =
let mutable id = 0
let mutable carName = ""
let mutable dealer = Dealer()
let mutable dealerId = 0
member x.ID with get() = id and set v = id <- v
member x.CarName with get() = carName and set v = carName <- v
member x.Dealer with get() = dealer and set v = dealer <- v
member x.DealerId with get() = dealerId and set v = dealerId <- v
type MyContext() =
inherit DbContext("server=some;database=dbname;user id=uid;password=pwd")
[<DefaultValue>] val mutable dealers : DbSet<Dealer>
member x.Dealers with get() = x.dealers and set v = x.dealers <- v
[<DefaultValue>] val mutable cars : DbSet<Car>
member x.Cars with get() = x.cars and set v = x.cars <- v
override x.OnModelCreating(modelBuilder:DbModelBuilder) =
modelBuilder.Entity<Dealer>()
.HasMany(ToLinq(<@ fun ka -> ka.Cars @>))
.WithRequired(ToLinq(<@ fun sk -> sk.Dealer @>))
.HasForeignKey(ToLinq(<@ fun fg -> fg.DealerId @>)) |> ignore
modelBuilder.Entity<Car>()
.HasRequired(ToLinq(<@ fun ak -> ak.Dealer @>))
.WithMany()
.HasForeignKey(ToLinq(<@ fun ka -> ka.DealerId @>)) |> ignore
modelBuilder.Entity<Dealer>().ToTable("Dealers")
modelBuilder.Entity<Car>().ToTable("Cars")
... The function ToLinq:
let ToLinq (exp : Expr<'a -> 'b>) =
let linq = exp.ToLinqExpression()
let call = linq :?> MethodCallExpression
let lambda = call.Arguments.[0] :?> LambdaExpression
Expression.Lambda<Func<'a, 'b>>(lambda.Body, lambda.Parameters)
... And Program.fs which will get the Dealer and his Cars:
let ctx = new MyContext()
let joe = ctx.Dealers.Find(1)
joe.Cars |> Seq.iter(fun x -> printfn "%s" x.CarName)
printfn "DONE"
Any help is appreciated!
Change
let mutable cars = List<Car>() :> ICollection<Car>
to
let mutable cars = Unchecked.defaultof<ICollection<Car>>
and
let mutable dealer = Dealer()
to
let mutable dealer = Unchecked.defaultof<Dealer>
To check whether the LINQ expression is the problem, you could try to create such an expression manually. The following F# code tries to mimic the code that you can see with Reflector or ILSpy in the C# code:
open System
open System.Linq.Expressions
// Manually creates a LINQ expression that represents: << x => x.propName >>
// 'T: type of x
// 'C: return type of the property
let createPropertyGetDelegate<'T, 'C> propName =
let typ = typeof<'T> // '
let parameterExpr = Expression.Parameter(typ, "x")
let getMethod = typ.GetProperty(propName).GetGetMethod()
let propExpr = Expression.Property(parameterExpr, getMethod)
Expression.Lambda<Func<'T, 'C>>(propExpr, [|parameterExpr|])
type Example() =
member x.Test = 42
let del = createPropertyGetDelegate<Example, int> "Test"
printfn "%A" <| del.Compile().Invoke( Example() )
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With