C # to F # - Код EF сначала - PullRequest
       7

C # to F # - Код EF сначала

8 голосов
/ 24 марта 2011

У меня отношение один ко многим между Dealer, у которого может быть много Cars.

Я пытаюсь преобразовать свой код C #, который используется для EF, в F # ...Проблема только в том, что в моем коде F # он может нормально получить Дилера, но это не подходит для этого Дилера ... он просто возвращает ноль, но в версии C # это работает?

Мой код:

Версия C #

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, которая запрашивает дилера и его автомобили:

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

Версия 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:

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)

... И Program.fs, которые получат Дилер и его Автомобили:

let ctx = new MyContext()
let joe = ctx.Dealers.Find(1)
joe.Cars |> Seq.iter(fun x -> printfn "%s" x.CarName)
printfn "DONE"

Любая помощь приветствуется!

Ответы [ 3 ]

5 голосов
/ 25 марта 2011

Изменить

let mutable cars = List<Car>() :> ICollection<Car>

на

let mutable cars = Unchecked.defaultof<ICollection<Car>>

и

let mutable dealer = Dealer()   

на

let mutable dealer = Unchecked.defaultof<Dealer>
1 голос
/ 25 марта 2011

Чтобы проверить, является ли выражение LINQ проблемой, вы можете попробовать создать такое выражение вручную. Следующий код F # пытается имитировать код, который можно увидеть с помощью Reflector или ILSpy в коде C #:

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() )
0 голосов
/ 24 марта 2011

Я думаю, что проблема может быть в вашей функции F # ToLinq. Похоже, вы просто конвертируете анонимную функцию F # в MethodCallExpression и получаете дерево выражений, которое должно вызывать вашу функцию. Это не то же самое, что «реальное» дерево выражений с MemberAccessExpression или чем-то, что оказывается со стороны C #.

Я думаю, что EF хочет, чтобы дерево выражений было более семантическим, чтобы он мог обнаружить фактический доступ к свойству, а не простой «поддельный» вызов метода, который вы здесь делаете.

Мой совет - взломать Reflector на двух скомпилированных сборках и посмотреть, где разница в деревьях выражений. Затем заставьте код F # построить дерево выражений того же типа.

Отказ от ответственности: Я не очень хорошо разбираюсь в F #, но я бы хотел быть :). Это выглядит круто!

...