【问题标题】:C# to F# - EF Code FirstC# 到 F# - EF 代码优先
【发布时间】:2011-03-24 18:31:25
【问题描述】:

我在Dealer 之间建立了一对多的关系,而Cars 可以拥有多个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"

感谢任何帮助!

【问题讨论】:

  • 我对 EF 一无所知,但我看到在 F# 版本中 Dealer.Cars 是一个虚拟属性,而在 C# 版本中它只是一个普通属性。如果你在 F# 中改变它会有什么不同吗?
  • @wmeyer - 我这边的错。 Dealer.Cars 在 C# 版本中也应该是虚拟的 - 唯一会改变的是集合现在将在 c# 中延迟加载,它也很好(刚刚测试过)。
  • 我确信这不是一个重要的区别,但是你的 C# 没有调用 DBContext(str) 而你的 F# 版本调用了?
  • @Robert Jeppesen - 正如你所说,这不是一个重要的区别。我在 C# 版本中不调用 DbContext(str) 的原因仅仅是它不能。我还尝试以与 C# 版本相同的方式定义 connectionString,结果相同。 - 请注意,它可以毫无问题地连接到数据库并获得Dealer,当我尝试为该经销商获取汽车时首先出现问题。
  • public MyContext() : base("connstring") 应该可以工作吗?

标签: c# entity-framework f# ef-code-first entity-framework-4.1


【解决方案1】:

改变

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

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

let mutable dealer = Dealer()   

let mutable dealer = Unchecked.defaultof<Dealer>

【讨论】:

  • 这确实有效,我认为这很奇怪。 C# 版本也初始化了这些属性,但那行得通吗?任何关于为什么的见解?
  • 完美运行!你知道我为什么需要Unchecked.defaultof&lt;&gt; 来激活ICollection 和Dealer 吗?
  • F# 是 field initialized C# 是 property initialized 它是默认的
【解决方案2】:

要检查 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() )

【讨论】:

  • ToLinq 方法工作正常。问题似乎是 Dealer.Cars 属性没有被初始化。 @newcommer 似乎找到了解决方案 :) - 不过还是谢谢!
【解决方案3】:

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

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

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

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

【讨论】:

  • 我会试一试并返回结果! :=)
  • 在一个完全不相关的事情上;您从哪里获得 F# 语法?您发现了哪些不错的教程网站?
  • Don Syme 的书 Expert F# 2.0 是一个很好的参考。它有点贵,但值得入手。 Jon Skeet 和 Thomas Petricek 编写了一本体面的书,名为 Real-World Functional Programming With Examples in F# and C#,非常适合学习如何在现实世界中使用函数式编程,但不是很好的语言参考。我会推荐前者而不是后者,但两者都很好。
  • 我无法打开 Reflector 并比较 OnModelCreating 方法,因为编译器将序列化 F# 表达式。 - 关于我从哪里获得 F# 语法的问题,那么大部分只是 fsharp.netmsdn.microsoft.com/en-us/library/dd233154.aspx 将我踢向了方向。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-21
  • 1970-01-01
  • 1970-01-01
  • 2016-05-26
  • 2012-05-17
  • 2018-02-13
  • 1970-01-01
相关资源
最近更新 更多