C# EF延迟加载

C# EF延迟加载,c#,entity-framework,lazy-loading,C#,Entity Framework,Lazy Loading,我对惰性加载有点困惑。 我的印象是,当导航属性被访问时,它会加载这些属性,但在我的代码中,它似乎试图将它们全部拉进去。 这可能是由于我的服务/存储库模式,但目前我得到了循环引用:( 当我这样呼叫我的服务时: using (var service = new UserService(new CompanyService())) { var u = await service.GetAll(); return new JsonResult { D

我对惰性加载有点困惑。 我的印象是,当导航属性被访问时,它会加载这些属性,但在我的代码中,它似乎试图将它们全部拉进去。 这可能是由于我的服务/存储库模式,但目前我得到了循环引用:(

当我这样呼叫我的服务时:

using (var service = new UserService(new CompanyService()))
{
    var u = await service.GetAll();                  

    return new JsonResult { Data = new { success = true, users = u } }; // Return our users
}
public class InterfaceContractResolver : DefaultContractResolver
{
    private readonly Type interfaceType;

    public InterfaceContractResolver(Type interfaceType)
    {
        this.interfaceType = interfaceType;
    }

    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    {
        var properties = base.CreateProperties(this.interfaceType, memberSerialization);

        return properties;
    }
}
public async Task<JsonNetResult> Get()
{
    try
    {
        using (var service = new UserService(new CompanyService()))
        {
            var u = await service.GetAll();

            var serializedObject = JsonConvert.SerializeObject(u,
                new JsonSerializerSettings()
                {
                    ContractResolver = new InterfaceContractResolver(typeof(IBaseUser))
                });

            return new JsonNetResult { Data = new { success = true, users = serializedObject } }; // Return our users
        }
    }
    catch (Exception ex)
    {
        return new JsonNetResult { Data = new { success = false, error = ex.Message } };
    }
}
    // 
    // AJAX: /Users/Get

    public JsonResult Get()
    {
        try
        {
            using (var service = new UserService(new CompanyService()))
            {
                var u = service.GetAll("MemberOf");                  

                return new JsonResult { Data = new { success = true, users = u } }; // Return our users
            }
        }
        catch (Exception ex)
        {
            return new JsonResult { Data = new { success = false, error = ex.Message } };
        }
    }
它带来了一个用户列表

public partial class User : IdentityUser
{
    public User()
    {
        // ...
        this.MemberOf = new List<Group>();
        // ...
    }

    // ...

    // ...
    public virtual ICollection<Group> MemberOf { get; set; }
    // ...
}
这就带来了一家公司

public partial class Company
{
    public Company()
    {
        this.Assets = new List<Asset>();
        // ..
    }

    public string Id { get; set; }
    public string Name { get; set; }
    // ..
            
    // ...
    public virtual ICollection<Asset> Assets { get; set; }
    // ...
}
但我仍然得到循环引用

有人能给我解释一下为什么它试图加载所有导航属性,以及我是否可以(轻松地)做些什么来阻止它

更新2

Keith建议使用接口和一个ContractResolver来帮助序列化,所以我就是这么做的

首先,我创建了两个新接口:

public interface IBaseUser
{
    string Id { get; set; }
    string UserName { get; set; }
    string Email { get; set; }
    bool IsApproved { get; set; }
    bool IsLockedOut { get; set; }

    ICollection<Group> MemberOf { get; set; }
}
模型实现了这些类

public partial class User : IdentityUser, IBaseUser

这是第一步,第二步是创建ContractResolver类,如下所示:

using (var service = new UserService(new CompanyService()))
{
    var u = await service.GetAll();                  

    return new JsonResult { Data = new { success = true, users = u } }; // Return our users
}
public class InterfaceContractResolver : DefaultContractResolver
{
    private readonly Type interfaceType;

    public InterfaceContractResolver(Type interfaceType)
    {
        this.interfaceType = interfaceType;
    }

    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    {
        var properties = base.CreateProperties(this.interfaceType, memberSerialization);

        return properties;
    }
}
public async Task<JsonNetResult> Get()
{
    try
    {
        using (var service = new UserService(new CompanyService()))
        {
            var u = await service.GetAll();

            var serializedObject = JsonConvert.SerializeObject(u,
                new JsonSerializerSettings()
                {
                    ContractResolver = new InterfaceContractResolver(typeof(IBaseUser))
                });

            return new JsonNetResult { Data = new { success = true, users = serializedObject } }; // Return our users
        }
    }
    catch (Exception ex)
    {
        return new JsonNetResult { Data = new { success = false, error = ex.Message } };
    }
}
    // 
    // AJAX: /Users/Get

    public JsonResult Get()
    {
        try
        {
            using (var service = new UserService(new CompanyService()))
            {
                var u = service.GetAll("MemberOf");                  

                return new JsonResult { Data = new { success = true, users = u } }; // Return our users
            }
        }
        catch (Exception ex)
        {
            return new JsonResult { Data = new { success = false, error = ex.Message } };
        }
    }
然后我创建了这个存储库:

public abstract class Repository<TEntity> : IDisposable, IRepository<TEntity> where TEntity : class
{
    public DbContext Context { get; private set; }
    public IQueryable<TEntity> EntitySet { get { return this.DbEntitySet; } }
    public DbSet<TEntity> DbEntitySet { get; private set; }

    public Repository(DbContext context)
    {
        if (context == null)
            throw new ArgumentNullException("context");

        this.Context = context;
        this.DbEntitySet = context.Set<TEntity>();
    }

    public Task<TEntity> GetAsync(object id)
    {
        return this.DbEntitySet.FindAsync(new object[]
        {
            id
        });
    }

    public IEnumerable<TEntity> GetAll()
    {
        return this.DbEntitySet;
    }

    public IEnumerable<TEntity> GetAll(string include)
    {
        return this.DbEntitySet.Include(include);
    }

    public IEnumerable<TEntity> GetAll(string[] includes)
    {
        foreach (var include in includes)
            this.DbEntitySet.Include(include);

        //var t = this.DbEntitySet.Include("Settings").ToList();

        // return this.GetAll();
        return this.DbEntitySet;
    }

    public void Add(TEntity model)
    {
        this.DbEntitySet.Add(model);
    }

    public void Remove(TEntity model)
    {
        this.Context.Entry<TEntity>(model).State = EntityState.Deleted;
    }

    public void Dispose()
    {
        this.Context.Dispose();
    }
}
你猜怎么着?!?!? 如果我在返回前放置一个断点,我会看到u的属性都已填充,导航属性都未设置,除了MemberOf,这正是我想要的(现在),但当我跨过返回时,我会得到与以前相同的错误

Newtonsoft.Json.Json.JsonSerializationException类型的异常在Newtonsoft.Json.dll中发生,但未在用户代码中处理

其他信息:检测到类型为“System.Data.Entity.DynamicProxies.Asset”的自引用循环。路径为“users[0]。公司。资产[0]。类别[0]。资产

这怎么可能

更新4

这似乎是因为属性仍然标记为Virtual。当我删除Virtual时,我停止获取资产的错误,而现在获取CreatedBy属性的错误

这是我的用户课程:

public partial class User : IdentityUser
{
    public string CompanyId { get; set; }
    public string CreatedById { get; set; }
    public string ModifiedById { get; set; }
    public System.DateTime DateCreated { get; set; }
    public Nullable<System.DateTime> DateModified { get; set; }
    public System.DateTime LastLoginDate { get; set; }
    public string Title { get; set; }
    public string Forename { get; set; }
    public string Surname { get; set; }
    public string Email { get; set; }
    public string JobTitle { get; set; }
    public string Telephone { get; set; }
    public string Mobile { get; set; }
    public string Photo { get; set; }
    public string LinkedIn { get; set; }
    public string Twitter { get; set; }
    public string Facebook { get; set; }
    public string Google { get; set; }
    public string Bio { get; set; }
    public string CompanyName { get; set; }
    public string CredentialId { get; set; }
    public bool IsLockedOut { get; set; }
    public bool IsApproved { get; set; }
    public bool CanEditOwn { get; set; }
    public bool CanEdit { get; set; }
    public bool CanDownload { get; set; }
    public bool RequiresApproval { get; set; }
    public bool CanApprove { get; set; }
    public bool CanSync { get; set; }
    public bool AgreedTerms { get; set; }
    public bool Deleted { get; set; }

    public Company Company { get; set; }
    public User CreatedBy { get; set; }
    public User ModifiedBy { get; set; }
    public ICollection<Asset> Assets { get; set; }
    public ICollection<Category> Categories { get; set; }
    public ICollection<Collection> Collections { get; set; }
    public ICollection<Comment> Comments { get; set; }
    public ICollection<LocalIntegration> LocalIntegrations { get; set; }
    public ICollection<Page> Pages { get; set; }
    public ICollection<Rating> Ratings { get; set; }
    public ICollection<Theme> Themes { get; set; }
    public ICollection<Group> MemberOf { get; set; }
    public ICollection<Category> ForbiddenCategories { get; set; }
    public ICollection<Page> ForbiddenPages { get; set; }
}
公共部分类用户:IdentityUser
{
公共字符串CompanyId{get;set;}
公共字符串CreatedById{get;set;}
公共字符串ModifiedById{get;set;}
public System.DateTime DateCreated{get;set;}
公共可为空的DateModified{get;set;}
public System.DateTime lastloginandate{get;set;}
公共字符串标题{get;set;}
公共字符串名{get;set;}
公共字符串姓氏{get;set;}
公共字符串电子邮件{get;set;}
公共字符串JobTitle{get;set;}
公用字符串电话{get;set;}
公共字符串Mobile{get;set;}
公共字符串Photo{get;set;}
公共字符串LinkedIn{get;set;}
公共字符串Twitter{get;set;}
公共字符串{get;set;}
公共字符串Google{get;set;}
公共字符串Bio{get;set;}
公共字符串CompanyName{get;set;}
公共字符串CredentialId{get;set;}
公共bool IsLockedOut{get;set;}
公共布尔值已批准{get;set;}
公共bool CanEditOwn{get;set;}
公共bool CanEdit{get;set;}
公共bool CanDownload{get;set;}
公共布尔要求重新批准{get;set;}
公共bool可以批准{get;set;}
公共bool CanSync{get;set;}
公共bool agreedtterms{get;set;}
公共bool已删除{get;set;}
上市公司{get;set;}
公共用户CreatedBy{get;set;}
公共用户通过{get;set;}修改
公共ICollection资产{get;set;}
公共ICollection类别{get;set;}
公共ICollection集合{get;set;}
公共ICollection注释{get;set;}
公共ICollection LocalIntegrations{get;set;}
公共ICollection页{get;set;}
公共ICollection分级{get;set;}
公共ICollection主题{get;set;}
{get;set;}的公共ICollection成员
公共ICollection禁止类别{get;set;}
公共ICollection禁止页面{get;set;}
}
以下是错误:

Newtonsoft.Json.Json.JsonSerializationException类型的异常在Newtonsoft.Json.dll中发生,但未在用户代码中处理

其他信息:检测到类型为“System.Data.Entity.DynamicProxies.User”的属性“CreatedBy”的自引用循环。路径为“users[0]”

我不明白如何实现创建的属性,因为它不是一个虚拟属性,我不要求使用急切或显式加载

有人知道为什么吗?

这可能有用:

创建一个或多个定义要序列化的属性的接口。实体的每个“快照”一个接口

让您的EF实体实现所有接口

然后创建一个协定解析程序,该解析程序仅对传递给解析程序的接口类型上定义的属性有效。请参阅协定解析程序代码并将其与协定解析程序代码一起使用

一旦编码了契约解析程序,就可以传递EF实体和您喜欢的任何接口

希望序列化程序将忽略契约解析程序未“看到”的属性


如果可以,请用您的工作代码发布您自己的答案,并将其标记为答案。

JSON序列化是通过使用反射在内部实现的。因此,它尝试访问您必须构建JSON结果的每个公共属性

您可以在不希望序列化的导航属性上添加
[JsonIgnore]
属性。这也有助于防止序列化产生循环引用(例如,对于多对多关系)

也就是说,我建议使用ViewModels而不是实体
public class UserRepository : Repository<User>
{
    public UserRepository(DbContext context)
        : base(context)
    {
    }
}
    public IList<User> GetAll(string include)
    {
        return this.repository.GetAll(include).Where(model => model.CompanyId.Equals(this.companyId, StringComparison.OrdinalIgnoreCase)).ToList();
    }
    // 
    // AJAX: /Users/Get

    public JsonResult Get()
    {
        try
        {
            using (var service = new UserService(new CompanyService()))
            {
                var u = service.GetAll("MemberOf");                  

                return new JsonResult { Data = new { success = true, users = u } }; // Return our users
            }
        }
        catch (Exception ex)
        {
            return new JsonResult { Data = new { success = false, error = ex.Message } };
        }
    }
public partial class User : IdentityUser
{
    public string CompanyId { get; set; }
    public string CreatedById { get; set; }
    public string ModifiedById { get; set; }
    public System.DateTime DateCreated { get; set; }
    public Nullable<System.DateTime> DateModified { get; set; }
    public System.DateTime LastLoginDate { get; set; }
    public string Title { get; set; }
    public string Forename { get; set; }
    public string Surname { get; set; }
    public string Email { get; set; }
    public string JobTitle { get; set; }
    public string Telephone { get; set; }
    public string Mobile { get; set; }
    public string Photo { get; set; }
    public string LinkedIn { get; set; }
    public string Twitter { get; set; }
    public string Facebook { get; set; }
    public string Google { get; set; }
    public string Bio { get; set; }
    public string CompanyName { get; set; }
    public string CredentialId { get; set; }
    public bool IsLockedOut { get; set; }
    public bool IsApproved { get; set; }
    public bool CanEditOwn { get; set; }
    public bool CanEdit { get; set; }
    public bool CanDownload { get; set; }
    public bool RequiresApproval { get; set; }
    public bool CanApprove { get; set; }
    public bool CanSync { get; set; }
    public bool AgreedTerms { get; set; }
    public bool Deleted { get; set; }

    public Company Company { get; set; }
    public User CreatedBy { get; set; }
    public User ModifiedBy { get; set; }
    public ICollection<Asset> Assets { get; set; }
    public ICollection<Category> Categories { get; set; }
    public ICollection<Collection> Collections { get; set; }
    public ICollection<Comment> Comments { get; set; }
    public ICollection<LocalIntegration> LocalIntegrations { get; set; }
    public ICollection<Page> Pages { get; set; }
    public ICollection<Rating> Ratings { get; set; }
    public ICollection<Theme> Themes { get; set; }
    public ICollection<Group> MemberOf { get; set; }
    public ICollection<Category> ForbiddenCategories { get; set; }
    public ICollection<Page> ForbiddenPages { get; set; }
}