繁体   English   中英

具有多对一功能的SQLite.Net扩展无法按预期工作

[英]SQLite.Net Extensions with Many-To-One doesn't work as expected

我的表将包含许多重复的字符串,例如域。 为了最大程度地减少数据库大小,我只想在其他表中保存唯一域,并在主表中使用域ID。

一直以来,我都是手动进行的,但是不久前,我发现SQLite可以自动进行。

现在,我尝试使用带有“ FOREIGN”键的多对一关系,但没有成功。 也许我做错了。

范例程式码

表类:

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

主要代码:

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

第一次运行后,看起来还不错。

首轮

但是在第二次运行后,当域重复时-sqlite扩展名插入了错误的域ID

第二次跑

我在哪里犯错?

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

SQLite-Net Extensions需要引用对象的主键才能分配外键。 似乎您的InsertOrIgnoreAllWithChildren 10分配给所有Domains对象,即使未插入它们也是如此。

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

尝试这样的事情:

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

您可以将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; }
}

我的错误是功能“外键”不起作用。 因此,我将视图与联接两个表和触发器一起用于编辑视图。

它可以按我的需要工作。

SQLite.Net和SQLite.Net.Extensions不支持视图和触发器,因此我仅使用SQLite.Net和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

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

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

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

表类:

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

第二次运行后:

在此处输入图片说明

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM