C# 具有多对一功能的SQLite.Net扩展';I don’我没有按预期工作

C# 具有多对一功能的SQLite.Net扩展';I don’我没有按预期工作,c#,many-to-one,sqlite-net,sqlite-net-extensions,sqlite.net,C#,Many To One,Sqlite Net,Sqlite Net Extensions,Sqlite.net,我的表将包含许多重复的字符串,如域。为了最小化数据库大小,我希望在其他表中只保存唯一的域,并在主表中使用域id 我总是手工操作,但不久前我发现SQLite可以自动操作 现在我尝试使用多对一关系和“外键”,但没有成功。 也许我做错了什么 示例代码 表格类别: public class Domains { public Domains() { } public Domains(string domain) { this.Domain = domain; } [Primary

我的表将包含许多重复的字符串,如域。为了最小化数据库大小,我希望在其他表中只保存唯一的域,并在主表中使用域id

我总是手工操作,但不久前我发现SQLite可以自动操作

现在我尝试使用多对一关系和“外键”,但没有成功。 也许我做错了什么

示例代码

表格类别:

public class Domains
{
    public Domains() { }
    public Domains(string domain) { this.Domain = domain; }

    [PrimaryKey, AutoIncrement]
    public int Id { get; set; }
    [Unique, MaxLength(64)]
    public string Domain { get; set; }
}

public class Statistics
{
    [PrimaryKey, AutoIncrement]
    public int Id { get; set; }
    public int Timestamp { get; set; }

    [ForeignKey(typeof(Domains))]
    public int DomainId { get; set; }

    public int Status { get; set; }

    [ManyToOne(CascadeOperations = CascadeOperation.All)]
    public Domains Domain { get; set; }
}
public class Domains
{
    public Domains() { }
    public Domains(string domain) { this.Domain = domain; }

    [PrimaryKey, AutoIncrement]
    public int Id { get; set; }
    [Unique, MaxLength(64)]
    public string Domain { get; set; }
}

public class Statistics
{
    [PrimaryKey, AutoIncrement]
    public int Id { get; set; }
    public int Timestamp { get; set; }
    public int DomainId { get; set; }
    public int Status { get; set; }
}

// Virtual Table
public class StatisticsView
{
    [PrimaryKey, AutoIncrement]
    public int Id { get; set; }
    public int Timestamp { get; set; }
    public string Domain { get; set; }
    public int Status { get; set; }
}       
主要代码:

static void Main(string[] args)
{
    var dbFile = "stats.db";
    var domains = new[] { "stackoverflow.com", "superuser.com", "serverfault.com", "google.com", "microsoft.com" };
    var statList = new List<Statistics>();

    var sqlBase = new SQLiteConnection(dbFile);
    sqlBase.Execute("PRAGMA foreign_keys = ON");
    sqlBase.CreateTable<Domains>();
    sqlBase.CreateTable<Statistics>();

    Console.WriteLine(SQLite3.LibVersionNumber());

    var runTimestamp = (int)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds;
    foreach (var domain in domains)
    {
        HttpWebResponse resp = null;
        var status = -1;
        try
        {
            resp = (HttpWebResponse)WebRequest.Create("http://" + domain).GetResponse();
        }
        catch { };
        status = (int)resp.StatusCode;

        var stat = new Statistics();
        stat.Domain = new Domains(domain);
        stat.Status = status;
        stat.Timestamp = runTimestamp;

        statList.Add(stat);
    }

    sqlBase.InsertOrIgnoreAllWithChildren(statList); // Modification "INSERT" with "OR IGNORE"

    Console.WriteLine(@"Table ""Domains""");
    foreach (var table in sqlBase.Table<Domains>())
    {
        Console.WriteLine("Id: {0}\tDomain: {1}", table.Id, table.Domain);
    }
    Console.WriteLine();

    Console.WriteLine(@"Table ""Statistics""");
    foreach (var table in sqlBase.Table<Statistics>())
    {
        Console.WriteLine("Id: {0}\tDomain Id: {1}", table.Id, table.DomainId);
    }

    Console.WriteLine();
    Console.WriteLine("Press any key to exit...");
    Console.ReadKey();
}
static void Main(string[] args)
{
    var dbFile = "stats.db";
    var domains = new[] { "stackoverflow.com", "superuser.com", "serverfault.com", "google.com", "microsoft.com" };
    var statList = new List<StatisticsView>();

    var sqlBase = new SQLiteConnection(dbFile);
    sqlBase.CreateTable<Domains>();
    sqlBase.CreateTable<Statistics>();
    sqlBase.Execute("CREATE VIEW IF NOT EXISTS 'StatisticsView' AS SELECT Stat.Id, Stat.Timestamp, Dom.Domain, Stat.Status FROM Statistics AS Stat INNER JOIN Domains Dom ON Stat.DomainId=Dom.Id;");
    sqlBase.Execute("CREATE TRIGGER IF NOT EXISTS 'StatisticsViewInsert' INSTEAD OF INSERT ON 'StatisticsView' BEGIN INSERT OR IGNORE INTO Domains(Domain) VALUES(NEW.Domain); INSERT INTO Statistics(Timestamp, Status, DomainId) VALUES (NEW.Timestamp, NEW.Status, (SELECT Id FROM Domains WHERE Domain=NEW.Domain)); END");
    sqlBase.Execute("CREATE TRIGGER IF NOT EXISTS 'StatisticsViewUpdate' INSTEAD OF UPDATE ON 'StatisticsView' BEGIN INSERT OR IGNORE INTO Domains(Domain) VALUES(NEW.Domain); UPDATE Statistics SET Status=NEW.Status, Timestamp=NEW.Timestamp, DomainId=(SELECT Id FROM Domains WHERE Domain=NEW.Domain) WHERE Id=OLD.Id; END");
    sqlBase.Execute("CREATE TRIGGER IF NOT EXISTS 'StatisticsViewDelete' INSTEAD OF DELETE ON 'StatisticsView' BEGIN DELETE FROM Domains WHERE (Domain = OLD.Domain AND (SELECT COUNT(Id) FROM Statistics WHERE DomainId=(SELECT Id FROM Domains WHERE Domain=OLD.Domain)) < 2); DELETE FROM Statistics WHERE Id=OLD.Id; END");

    Console.WriteLine(SQLite3.LibVersionNumber());

    var runTimestamp = (int)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds;
    foreach (var domain in domains)
    {
        HttpWebResponse resp = null;
        var status = -1;
        try
        {
            resp = (HttpWebResponse)WebRequest.Create("http://" + domain).GetResponse();
        }
        catch { };
        status = (int)resp.StatusCode;

        var stat = new StatisticsView();
        stat.Domain = domain;
        stat.Status = status;
        stat.Timestamp = runTimestamp;

        statList.Add(stat);
    }

    sqlBase.InsertAll(statList);

    Console.WriteLine(@"Table ""Domains""");
    foreach (var table in sqlBase.Table<Domains>())
    {
        Console.WriteLine("Id: {0}\tDomain: {1}", table.Id, table.Domain);
    }
    Console.WriteLine();

    Console.WriteLine(@"Table ""Statistics""");
    foreach (var table in sqlBase.Table<Statistics>())
    {
        Console.WriteLine("Id: {0}\tDomain Id: {1}", table.Id, table.DomainId);
    }

    Console.WriteLine();
    Console.WriteLine("Press any key to exit...");
    Console.ReadKey();
}
static void Main(字符串[]args)
{
var dbFile=“stats.db”;
var domains=new[]{“stackoverflow.com”、“superuser.com”、“serverfault.com”、“google.com”、“microsoft.com”};
var statList=新列表();
var sqlBase=新的SQLiteConnection(dbFile);
Execute(“PRAGMA foreign_keys=ON”);
CreateTable();
CreateTable();
WriteLine(SQLite3.LibVersionNumber());
var runTimestamp=(int)(DateTime.UtcNow-newdatetime(1970,1,1)).TotalSeconds;
foreach(域中的var域)
{
HttpWebResponse resp=null;
var状态=-1;
尝试
{
resp=(HttpWebResponse)WebRequest.Create(“http:/”+domain.GetResponse();
}
捕获{};
状态=(int)对应的状态代码;
var stat=新统计数据();
统计域=新域(域);
状态=状态;
统计时间戳=运行时间戳;
statList.Add(stat);
}
sqlBase.InsertOrIgnoreAllWithChildren(statList);//用“或忽略”修改“插入”
Console.WriteLine(@“表”“域”“);
foreach(sqlBase.table()中的var表)
{
WriteLine(“Id:{0}\tDomain:{1}”,table.Id,table.Domain);
}
Console.WriteLine();
Console.WriteLine(@“Table”“Statistics”“);
foreach(sqlBase.table()中的var表)
{
WriteLine(“Id:{0}\t主Id:{1}”、table.Id、table.DomainId);
}
Console.WriteLine();
Console.WriteLine(“按任意键退出…”);
Console.ReadKey();
}
第一次跑步后,它看起来很好

但在第二次运行之后,当域重复时,sqlite扩展插入了错误的域id


我犯了什么错误?

每次尝试保存新统计数据时,您都在代码中创建新的
实体

SQLite Net Extensions需要引用对象的主键才能分配外键。您的
InsertOrIgnoreAllWithChildren
似乎正在将
10
分配给所有
对象,即使它们没有被插入

您需要做的是获取当前域,以便获得正确的主键

试着这样做:

var dbFile = "stats.db";
var statList = new List<Statistics>();

var sqlBase = new SQLiteConnection(dbFile);
sqlBase.Execute("PRAGMA foreign_keys = ON");
sqlBase.CreateTable<Domains>();
sqlBase.CreateTable<Statistics>();

// Fetch existing domains from database
var domains = sqlBase.Table<Domains>().toList();

if (domains.isEmpty()) {
    // Insert domains into database if they don't exist
    var domainNames = new[] { "stackoverflow.com", "superuser.com", "serverfault.com", "google.com", "microsoft.com" };
    domains = domainNames.Select(domainName => new Domain(domainName));
    sqlBase.InsertAll(domains);
}

var runTimestamp = (int)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds;
foreach (var domain in domains)
{
    HttpWebResponse resp = null;
    var status = -1;
    try
    {
        resp = (HttpWebResponse)WebRequest.Create("http://" + domain.domain).GetResponse();
    }
    catch { };
    status = (int)resp.StatusCode;

    var stat = new Statistics();
    stat.Domain = domain; // Assign the existing domain object
    stat.Status = status;
    stat.Timestamp = runTimestamp;

    statList.Add(stat);
}

// Insert only Statistics (Domains already exist), and assign foreign keys
sqlBase.InsertAllWithChildren(statList);
var dbFile=“stats.db”;
var statList=新列表();
var sqlBase=新的SQLiteConnection(dbFile);
Execute(“PRAGMA foreign_keys=ON”);
CreateTable();
CreateTable();
//从数据库中获取现有域
var domains=sqlBase.Table().toList();
if(domains.isEmpty()){
//如果域不存在,则将其插入数据库
var domainNames=new[]{“stackoverflow.com”、“superuser.com”、“serverfault.com”、“google.com”、“microsoft.com”};
domains=域名。选择(域名=>新域名(域名));
sqlBase.InsertAll(域);
}
var runTimestamp=(int)(DateTime.UtcNow-newdatetime(1970,1,1)).TotalSeconds;
foreach(域中的var域)
{
HttpWebResponse resp=null;
var状态=-1;
尝试
{
resp=(HttpWebResponse)WebRequest.Create(“http:/”+domain.domain.GetResponse();
}
捕获{};
状态=(int)对应的状态代码;
var stat=新统计数据();
stat.Domain=Domain;//分配现有的域对象
状态=状态;
统计时间戳=运行时间戳;
statList.Add(stat);
}
//仅插入统计信息(域已存在),并分配外键
sqlBase.InsertAllWithChildren(statList);

每次尝试保存新统计信息时,您都会在代码中创建新的
实体

SQLite Net Extensions需要引用对象的主键才能分配外键。您的
InsertOrIgnoreAllWithChildren
似乎正在将
10
分配给所有
对象,即使它们没有被插入

您需要做的是获取当前域,以便获得正确的主键

试着这样做:

var dbFile = "stats.db";
var statList = new List<Statistics>();

var sqlBase = new SQLiteConnection(dbFile);
sqlBase.Execute("PRAGMA foreign_keys = ON");
sqlBase.CreateTable<Domains>();
sqlBase.CreateTable<Statistics>();

// Fetch existing domains from database
var domains = sqlBase.Table<Domains>().toList();

if (domains.isEmpty()) {
    // Insert domains into database if they don't exist
    var domainNames = new[] { "stackoverflow.com", "superuser.com", "serverfault.com", "google.com", "microsoft.com" };
    domains = domainNames.Select(domainName => new Domain(domainName));
    sqlBase.InsertAll(domains);
}

var runTimestamp = (int)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds;
foreach (var domain in domains)
{
    HttpWebResponse resp = null;
    var status = -1;
    try
    {
        resp = (HttpWebResponse)WebRequest.Create("http://" + domain.domain).GetResponse();
    }
    catch { };
    status = (int)resp.StatusCode;

    var stat = new Statistics();
    stat.Domain = domain; // Assign the existing domain object
    stat.Status = status;
    stat.Timestamp = runTimestamp;

    statList.Add(stat);
}

// Insert only Statistics (Domains already exist), and assign foreign keys
sqlBase.InsertAllWithChildren(statList);
var dbFile=“stats.db”;
var statList=新列表();
var sqlBase=新的SQLiteConnection(dbFile);
Execute(“PRAGMA foreign_keys=ON”);
CreateTable();
CreateTable();
//从数据库中获取现有域
var domains=sqlBase.Table().toList();
if(domains.isEmpty()){
//如果域不存在,则将其插入数据库
var domainNames=new[]{“stackoverflow.com”、“superuser.com”、“serverfault.com”、“google.com”、“microsoft.com”};
domains=域名。选择(域名=>新域名(域名));
sqlBase.InsertAll(域);
}
var runTimestamp=(int)(DateTime.UtcNow-newdatetime(1970,1,1)).TotalSeconds;
foreach(域中的var域)
{
HttpWebResponse resp=null;
var状态=-1;
尝试
{
resp=(HttpWebResponse)WebRequest.Create(“http:/”+domain.domain.GetResponse();
}
捕获{};
状态=(int)对应的状态代码;
var stat=新统计数据();
stat.Domain=Domain;//分配现有的域对象
状态=状态;
统计时间戳=运行时间戳;
statList.Add(stat);
}
//仅插入统计信息(域已存在),并分配外键
sqlBase.InsertAllWithChildren(statList);

您可以使用
作为主键(和外键),当前代码将按预期工作:

public class Domains
{
    public Domains() { }
    public Domains(string domain) { this.Domain = domain; }

    [PrimaryKey, MaxLength(64)]
    public string Domain { get; set; }
}

public class Statistics
{
    [PrimaryKey, AutoIncrement]
    public int Id { get; set; }
    public int Timestamp { get; set; }

    [ForeignKey(typeof(Domains))]
    public String DomainId { get; set; }

    public int Status { get; set; }

    [ManyToOne(CascadeOperations = CascadeOperation.All)]
    public Domains Domain { get; set; }
}

您可以使用
Domain
作为主键(和外键),当前代码将按预期工作:

public class Domains
{
    public Domains() { }
    public Domains(string domain) { this.Domain = domain; }

    [PrimaryKey, MaxLength(64)]
    public string Domain { get; set; }
}

public class Statistics
{
    [PrimaryKey, AutoIncrement]
    public int Id { get; set; }
    public int Timestamp { get; set; }

    [ForeignKey(typeof(Domains))]
    public String DomainId { get; set; }

    public int Status { get; set; }

    [ManyToOne(CascadeOperations = CascadeOperation.All)]
    public Domains Domain { get; set; }
}
我的错,是我的错