【问题标题】:WCF crashes after sending EF object with it's referencesWCF 在发送 EF 对象及其引用后崩溃
【发布时间】:2014-03-26 22:49:54
【问题描述】:

我有几个表的数据库,包括:

CREATE TABLE [dbo].[Exam]
(
    [Id] INT NOT NULL PRIMARY KEY identity, 
    [Author id] INT NULL, 
    ...
    CONSTRAINT [FK_Exam_Author] FOREIGN KEY ([Author id]) REFERENCES [User]([Id]), 
)

CREATE TABLE [dbo].[User]
(
    [Id] INT NOT NULL PRIMARY KEY identity, 
    ...
)

我有ADO.NET 生成的数据库模型。

public partial class Exam
{
    public Exam()
    {
        ...
    }

    public int Id { get; set; }
    public Nullable<int> Author_id { get; set; }
    ...

    public virtual User User { get; set; }
    ...
}

public partial class Exam
{
    public User()
    {
        this.Exam = new HashSet<Exam>();
        ...
    }

    public int Id { get; set; }
    ...

    public virtual ICollection<Exam> Exam { get; set; }
    ...
}

我的Wcf service 中还有GetExam 函数,它从数据库返回一项检查。

public Exam GetExam()
{
    var context = new GDBDatabaseEntities();
    context.Configuration.ProxyCreationEnabled = false;
    var exams = context.Exam;
    var exam = exams.FirstOrDefault();
    return exam;
}

禁用ProxyCreationEnabled 是通过Wcf 发送Exam 所必需的。

不幸的是,上面的代码返回给我一个空的User 字段的考试(EF 自动生成此字段作为对FK_Exam_Author 的响应)...

我尝试使用Include 函数加载User 属性:

var exams = context.Exam.Include("User");

我收到了Wcf 的错误:

调用服务失败。可能原因:服务离线或无法访问;客户端配置与代理不匹配;现有代理无效。有关更多详细信息,请参阅堆栈跟踪。您可以尝试通过启动新代理、恢复到默认配置或刷新服务来恢复。

就在从Wcf 返回之前,调试器显示Exam 对象看起来正常(已加载User 依赖项)。

我认为这可能是事实造成的,加载了User已经加载了他的Exams列表,它加载了User...(循环依赖)。

我对 User 有完全相同的问题 - Wcf 适用于具有空 Exams[] 属性的 User,但是当我加载 User 的考试列表时,Wcf 已崩溃...

如何正确加载关系并通过Wcf发送?

另一个问题是Wcf 如何知道如何序列化我的对象(当我使用LINQ to SQL 时,它会生成具有DataContractDataMember 属性的类,但Entity Framework 只是不这样做。

更新

这是循环依赖的问题,因为

public Exam GetExam()
{
    var context = new GDBDatabaseEntities();
    context.Configuration.ProxyCreationEnabled = false;
    var exams = context.Exam.Include("User");
    var exam = exams.FirstOrDefault();
    exam.User.Exam = null;
    return exam;
}

有效。

谁能解释一下为什么 Include 函数对于加载我需要的 1 级依赖项是必需的,但其他依赖项正在加载无穷大?这太荒谬了...

更新

根据答案和Yahoo's Best Practices for Speeding Up Your Web Site first rule,我决定在我的Windows 8 application 中为每个Page 创建新对象,这将减少发送数据量、请求数量,并让我避免发送冗余数据。

【问题讨论】:

    标签: c# wcf entity-framework


    【解决方案1】:

    在尝试了几次之后,我很快放弃了使用 EF 对象作为我的数据合约。正如您所发现的,循环依赖关系很快就破坏了DataContractSerializer。此外,您的数据库对象可能包含不需要通过网络发送给客户端的信息。

    相反,创建与 EF 生成的对象类似的 DataContract 对象以与 WCF 一起使用(没有循环依赖和其他问题),并在两者之间进行转换。例如:

    [DataContract]
    public class User
    {
        [DataMember]
        public IEnumerable<Exam> Exams {get; set;}
    }
    
    [DataContract]
    public class Exam
    {
       [DataMember]
       public int Score {get; set;}
    }
    

    那么你只需要一个像这样的加载器函数:

    IEnumerable<Contracts.User> GetAllUsers()
    {
       foreach (DB.User in context.Users)
       {
           Contracts.User wcfUser = new Contracts.User();
           wcfUser.Exams = new List<Contracts.Exam>();
           foreach (DB.Exam exam in wcfUser.Exams)
              wcfUser.Exams.Add(new Contracts.Exam() { Score = exam.Score };
    
           yield return wcfUser;
       }
    }
    

    显然,这大大扩展了您的代码库,但避免了您的客户需要了解您的数据库对象,有助于降低网络流量,并实现更好的关注点分离。

    希望能回答您的问题!如果我能澄清任何事情,请告诉我。

    【讨论】:

    • 为每个表创建额外的类是不可能的(这是一个相当大的项目)。很难相信在 EF 中没有对 WCF 的原生支持,因为我已经使用 LINQ to SQL 解决了相同的问题,这被认为更粗糙。
    • 我也看到了这种中断(正如您所指出的,它们非常相似)。正如我的回答中所指出的,这肯定是很多额外的代码,但通过 WCF 连接发送数据库对象(及其所有上下文跟踪、循环属性等)也不是一个好主意。 EF 类从来都不是 WCF 数据协定。我没用过,但你可能想看看 WCF 数据服务,因为它有一些自动连接到数据库对象的功能。
    • 你说得对,通过 WCF 连接发送数据库对象不是一个好主意。查看我的更新。
    • 很高兴您能够解决问题!您是否从更新中寻找任何其他信息?
    猜你喜欢
    • 2013-12-30
    • 2011-07-20
    • 2015-03-21
    • 1970-01-01
    • 1970-01-01
    • 2017-04-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多