[英]UseLazyLoadingProxies codefirst null navigation property only after database creation
user.UserWordRequests.Add(
new UserWordRequest
{
Date = DateTime.Now,
Word = word
}
);
user.UserWordRequests
is null.执行第一次控制台程序,上面user.UserWordRequests
中的代码是null。 Expected value is empty list .预期值为空列表。user.UserWordRequests
is empty list (or filled with data, depends of data in db).再次执行控制台程序,现在user.UserWordRequests
是空列表(或填充数据,取决于数据库中的数据)。More data, here is user object in step 2:更多数据,这里是步骤 2 中的用户 object:
And here in step 4:在第 4 步中:
public class User
{
public User()
{
}
[Key]
public int Id { get; set; }
[Required]
public string Name { get; set; }
public virtual ICollection<UserWordRequest> UserWordRequests { get; set; }
}
public class Word
{
public Word()
{
}
[Key]
public int Id { get; set; }
[Required]
public string Value { get; set; }
public virtual ICollection<UserWordRequest> UserWordRequests { get; set; }
}
public class UserWordRequest
{
public UserWordRequest()
{
}
[Key]
public int Id { get; set; }
[Required]
public int UserId { get; set; }
public virtual User User { get; set; }
[Required]
public int WordId { get; set; }
public virtual Word Word { get; set; }
[Required]
public DateTime Date { get; set; }
}
class Program
{
private static async Task Main(string[] args)
{
var host =
Host.CreateDefaultBuilder(args)
.ConfigureLogging(loggingBuilder =>
{
loggingBuilder.ClearProviders(); // Disable console messages
})
.ConfigureServices((hostContext, services) =>
{
// Auto mapping config !!!
var config = new ConfigurationBuilder()
.SetBasePath(Path.Combine(AppContext.BaseDirectory))
.AddJsonFile("appsettings.json")
.Build();
var settings = config/*.GetSection("GeneralSection")*/.Get<Config>();
// AddHostedService
services
.AddHostedService<ConsoleHostedService>()
.AddDbContext<DataBaseContext>
(
options =>
options
.UseLazyLoadingProxies(true)
.UseSqlite(
settings.General.DataBaseConnection
)
,
ServiceLifetime.Singleton,
ServiceLifetime.Singleton
)
//.AddEntityFrameworkProxies()
.AddSingleton((Config) => { return settings; })
.AddSingleton<Logger>()
.AddSingleton<Util.File>()
.AddSingleton<Core>()
.AddSingleton<DataBaseContext>()
;
}).Build();
host.Services.GetService<DataBaseContext>().Database.Migrate();
// Then run application
host.Run();
}
}
public class ConsoleHostedService : IHostedService
{
private readonly IHostApplicationLifetime _appLifetime;
private readonly Core _Core;
private readonly Logger _Logger;
private readonly DataBaseContext _db;
public ConsoleHostedService(
IHostApplicationLifetime appLifetime,
Core Core,
Logger Logger,
DataBaseContext DataBaseContext)
{
_appLifetime = appLifetime;
_Core = Core;
_Logger = Logger;
_db = DataBaseContext;
}
public Task StartAsync(CancellationToken cancellationToken)
{
_appLifetime.ApplicationStarted.Register(() =>
{
Task.Run(async () =>
{
try
{
// Start program
//_db.Database.Migrate();
_Core.run();
}
catch (Exception e)
{
_Logger.Log(Logger.LogType.Error, "", e);
}
finally
{
// Stop the application once the work is done
_appLifetime.StopApplication();
}
});
});
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
}
public class DataBaseContext : DbContext
{
public DataBaseContext(DbContextOptions<DataBaseContext> options) : base(options)
{
}
public virtual DbSet<Word> Words { get; set; }
public virtual DbSet<User> Users { get; set; }
public virtual DbSet<Log> Logs { get; set; }
public virtual DbSet<UserWordRequest> UserWordRequests { get; set; }
}
/// <summary>
/// ❗❗❗ We need this class only for doing add-migration, but this is not used by anyone
/// </summary>
public class DataBaseContextFactory : IDesignTimeDbContextFactory<DataBaseContext>
{
public DataBaseContext CreateDbContext(string[] args)
{
var optionsBuilder = new DbContextOptionsBuilder<DataBaseContext>();
optionsBuilder.UseSqlite("Data Source=this_name_is_not_used.db");
return new DataBaseContext(optionsBuilder.Options);
}
}
public Core(DataBaseContext DataBaseContext, Logger Logger, File File, Config Config)
{
_db = DataBaseContext;
_Logger = Logger;
_File = File;
_Config = Config;
_Random = new Random();
_Logger.Log(Logger.LogType.Info, "Core");
}
public void run()
{
try
{
... some code
var word = _db.Words.FirsOrDefault();
var user = _db.Users.FirsOrDefault();
// ‼‼‼ PROBLEMATIC CODE
user.UserWordRequests.Add(
new UserWordRequest
{
Date = DateTime.Now,
Word = word
}
);
_db.SaveChanges();
... some code
}
catch(Exception e)
{
_Logger.Log(Logger.LogType.Error, "", e);
}
}
net6.0 and: net6.0 和:
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="6.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Configuration" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.0" />
Use create or createProxy instead new
for avoid errors.使用create或createProxy而不是new
以避免错误。
First time console app is executed, when database is created, code created users with new
instead Create
or CreateProxy
:第一次执行控制台应用程序时,创建数据库时,代码使用new
而不是Create
或CreateProxy
创建用户:
var user = new User();
user.prop = "value";
...
db.Users.Add(user)
Later, inside first time console app execution, application executes this code and UserWordRequests
is null instead empty list.后来,在第一次控制台应用程序执行中,应用程序执行此代码并且UserWordRequests
是 null 而不是空列表。
var user = db.Users.Where(...).FirstOrDefault();
user.UserWordRequests.Add(
new UserWordRequest
{
Date = DateTime.Now,
Word = ...
}
);
The solution comes using Create
or CreateProxy
for user creations.该解决方案使用Create
或CreateProxy
来创建用户。
var proxy = _db.Users.CreateProxy();
proxy.Name = user.Name;
_db.Users.Add(proxy);
Then, UserWordRequests
is empty list, not null.然后, UserWordRequests
是空列表,而不是 null。
But... why second time this does not happen?但是......为什么第二次没有发生这种情况? Because users are created in database and the code did not execute the inserts, it obtained them with a database query.因为用户是在数据库中创建的,并且代码没有执行插入,所以它通过数据库查询来获取它们。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.