PDA

View Full Version : سوال: ذخیره اطلاعات همزمان دو جدول وابسته به هم



علی فتحی
جمعه 22 خرداد 1394, 01:24 صبح
سلام من دو جدول دارم با فیلد مشترک یکی شماره سند و تاریخ جدول دومی جزیات سند شامل شماره سند و غیره

اگر اطلاعات جدول اول رو ثبت کنم یعنی شماره سند مقدار داشته باشه برای ثبت جزیات مشکلی نیست و لی اگر مقدار نداشته باشه معلومه که ثبت ممکن نیست بدلیل کلید اصلی جدول اصلی.

حالا من با یک باتن چگونه میتونم اطلاعات هر دو جدول رو کنترل کنم. یعنی اول چک کنه که شماره سند در جدول اصلی ثبت شده یا نه .اگر نشده ثبت کنه بعد ادامه بده و جدول دوم ثبت بشه اگر هم ثبت شده بره سراغ جدول دوم:

این کدو نوشتم برا بار اول درست کار میکنه ولی در مرحله دوم ارور داره

private void buttonX2_Click(object sender, EventArgs e) {
int sanad = int.Parse(textBoxX1.Text);
if (db.tblTransactionIDs.Any(item => item.TransactionID != sanad))
{
tblTransactionID SD = new tblTransactionID();
SD.TransactionID = int.Parse(textBoxX1.Text);
SD.Date = maskedTextBoxAdv1.Text;
SD.Descriotion = textBoxX3.Text;
SD.Atf = textBoxX2.Text;
db.tblTransactionIDs.AddObject(SD);
db.SaveChanges();

}
else
{


tblTransaction ff = new tblTransaction();
ff.TransationID = int.Parse(textBoxX1.Text);
ff.AccountNumber = int.Parse(comboBoxEx1.Text);
ff.MoinNumber = int.Parse(comboBoxEx2.Text);
ff.Description = textBoxX4.Text;
ff.Debit = int.Parse(textBoxX6.Text);
ff.Credit = int.Parse(textBoxX7.Text);
db.tblTransactions.AddObject(ff);
db.SaveChanges();
tblTransactionBindingSource.DataSource = db.tblTransactions.ToList();

ali_md110
جمعه 22 خرداد 1394, 02:14 صبح
اگر ازEf استفاده میکنید پشنهاد میکنم از الگوی Unit Of Work استفاده کنید
این الگو میگه که یک دیتاکانتکس آماده رو برای چند تراکنش استفاده کن بجای اینکه برای هر عملی هربار دیتاکانتکست رو وهله سازی کنیم
برای تراکنش های همزمان مثل همین روابط یک به چند مناسب هست
بعدش اینکه روش شما خیلی صحیح و حرفه ای نیست


if (db.tblTransactionIDs.Any(item => item.TransactionID != sanad))
{

نیاز به جستجو زدن در هنگام ثبت رابطه یک به چند نیست
شما مگه از رابطه یک به چند استفاده نمیکنید؟
وقتی موجودیت بدین صورت باشه
جدول گروه کالا


public class ProductGroup
{
public virtual int ProductGroupID { get; set; } )]
public virtual string ProductGroupName { get; set; }
public virtual ICollection<Product> Products { get; set; }
}

وکلاس کالاها



public class Product
{

public virtual int ProductID { get; set; }
[ForeignKey("ProductGroupID")]
public virtual ProductGroup ProductGroup { get; set; }
public virtual int? ProductGroupID { get; set; }
public virtual string ProductName { get; set; }
public virtual string ModelName { get; set; } //نام مدل-برند یک کالا
public virtual decimal UnitPrice { get; set; }
}



برای ثبت سند میتونید ابتدا یک وهله از کلاس ProductGroup بسازید
و داده ها رو اضافه کنید

و یک لیست تعریف و برابر با لیست درون کلاس ProductGroup قرار بدبد و داده های جدول Product رو به این لیست اضافه کنید
وقتی SaveChange بزنید همزمان در دوجدول ذخیره میشه


Public ProductGroup cat{get;set;}
public ICollection<Product> Productlist { get; set; }
void insert()
{
cat=new ProductGroup ();

cat.نام فیلد=مقدار
db.ProductGroups.Add(cat(
cat.Products=Productlist ;
Productlist .Add(اضافه کردن کالا)
db.savechanges();
}

علی فتحی
جمعه 22 خرداد 1394, 13:59 عصر
ممنون از راهنماییتون .ولی جدول شماره 1 یعنی سند - در مسال بالا گروه کالا فقط یک بار ثبت میشه نه چندین بار =یعنی کد شما رو نتونستم پیاده سازی کنم

ali_md110
جمعه 22 خرداد 1394, 14:12 عصر
بعد از هر بار درج در سربرگ و Savechange تا زمانی که وهله سازی مجدد صورت نگیره نمیتونید رکورد دیگه وارد کنید و فقط میتوند ویرایش کنید چون ef بصورت پویا درون حافضه موجودیت ProductGroup ی رو نگه داشته که new سازی شده یاشه
منظورتون از چند بار توضیح بدید ؟

علی فتحی
جمعه 22 خرداد 1394, 14:37 عصر
ببخشد مچبور شدم تصویر توضیح بدم :اگر طبق تصویر تایید سند رو بزنم اطلاعات داخل دیتاگرید 1 وجود داشت مشکلی برای ثبت جزیات نداریم:

سوالم اینه با ثبت باتن ثبت سند پایین عکس بتونم عملیات تایید سند بالا رو هم بزتن باتن کادر سبز رنگ.


تایید سند


private void buttonX1_Click(object sender, EventArgs e) {
tblTransactionID SD = new tblTransactionID();
SD.TransactionID = int.Parse(textBoxX1.Text);
SD.Date = maskedTextBoxAdv1.Text;
SD.Descriotion = textBoxX3.Text;
SD.Atf = textBoxX2.Text;
db.tblTransactionIDs.AddObject(SD);
db.SaveChanges();
tblTransactionIDBindingSource.DataSource = db.tblTransactionIDs.ToList();

ali_md110
جمعه 22 خرداد 1394, 14:47 عصر
شما داد ه های سرسند و جزئیات سند رو از طریق تکست باکس وارد میکنید؟ سند دوم رو به موجودیت اضافه نکردید در این کدهای شما فقط سرسند داره اضافه میشه جزئیات سند اضافه نکرده اید

ببینید شما میتونید Break point بزارید روی ردیف های کد و ردگیری کنید

علی فتحی
جمعه 22 خرداد 1394, 15:01 عصر
من نمیدونم چرا نتونستم مطلب رو برسونم : کد بالا عرض کردم فقط برای سر سند هستش. کد ته سند رو دارم ولی نفرستادم . دوتا جدول بالا جوین هستن دوست عزیز از روی تصویر که معلومه
تیم ویور داری بیای سورس رو ببینی؟

reza_ali202000
جمعه 22 خرداد 1394, 15:08 عصر
فکر میکنم یه خورده ساختار فرمتون رو تعییر بدین. فکر نمیکنم توی یه فرم دوتا ثبت برا یه جدول کار اصولی باشه و اگه هم واقعا نیاز داره بهتره هرکدوم از ثبت ها اتفاق افتاد پیغام بده و خارج بشه از فرم تا مشکلی پیش نیاد.
یا لاقل متن تکس باکسها خالی بشه.

ali_md110
جمعه 22 خرداد 1394, 15:15 عصر
کد ته سند رو هم بزارید لطفا شما قسمتی از کد گذاشتید بقیه دیگه نیست
نیازی بع عکس و team هم نیست
ef بصورتی هست وقتی یک شی به موجودیتهای اون اضافه میشه تا وقتی اون رو null نکنید درون حافظه قرار داره اگر شما 10 تا دکمه ثبت با 10 تا savechanges بزارید باز برای ef یک عمل محسوب میشه البته اگر وهله سازی دیتاکانتکس شما واحد باشه

علی فتحی
جمعه 22 خرداد 1394, 15:17 عصر
:گریه::گریه::گریه::گریه::گریه: ای داد عزیزان منم همینو میخوام مگر در سوال بالا چی پرسیدم . من میخوام یکی از باتنهای ثبت رو پاک کنم از باتن زیر عکس =ثبت سند= برای هردو ذخیره استفاده کنم.جدول ها فرعی و اصلی هستن

من دیگه نمیدونم چگونه توضیح بدم .با عکس و کدو و جدول همشو اوردم.ولی متصفانه نمیدونم برنامه نویسان چرا مطلبو نمیگیرن

ali_md110
جمعه 22 خرداد 1394, 15:46 عصر
ببینید دو ست من شما مطلب رودرست نگرفتید
اگر یک کانتکس ef داشته باشیم


var db= new EfDbContext()


و کد درج هم اینطور باشه



void insert()
{
var cat=new ProductGroup ();

cat.نام فیلد=مقدار
db.ProductGroups.Add(cat(

var product= new Product()
product.ProductGroup=cat;
product.ProductName=""نام کالا
db.Products.Add(product)
}

در این متد دو شی به سیستم وارد شده یکی گروه کالا و دیگری کالا و گفتیم شی گروه کالا به عنوان کلید فرعی کالا هم قرار بگیر
product.ProductGroup=cat;
اگر db.SaveChanges رو هر جای برنامه صدا بزنیذ ( جاییکه به کانتکس دسترسی داریم)
عمل ذخیره شدن انجام میگیره
اگر در جدول دوم یعنی جزئیات سند کلید فرعی نال پذیر باشه ممکنه فقط جدول اول ذخیره بشه اگر خاصیت نال پذیری این کلید رو بردارید یعنی فیلدی بشه که باید مقدار داشته باشه ef خودش تشخیص میده و اگر در جدول دوم خطا داشته باشیم یا جدول دوم مقدار دهی نشده و به دیتاکانتکست اضافه نکرده باشیم هیچ کدام از جداول ذخیره نخواهند شد

علی فتحی
جمعه 22 خرداد 1394, 16:55 عصر
اینم کدی که شما فرمودین ولی فقط یک رکود رو ثبت میکنه و ارور میده

DataEntities db = new DataEntities(); tblTransactionID SD = new tblTransactionID();
SD.TransactionID = int.Parse(textBoxX1.Text);
SD.Date = maskedTextBoxAdv1.Text;
SD.Descriotion = textBoxX3.Text;
SD.Atf = textBoxX2.Text;
db.tblTransactionIDs.AddObject(SD);
//db.SaveChanges();
//MessageBox.Show("", "dff");


tblTransaction ff = new tblTransaction();
ff.TransationID = SD.TransactionID;
ff.AccountNumber = int.Parse(comboBoxEx1.Text);
ff.MoinNumber = int.Parse(comboBoxEx2.Text);
ff.Description = textBoxX4.Text;
ff.Debit = int.Parse(textBoxX6.Text);
ff.Credit = int.Parse(textBoxX7.Text);
db.tblTransactions.AddObject(ff);
db.SaveChanges();
tblTransactionBindingSource.DataSource = db.tblTransactions.ToList();

ali_md110
جمعه 22 خرداد 1394, 17:39 عصر
خب درسته باید یک رکورد ثبت کنه مگه میخواستید چند تا رکورد توی هر جدول ثبت کنه؟
Exception که پرتاب میکنه رو بفرستید
درضمن از کدوم ورژن Ef استفاده می کنید؟
متد AddObject رو هم استفاده نکیند از متد AddOrUpdate که یک متد الحاقی هست استفاده کنید

علی فتحی
جمعه 22 خرداد 1394, 17:51 عصر
ورژن 6 فکر کنم . هم.ن .یزوال استادیو 2010.شما عکس بالارو ببینید خواهشا . گرید اول یک بار ثبت میشه ولی گرید دوم تکرار داره .از عکس کاملا معلومه من تعجب میکنم حتی با قلم اشاره دادم که

علی فتحی
جمعه 22 خرداد 1394, 18:07 عصر
ورژن 6 فکر کنم . هم.ن .یزوال استادیو 2010.شما عکس بالارو ببینید خواهشا . گرید اول یک بار ثبت میشه ولی گرید دوم تکرار داره .از عکس کاملا معلومه من تعجب میکنم حتی با قلم اشاره دادم که

ali_md110
جمعه 22 خرداد 1394, 18:08 عصر
ویزوال استودیو 2010 ورزن پیشفرض 4 هست
ببینید منو گیچ کردید یکبار میگید فقط یک رکورد ذخیره میشه الان میگید جدول جزئیات رکورد تکراری ثبت میکنه
شما میخواین به ازای یک رکورد در چدول سر سند مثلا رکوردی که با کد 1 در چدول سرسند ذخیره شده بیاد 1 رکورد در جدول جزئیات ثبت کنه ولی اومده 4تا و 3تا ثبت شده
خب این بر میگرده به این که شما دارید عمل نوسازی رو درون دکمه ذخیره انجام میدید و با هربار کلیک میاد یک وهله سازی انجام میده و رکورد جدید ذخیره میکنه
شما اگر میخواید اینطور نشه باید عمل وهله سازی کلاس tblTransaction و tblTransactionID توی یک دکمه دیگه مثلا دکمه ADD و یا فرم لود بزارید و مقدار دهی موجودیتها و ذخیره شدن SaveChanges رو درون دگمه Save بزارید
مشکل شما اینجا هست

علی فتحی
جمعه 22 خرداد 1394, 18:16 عصر
اقای مهندس دقیقا مثل اینه که بیایم گروه کالارو تعریف کنیم :مثلا لوازم خانگی =بعد بیایم داخل جدول دو انواع لواز م خانگی درج کنیم مثلا یخچال -تلویزیون - جاروبرقی و...
فکر کنم از این واضحتر دیگه نمیشه

خب اگر اول گروه رو تعریف کنیم بهد کالارو مشکلی وجود نداره چون کدگروه در داخل کالا وجود داره:

من میخوام هنگام ثبت هم گروه و هم کالارو ثبت کنم همزمان باهم: سیستم چک کنه اگر گروه لوازم خانگی با کد 1 وجود داشت لوازم ثبت بشه .اگر وجود نداشت اول گروه رو ثبت کنه بعد بره سراغ کالا.خب مصلم هست که گروه یکبار ثبت میشه و لی لوازم خانگی الی اخر...

ali_md110
جمعه 22 خرداد 1394, 18:45 عصر
از اولش اینو میگفتید برادر من

معمولا این گونه چک کردنها جهت تکراری نبودن کد یک کالا یا نام یک کالا از صفت unique استفاده میکنن تا استثنا از سمت دیتاکانتکست و دیتابیس پرتاب شده و با یک try catch اون رو مدیریت بکنبم .
جستجو زدن کمی سرعت برنامه رو پایین میاره ولی شما چون میخواین قبل از پرتاب استثنا اون رو مدیریت کنید میتونید ابتدا توی یک لیست جستجو بزنید بدین صورت:

یک فیلد از نوع tblTransactionID بیرون از دکمه تعریف کنید


public tblTransactionID trs{get;set;}




var find=db.tblTransactionIDs.Find(item => item.TransactionID != sanad;
if(find!=null)
{
trs=find;
}
else
{

trs= new tblTransactionID();
trs.field=(مقادیر فیلدها)

}



db.tblTransactionIDs.AddOrUpdate(trs)

بعدش از اینجا به بعد میتونید یک نوع از کلاس جزئیات وهله سازی کنید و عمل SaveChange انجام بدید



tblTransaction ff = new tblTransaction();
ff.TransationID = trs.id);
ff.AccountNumber = int.Parse(comboBoxEx1.Text);
ff.MoinNumber = int.Parse(comboBoxEx2.Text);
ff.Description = textBoxX4.Text;
ff.Debit = int.Parse(textBoxX6.Text);
ff.Credit = int.Parse(textBoxX7.Text);
db.tblTransactions.AddOrUpdate(ff);
db.SaveChanges();

علی فتحی
شنبه 23 خرداد 1394, 23:06 عصر
نشد با تشکر ازدوستانی که وقت گذاشتن. من که موفق نشدم استفاده کنم . امیدوارم دیگران که میبینن بتونن و به دردشون بخوره:<br><br>بنده برای حل مشکلی ناچارا دو تاباتن تعریف کردم اول سند رو تایید میکنم جدول 1 بعد جزیات رو ثبت میزنم جدول 2<br><br>بازم متشکرم که وقت &nbsp;گذاشتین

ali_md110
یک شنبه 24 خرداد 1394, 00:12 صبح
سلام جناب فتحی
مشکل شما اصلن موضوع جدی نیست یک جای کار شما اشکال داره که کدهایی که در اخر فرستادم جواب نداده
روش کار که مشخص هست ابتدا توسط متد Find بر روی کلید اصلی شما که همون کد سرسند هست جستجو زده میشه اگر کد موجود باشه یک ردیف یا یک موجودیت از نوع سرسند برمیگردونه و فیلدی که بصورت عمومی تعریف کردیم رو برابر با این موجودیت قرار میده و دیگه عمل وهله سازی یا نوسازی انجام نمیشه که برای سر سند رکورد جدید ثبت کنه و اگر رکورد باشرط کلی اصلی موجود نباشه یک وهله سازی جدید انجام میشه و یک رکورد سرسند جدید زده میشه و باقی ماجرا برای جرئیات سند که مشخصه

جهت تکیمیل وقتی یک یا چند کلاس ساخته میشه وبه عنوان موجوذیت در نظر گرفته شد و اونها بویسله متد Setبه دیتاکانتکس معرفی شد دیتاکانتکس ef هوشمند هست هروقت که تغییرات یا رکوردهای جدید به این موجودیتها اضافه شده ef تشخیص میده و فقط کافی هست متد SaveChanges رو فراخوانی کنیم این SaveCahanges برای تمام موجودیتها که با یک دیتاکانتکس وهله ساز شده استفاده خواهد شد و همزمان عمل ذخیره سازی انجام میشه

علی فتحی
یک شنبه 24 خرداد 1394, 01:19 صبح
مرسی از توجهتون به سول: بار اول قبول میکنه ولی برای بارهای دو م و... باید فرم رو دوبارلود کنم وگر نه ثبت غیر ممکن خواهد بود.البته قضیه رو برعکس کردم شد فقط ایرادی که داره برای ثبت اولین رکود دوبار باید باتن رو کلیک کرد.

private void buttonX2_Click(object sender, EventArgs e) {
try
{
int sanad = int.Parse(textBoxX1.Text);
if (db.tblTransactionIDs.Any(item => item.TransactionID == sanad))
{
add();

}
else
{
tblTransactionID SD = new tblTransactionID();
SD.TransactionID = int.Parse(textBoxX1.Text);
SD.Date = maskedTextBoxAdv1.Text;
SD.Descriotion = textBoxX3.Text;
SD.Atf = textBoxX2.Text;
db.tblTransactionIDs.AddObject(SD);
db.SaveChanges();
}
}
catch
{
}


public void add() {
try
{
tblTransaction ff = new tblTransaction();
ff.TransationID = int.Parse(textBoxX1.Text);
ff.AccountNumber = int.Parse(comboBoxEx1.Text);
ff.MoinNumber = int.Parse(comboBoxEx2.Text);
ff.Description = textBoxX4.Text;
ff.Debit = int.Parse(textBoxX6.Text);
ff.Credit = int.Parse(textBoxX7.Text);
db.tblTransactions.AddObject(ff);
db.SaveChanges();
tblTransactionBindingSource.DataSource = db.tblTransactions.ToArray();

}
catch
{
}

علی فتحی
یک شنبه 24 خرداد 1394, 01:20 صبح
درضمن در کد شما کلمه find .در سیستم من ارور میده و شناخته شده نیست.ویزوال 2010 داری نمونه برات بفرستم؟

علی فتحی
یک شنبه 24 خرداد 1394, 01:55 صبح
نمونه در ویزوال 2010 .انتی تی 4

Mahmoud.Afrad
یک شنبه 24 خرداد 1394, 02:59 صبح
در مورد اینکه فقط یکبار عمل درج انجام میشه به خاطر شرط اشتباهی هست که در if چک میکنی.
برای افزودن جزئیات سند ، به جدول دستی اضافه نکنید. اگر به شئ SD دقت کنی یک لیست از نوع tblTransaction داره که میتونی به این لیست اضافه کنی.

DataEntities db = new DataEntities();
int id = int.Parse(textBox1.Text);
tblTransactionID SD = db.tblTransactionID.SingleOrDefault(item => item.TransactionID == id);
if (SD == null)
{
SD = new tblTransactionID();
SD.TransactionID = id;
db.tblTransactionID.AddObject(SD);
}

SD.Date = maskedTextBoxAdv1.Text;
SD.Descriotion = textBoxX3.Text;
SD.Atf = textBoxX2.Text;

for (int i = 0; i < dataGridView2.Rows.Count; i++)
{
DataGridViewRow row = dataGridView2.Rows[i];
object value = row.Cells["TransactionID"].Value;
if (value == null)
continue;
int rowId = Convert.ToInt32(value);
if (rowId != SD.TransactionID)
continue;

tblTransaction tt = new tblTransaction();
tt.AccountNumber = Convert.ToInt32(comboBoxEx1.Text);
tt.MoinNumber = int.Parse(comboBoxEx2.Text);
tt.Description = textBoxX4.Text;
tt.Debit = int.Parse(textBoxX6.Text);
tt.Credit = int.Parse(textBoxX7.Text);

SD.tblTransaction.Add(tt);
}


db.SaveChanges();


در مورد اینکه همه سطرهای دیتاگرید دوم ثبت بشه احتمالا باید از محتوای گرید برای مقداردهی به شئ tt استفاده کنی.

ali_md110
یک شنبه 24 خرداد 1394, 09:39 صبح
ابتدا شما باید Ef خودتون به نسخه های بالاتر ارتقاء بدید Entity Framework یک تکنولوزی تقریبا جدید ماکروسافت هست که به مرور داره اشکالات اون برطرف میشه و گسترده تر و پیشرفته تر میشه بعضی متد ها مثل Find که یک کلید اصلی دریافت میکنه در نسخه بالاتر وجود داره و چون بر اساس کلید اصلی جستجو میزنه سرعت بالاتری داره حتما ازش بهره ببرید

" البته در پست قبلی متد Find رو طبق سلیقه خودم بصورت یک Extensions نوشته بودم و توی پروزه هام استفاده میکردم که میتونست یک عبارت لامبدا به عنوان شرط بگیره که اینجا اشتباها برای شما ارسال کرده بودم"
متد Find نسخه 6 فقط میتونه یک کلید اصلی یا چند کلید اصلی بگیره و شرط با عبارت لامبدا قبول نمیکنه
کدهاتون رو تصحیح کردم



try
{
Table1 sd;
var sanad = int.Parse(textBox1.Text);
var tbl1 = db.Table1.SingleOrDefault(x => x.kodg == sanad);//هم میتونید استفاده کنید FirstOrDefault یا SingleOrDefault در نسخه بالاتر وجود دارد بجای ان از Findمتد
if (tbl1==null)
{
sd =new Table1 {kodg = int.Parse(textBox1.Text), name = textBox2.Text};//برای مقدار دهی فیلدها از سازنده استفاده شده که اختیاری هست ولی مرجح تر
db.Table1.AddObject(sd);

}
else
{
tbl1.kodg = int.Parse(textBox1.Text);
tbl1.name = textBox2.Text;
sd = tbl1;

}


///Table1 = sd برای ثبت کالا باید مشخص کنید که گروه ان چیست و در اینجا صراحتا مشخص میکنم بوسیله

var tb = new Table2 {kodg = int.Parse(textBox1.Text), namk = textBox3.Text,Table1 = sd};
db.Table2.AddObject(tb);
db.SaveChanges();//این متد میتواند بیرون از دکمه هم صدا زده شود
table2BindingSource.DataSource = db.Table2.ToList();

}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}