C#، عناصر دفعة LINQ - ما هي أفضل طريقة للقيام بذلك؟
-
30-09-2019 - |
سؤال
لدي قواعد بيانات - (1) الخلاصات ، (2) الإنتاج. يتم تغذية قاعدة بيانات Feeds من ملفات العميل التي نحصل عليها يوميًا وهي ، من وجهة نظري ، مصدر قراءة فقط للبيانات. عند استلام ملف التغذية ، يقوم تطبيق التغذية بعمله ، ثم يتصل أخيرًا بخدمة ويب على موقع الإنتاج. تقوم خدمة الويب هذه بعد ذلك بمزامنة بين DB Feeds و Prod DB. في جوهر هذا هو رمز الزائفة:
- احصل على جميع عناصر التغذية للعميل
- احصل على جميع عناصر prod لنفس العميل
- باستخدام LINQ للكائنات ، احصل على (1) جميع العناصر التي تسبب تحديثًا ، (2) جميع العناصر التي تسبب حذفًا و (3) جميع العناصر التي تسبب إدراجًا.
- تحديثات العملية
- عملية حذف
- إدراج العملية
بالنسبة للقطعة الأساسية من التعليمات البرمجية التي تفصل بين الإدراج والتحديثات والحذف:
List<model.AutoWithImage> feedProductList = _dbFeed.AutoWithImage.Where(feedProduct => feedProduct.ClientID == ClientID).ToList();
List<model.vwCompanyDetails> companyDetailList = _dbRiv.vwCompanyDetails.Where(feedProduct => feedProduct.ClientID == ClientID).ToList();
foreach (model.vwCompanyDetails companyDetail in companyDetailList)
{
List<model.Product> rivProductList = _dbRiv.Product.Include("Company").Where(feedProduct => feedProduct.Company.CompanyId == companyDetail.CompanyId).ToList();
foreach (model.AutoWithImage feedProduct in feedProductList)
{
bool alreadyExists = false;
model.Company company = null;
foreach (model.Product rivProduct in rivProductList)
{
if (feedProduct.StockNumber == rivProduct.SKU)
{
alreadyExists = true;
// Active feed items...
if (feedProduct.Active)
{
// Changed since last sync...
if (feedProduct.Updated > rivProduct.LastFeedUpdate)
{
model.Product updateProduct = new model.Product();
updateProduct.ProductId = rivProduct.ProductId;
// removed for brevity
updateProductList.Add(updateProduct);
}
// Not changed since last sync...
else if (feedProduct.Updated <= rivProduct.LastFeedUpdate)
{
//nop
}
}
// No longer active feed products...
else if (!feedProduct.Active)
{
model.Product deleteProduct = new model.Product();
deleteProduct = rivProduct;
// removed for brevity
deleteProductList.Add(deleteProduct);
}
}
if (company == null)
company = rivProduct.Company;
}
// Found feedProduct new product...
if (!alreadyExists)
{
model.Product insertProduct = new Product();
insertProduct.ProductId = Guid.NewGuid();
// removed for brevity
insertProductList.Add(insertProduct);
}
}
}
نعم ، أعرف أن هناك طرقًا أكثر كفاءة للقيام بذلك وبدأت في استخدامها. ومع ذلك ، فإن الكود أعلاه يعمل ، بسرعة نسبيا ويحطم بيانات بلدي إلى 3 قائمة <> مجموعات.
سؤالي أكثر في التعامل مع طريقة _dbriv.savechanges (). عندما أصدرها يبدو لإطلاق جميع المجموعات الثلاث (أعلاه). أحاول تعقب انتهاك رئيسي فريد من نوعه ، مع هذا في دفعة لا أجد سجلًا أو سجلين مذنبين بانتهاك القيد. أنا متأكد من أنني فاتني شيء في مكان ما في تفكيري حول كيفية عمل LINQ لـ SQL حقًا.
ما أود فعله هو:
- تنفيذ حفظ على التحديثات فقط. قم ببعض الأشياء الأخرى ومن بعد,
- تنفيذ حفظ على الحذف فقط. قم ببعض الأشياء الأخرى ومن بعد,
- تنفيذ حفظ ، واحد تلو الآخر (الآن) على الإدراج.
هل هناك طريقة ما لإصدار Savechanges على دفعة واحدة في وقت واحد؟
هل هناك طريقة لتسجيل كائن insertProductList والقيام بصف واحد في وقت واحد؟ هل أنا نباح الشجرة الخاطئة؟
تعديل:على الرغم من أنني أعلم أنه يمكنني الاتصال بـ Proc المخزنة من EF ، إلا أن نيتي هي معرفة كيفية تحويل Proc المخزنة إلى EF.
لقد كتبت ما أريده في SQL وهنا (وهذا يعمل بالضبط ما نحتاج إليه):
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROC [dbo].[ExecuteSync] @ClientID AS BIGINT AS
BEGIN
DECLARE @cid UNIQUEIDENTIFIER
DECLARE c1 CURSOR FOR
SELECT CompanyID FROM CompanyDetails WHERE ClientID = @ClientID
OPEN c1
FETCH NEXT FROM c1 INTO @cid
WHILE @@FETCH_STATUS = 0
BEGIN
SET NOCOUNT ON
SELECT 'Syncing feed data for ' + CompanyName FROM Company WHERE CompanyId = @cid
SET NOCOUNT OFF
-- n/a --------------------------------------------------------------------------------------------------------------------------------------------------------------------
--SELECT a.*
-- , p.*
-- FROM RIVFeeds..AutoWithImage a
-- INNER JOIN Product p ON a.StockNumber = p.SKU
-- WHERE ClientID = @ClientID
-- AND a.Active = 1
-- AND a.Updated <= p.LastFeedUpdate
-- Needs UPDATE -----------------------------------------------------------------------------------------------------------------------------------------------------------
PRINT '--[ UPDATE ]--'
UPDATE Product
SET [Description] = ''
, [Image] = a.ImageURL
, isDeleted = a.Active ^ 1
, isFromFeed = 1
, LastFeedUpdate = a.Updated
, LowestPrice = a.GuaranteedSalePrice
, RetailPrice = a.ListPrice
, [Title] = ''
, Updated = GETUTCDATE()
, UpdatedBy = 'Feed Sync Process'
FROM RIVFeeds..AutoWithImage a
INNER JOIN Product p ON a.StockNumber = p.SKU AND a.AutoID = p.alternateProductID
WHERE ClientID = @ClientID
AND p.CompanyID = @cid
AND a.Updated > p.LastFeedUpdate
-- Needs BACKUP -----------------------------------------------------------------------------------------------------------------------------------------------------------
PRINT '--[ BACKUP #1 ]--'
INSERT INTO ProductDeleted(ProductId, alternateProductID, CompanyID, CharacterId, URLDomain, SKU, Title, Description, ButtonConfig, RetailPrice, LowestPrice, Image
, BackgroundColor, FontColor, buttonPositionCSS, isFromFeed, isDeleted, LastFeedUpdate, Created, CreatedBy, Updated, UpdatedBy)
SELECT p.ProductId, p.alternateProductID, p.CompanyID, p.CharacterId, p.URLDomain, p.SKU, p.Title, p.Description, p.ButtonConfig, p.RetailPrice, p.LowestPrice, p.Image
, p.BackgroundColor, p.FontColor, p.buttonPositionCSS, p.isFromFeed, p.isDeleted, p.LastFeedUpdate, p.Created, p.CreatedBy, GETUTCDATE(), 'Feed Sync Process'
FROM Product p
WHERE p.isDeleted = 1
AND p.CompanyID = @cid
-- Needs DELETE -----------------------------------------------------------------------------------------------------------------------------------------------------------
PRINT '--[ DELETE #1 ]--'
DELETE FROM Product
WHERE CompanyID = @cid
AND isDeleted = 1
-- Needs INSERT -----------------------------------------------------------------------------------------------------------------------------------------------------------
PRINT '--[ INSERT ]--'
INSERT INTO Product(ProductId, alternateProductID, CompanyID, CharacterId, URLDomain, SKU, Title, Description, ButtonConfig, RetailPrice, LowestPrice, Image
, BackgroundColor, FontColor, buttonPositionCSS, isFromFeed, isDeleted, LastFeedUpdate, Created, CreatedBy)
SELECT NEWID()
, a.AutoID
, @cid
, ''
, ''
, a.StockNumber
, ''
, ''
, ''
, a.ListPrice
, a.GuaranteedSalePrice
, COALESCE(a.ImageURL, '')
, ''
, ''
, ''
, 1
, 0
, a.Updated
, GETUTCDATE()
, 'Feed Sync Process'
FROM RIVFeeds..AutoWithImage a
WHERE a.ClientID = @ClientID
AND a.StockNumber NOT IN (SELECT p.sku FROM Product p WHERE CompanyID = @cid AND isFromFeed = 1)
AND a.AutoID NOT IN (SELECT p.alternateProductID FROM Product p WHERE CompanyID = @cid AND isFromFeed = 1)
AND a.Active = 1
--PRINT @cid
FETCH NEXT FROM c1 INTO @cid
END
CLOSE c1
DEALLOCATE c1
END
GO
الآن أنا أكتبها في التعليمات البرمجية باستخدام أطر الكيانات (لم تكتمل حتى الآن):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using RivWorks.Model;
using RivWorks.Model.Entities;
using RivWorks.Model.Feeds;
using RivWorks.Model.RivData;
using model = RivWorks.Model.Entities;
namespace RivWorks.Controller.Sync
{
public static class Feeds
{
#region Public Methods
public static bool Product(long ClientID)
{
bool retFlag = true;
DateTime startTime = DateTime.Now;
DateTime splitTime = startTime;
Guid companyID;
DateTime createdUpdated = DateTime.UtcNow;
string createdUpdatedBy = "Feed Sync Process";
List<SyncMessage> Activity = new List<SyncMessage>();
List<model.Product> insertProductList = new List<Product>();
List<model.Product> updateProductList = new List<Product>();
List<model.Product> deleteProductList = new List<Product>();
using (RivEntities _dbRiv = new RivWorksStore(Stores.RivConnString).NegotiationEntities())
{
using (FeedsEntities _dbFeed = new FeedStoreReadOnly(Stores.FeedConnString).ReadOnlyEntities())
{
List<model.AutoWithImage> feedProductList = _dbFeed.AutoWithImage.Where(a => a.ClientID == ClientID).ToList();
List<model.vwCompanyDetails> companyDetailList = _dbRiv.vwCompanyDetails.Where(a => a.ClientID == ClientID).ToList();
foreach (model.vwCompanyDetails companyDetail in companyDetailList)
{
companyID = companyDetail.CompanyId;
List<model.Product> rivProductList = _dbRiv.Product.Include("Company").Where(a => a.Company.CompanyId == companyID).ToList();
#region Handle UPDATES...
var updateFeedProductList = from f in feedProductList
join r in rivProductList
on f.AutoID equals r.alternateProductID
where f.Updated > r.LastFeedUpdate.Value || f.Active == false
select f;
var updateRivProductList = from r in rivProductList
join f in feedProductList
on r.alternateProductID equals f.AutoID
where f.Updated > r.LastFeedUpdate.Value || f.Active == false
select r;
foreach (model.AutoWithImage feedProduct in updateFeedProductList)
{
bool alreadyExists = false;
foreach (model.Product rivProduct in updateRivProductList)
{
if (feedProduct.StockNumber == rivProduct.SKU && feedProduct.AutoID == rivProduct.alternateProductID)
{
alreadyExists = true;
// Active feed items...
if (feedProduct.Active)
{
// Changed since last sync...
if (feedProduct.Updated > rivProduct.LastFeedUpdate)
{
rivProduct.ProductId = rivProduct.ProductId;
rivProduct.Company = rivProduct.Company;
rivProduct.alternateProductID = feedProduct.AutoID;
rivProduct.Description = String.Empty.EnforceNoNull();
rivProduct.Image = feedProduct.ImageURL.EnforceNoNull();
rivProduct.isDeleted = false;
rivProduct.isFromFeed = true;
rivProduct.LastFeedUpdate = feedProduct.Updated;
rivProduct.LowestPrice = feedProduct.GuaranteedSalePrice;
rivProduct.RetailPrice = feedProduct.ListPrice;
rivProduct.Title = String.Empty.EnforceNoNull();
rivProduct.Updated = createdUpdated;
rivProduct.UpdatedBy = createdUpdatedBy;
}
// Not changed since last sync...
else if (feedProduct.Updated <= rivProduct.LastFeedUpdate)
{
// nop
}
}
}
}
}
_dbRiv.SaveChanges();
#endregion
#region Handle DELETES...
List<model.Product> deleteRivProductList = _dbRiv.Product
.Include("Company")
.Where(a => a.Company.CompanyId == companyID
&& a.isDeleted == true)
.ToList();
// transfer to ProductDelete table...
foreach (model.Product delProduct in deleteRivProductList)
{
model.ProductDeleted productDeleted = new ProductDeleted();
productDeleted.alternateProductID = delProduct.alternateProductID;
productDeleted.BackgroundColor = delProduct.BackgroundColor;
productDeleted.ButtonConfig = delProduct.ButtonConfig;
productDeleted.buttonPositionCSS = delProduct.buttonPositionCSS;
productDeleted.CharacterId = delProduct.CharacterId;
productDeleted.CompanyID = companyID;
productDeleted.Created = delProduct.Created;
productDeleted.CreatedBy = delProduct.CreatedBy;
productDeleted.Description = delProduct.Description;
productDeleted.FontColor = delProduct.FontColor;
productDeleted.Image = delProduct.Image;
productDeleted.isDeleted = delProduct.isDeleted;
productDeleted.isFromFeed = delProduct.isFromFeed;
productDeleted.LastFeedUpdate = delProduct.LastFeedUpdate;
productDeleted.LowestPrice = delProduct.LowestPrice;
productDeleted.ProductId = delProduct.ProductId;
productDeleted.RetailPrice = delProduct.RetailPrice;
productDeleted.SKU = delProduct.SKU;
productDeleted.Title = delProduct.Title;
productDeleted.Updated = createdUpdated;
productDeleted.UpdatedBy = createdUpdatedBy;
productDeleted.URLDomain = delProduct.URLDomain;
_dbRiv.AddToProductDeleted(productDeleted);
}
int moves = _dbRiv.SaveChanges();
// delete the records...
foreach (model.Product delProduct in deleteRivProductList)
{
_dbRiv.DeleteObject(delProduct);
}
int deletes = _dbRiv.SaveChanges();
#endregion
#region Handle INSERTS...
// to be written...
#endregion
}
}
}
return retFlag; // remember to set this...
}
#endregion
}
}
أعلم أنه فوضوي بعض الشيء الآن. أنا بما في ذلك هذا ، لذلك إذا كان لدى أي شخص اقتراحات حول كيفية تنظيف هذا الأمر بشكل أفضل ، وطرق أفضل للاستفادة من EF للقيام بذلك ، وما إلى ذلك ، سأقدر ذلك. أعلم أن هناك بعض الطرق البطيئة للغاية للقيام بها عبر الكيانات وأرغب في التعلم بدلاً من إطلاق النار على نفسي.
المحلول 2
لقد حصلت على رمز في مكانه الآن. نظرًا لعدم الإجابة على أي شخص آخر ، يجب أن أفترض أنني أتجه في الاتجاه الصحيح. شكرًا.
نصائح أخرى
أين تقوم بالفعل بإدخال السجلات أو حذفها؟ أراك تضيفهم إلى insertProductList
و deleteProductList
, ، لكنك لا تتصل أبدًا بإدراج أو حذف على طاولتك _dbRiv.Product
.
أعتقد أن ما تحاول إنجازه هو شيء مثل ما يلي:
//perform all updates
_dbRiv.SubmitChanges();
//perform all deletes
_dbRiv.Product.DeleteAllOnSubmit(deleteProductList);
_dbRiv.SubmitChanges();
//perform inserts, one at a time
foreach(model.Product p in insertProductList)
{
_dbRiv.Product.InsertOnSubmit(p);
_dbRiv.SubmitChanges();
}
ومع ذلك ، ليس من الواضح كيف تتوقع إجراء التحديثات. يبدو أنه بدلاً من إنشاء مثيل جديد model.Product
وتعيين خصائصه ، يجب عليك تحديث خصائص rivProduct
. خلاف ذلك ، مع الرمز الذي لديك ، أعتقد أنك ستحتاج إلى إرفاقها updateProduct
استخدام _dbRiv.Product.Attach(updateProduct, rivProduct)
بحيث ستعرف L2S أي خصائص تغيرت.