8

Dealer我在谁可以拥有很多之间建立了一对多的关系Cars

我正在尝试将用于 EF 的 C# 代码转换为 F#...问题只是在我的 F# 代码中它可以很好地获得经销商,但它不会为经销商...它只是返回 null,但在它工作的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"

任何帮助表示赞赏!

4

3 回答 3

5

改变

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

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

let mutable dealer = Dealer()   

let mutable dealer = Unchecked.defaultof<Dealer>
于 2011-03-25T04:47:21.047 回答
1

要检查 LINQ 表达式是否存在问题,您可以尝试手动创建这样的表达式。以下 F# 代码尝试模仿您可以在 C# 代码中使用 Reflector 或 ILSpy 看到的代码:

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() )
于 2011-03-25T00:14:55.907 回答
0

我认为问题可能出在您的 F#ToLinq函数上。看起来您只是将 F# 匿名函数转换为 aMethodCallExpression并让表达式树表现得像它应该调用您的函数一样。这与带有 aMemberAccessExpression或 C# 端的“真实”表达式树不同。

我认为 EF 希望表达式树以更加语义化的方式,以便它可以发现实际的属性访问,而不是您在这里所做的简单的“假”方法调用。

我的建议是在两个编译的程序集上打开反射器,看看表达式树的区别在哪里。然后让 F# 代码构建相同类型的表达式树。

免责声明:我不太精通 F#,但我想成为:)。这看起来很酷!

于 2011-03-24T20:07:23.933 回答