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