传递项目集合以在LINQ中搜索

时间:2014-07-23 18:04:25

标签: c# linq

我有一个制表符分隔的文本文件我将其行导入数据库但不是所有记录,只插入数据库中尚不存在的行。 所以我遍历这个文件的行,在for-each循环中调用我的存储库查询:

 while (streamreader.Peek() > 0)
 {
    string[] currentRowValues = streamreader.ReadLine().Split(delimiter);
    string lastName = currentRowValues[columnHeadertoLocationy["LName"]];
    string firstName = currentRowValues[columnHeadertoLocationy["FName"]];
    string middleName = currentRowValues[columnHeadertoLocationy["FName"]];
    if(!repo.IsProviderEligibleForImport(lastName, firstName, middleName)
    {
        // ok he does not already exist in DB, so go ahead and insert it, etc       
    }
// ....
}


    public bool IsProviderEligibleForImport(string lastName, string firstName, string midName)
    {
        var query = (from p in this.Context.Providers
            where
                p.FirstName == firstName &&
                p.LastName == lastName &&
                p.MidName== midName
            select p).FirstOrDefault();

        if (query == null)
            return false;
        return true;
    }

如下所示:

foreach row in TextFile
  bool allowInsert = IsProviderEligibleForImport(pass params)

所以我看到的问题是我的测试文件有108450行!因此,通过这种方法,我将在数据库查询108450次,这会破坏性能!我想知道是否有一些更聪明的方式,所以我传递所有记录在一个通道中搜索而不是每行一次?

2 个答案:

答案 0 :(得分:2)

假设您使用的是SQL 2008或更高版本,使用a Table Valued Parameter的存储过程可能是最佳方法。

根据“插入它等”代码的复杂程度,您可以在存储过程中执行插入操作,也可以将新记录返回到代码进行处理。 / p>

表格类型:

CREATE TYPE dbo.udt_ProviderImport AS TABLE 
(
    -- TODO: Match the data-types and lengths to your table:
    LastName    nvarchar(100) NOT NULL,
    FirstName   nvarchar(100) NOT NULL,
    MidName     nvarchar(100) NOT NULL
);

存储过程:
如果您只想将新记录返回到代码中以进行进一步处理,请删除INSERT INTO (...)块。

CREATE PROCEDURE dbo.usp_ImportProviderIfEligible
(
    @data    dbo.udt_ProviderImport READONLY
)
As
BEGIN
    INSERT INTO dbo.Providers
    (
        LastName,
        FirstName,
        MidName
    )
    SELECT
        LastName,
        FirstName,
        MidName
    FROM
        @data As D
    WHERE
        Not Exists
        (
            SELECT 1
            FROM dbo.Providers As P
            WHERE P.LastName = D.LastName
            And P.FirstName = D.FirstName
            And P.MidName = D.MidName
        )
    ;
END

C#代码:

var dt = new DataTable();
dt.Columns.Add("LastName", typeof(string));
dt.Columns.Add("FirstName", typeof(string));
dt.Columns.Add("MidName", typeof(string));

while (streamreader.Peek() > 0)
{
    string[] currentRowValues = streamreader.ReadLine().Split(delimiter);
    string lastName = currentRowValues[columnHeadertoLocationy["LName"]];
    string firstName = currentRowValues[columnHeadertoLocationy["FName"]];
    string middleName = currentRowValues[columnHeadertoLocationy["MName"]];
    dt.Rows.Add(lastName, firstName, middleName);
}

var data = new SqlParameter("data", SqlDbType.Structured);
data.TypeName = "dbo.udt_ProviderImport";
data.Value= dt;

// Use this if you're inserting directly from the stored procedure:
Context.Database.ExecuteSqlCommand("EXEC dbo.usp_ImportProviderIfEligible @data", data);

// If you're returning the new records for further processing,
// create a type to hold the returned values, and use:
// var results = Context.Database.SqlQuery<ResultType>("EXEC dbo.usp_ImportProviderIfEligible @data", data);

答案 1 :(得分:1)

我认为这会奏效。创建一个类或类似的东西来存储您的文本文件条目:

public class TextEntry
{
    public string FirstName {get; set;}
    public string LastName {get; set;}
    public string Middle {get; set;}
}

然后,我相信您可以使用下面的第二行点击数据库一次。 (对于大文件,可能需要很长时间)。然后,您可以根据内存中的条目进行过滤。

var textList = new List<TextEntry>();

//load all the entries from the text file

var providersInDB = Context.Providers.Where(p => textList.Any(t => p.FirstName == t.FirstName && p.LastName == t.LastName && p.MidName == t.Middle));

var textEntriesNotInDB = textList.Where(t => !providersInDB.Any(p => p.FirstName == t.FirstName && p.LastName == t.LastName && p.MidName == t.Middle));
//add those text entries

要对此进行扩展,您还可以修改TextEntry类以覆盖接受另一个EqualsTextEntry的{​​{1}}方法,然后您可以使用{{ 1}}而不是Provider我相信。