Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# to F# - EF Code First

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!

like image 927
ebb Avatar asked Mar 24 '11 18:03

ebb


2 Answers

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>
like image 141
newcommer Avatar answered Nov 18 '22 08:11

newcommer


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() )
like image 28
wmeyer Avatar answered Nov 18 '22 08:11

wmeyer