C # - F # - EF Code First

I have a one-to-many relationship between Dealer who can have many Cars .

I am trying to convert my C # code used for EF to F # ... The problem is that in my F # code it can get a Dealer fine, but it is not suitable for this Dealer ... it just returns null, but does it work in C #?

My code is:

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 that are requested from the dealer and his machine:

 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(); } 

Version F #

 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") 

... ToLinq Function:

 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 that will deliver 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!

+8
c # f # entity-framework ef-code-first
source share
3 answers

Edit

 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> 
+5
source share

To check if a LINQ expression is a problem, you can try to create one manually. The following F # code attempts to simulate the code that you see using Reflector or ILSpy in 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() ) 
+1
source share

I think the problem may be related to your F # ToLinq . It looks like you are just converting an anonymous F # function to MethodCallExpression and have an expression tree as if it should call your function. This is not the same as the "real" tree of expressions with MemberAccessExpression or be that as it may, on the part of C #.

I think that EF wants the expression tree to be more semantic so that it can detect the actual access to the properties, and not just a “fake” call to the method that you make here.

My advice would be to hack the Reflector on two compiled assemblies and see where the difference is in the expression trees. Then make the F # code a string of the same type of expression tree.

Disclaimer: I am not very versed in F #, but I would like to be :). That looks cool!

0
source share

All Articles