نمایش نتایج 1 تا 3 از 3

نام تاپیک: خطا موقع به روزرسانی و تغییر در Navigation Collection Property در Entity Framework 6.4

  1. #1

    خطا موقع به روزرسانی و تغییر در Navigation Collection Property در Entity Framework 6.4

    سلام دوستان

    در Entity Framework 6.4 Code First ، دو موجودیت دارم بصورت زیر :


    public class PhoneBookDbContext : DbContext
    {
    public virtual DbSet<PersonEntity> PersonEntities { get; set; }
    }


    public class PersonEntity
    {
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public virtual ICollection<MobileNumberEntity> MobileNumbers { get; set; }
    }


    public class MobileNumberEntity
    {
    public int Id{ get; set; }
    public string MobileNumber{ get; set; }
    public virtual PersonEntity PersonEntity { get; set; }
    public int PersonEntityId { get; set; }
    }


    رابطه ی بین موجودیت "شخص" و "شماره تلفن" ، بصورت یک به چند تعریف شده . یعنی هر شخص میتونه چندین شماره تلفن داشته باشه . اما برای هر شماره تلفن ، محدودیت unique تعریف شده .
    مخصوصا اینکه کلید خارجیِ PersonEntityId در جدول "MobileNumberEntity" ، بصورت not null هست (که انگار این قضیه ، به مشکلم ربط داره) .


    در کلاس Repository ، متد Edit ام بصورت زیر هست که یک Id ای ای که در دیتابیس موجود هست را میگیره و همچنین در پارامتر دوم اش ، شی ای از همون موجودیتِ ویرایش شده را میگیره و به روزرسانی میکنه .


    public void Edit(int entityIdInDatabase, TNonQueryEntity updateEntity)
    {
    updateEntity = updateEntity ?? throw new ArgumentNullException(nameof(updateEntity),
    ExceptionMessage.argumentNullExceptionMessage);


    TNonQueryEntity entityInDatabase = this.FindByLazyLoadingMode<TNonQueryEntity>(entity IdInDatabase);
    if (entityInDatabase == null)
    return;


    DbEntityEntry<TNonQueryEntity> nonQueryEntityEntry = this._context.Entry(entityInDatabase);
    DbPropertyValues nonQueryEntityPropertyValues = nonQueryEntityEntry?.CurrentValues;
    try
    {
    nonQueryEntityPropertyValues?.SetValues(updateEnti ty);
    }
    catch (InvalidOperationException operationException)
    {
    string checkMessage = "is part of the object's key information and cannot be modified";
    if (operationException.Message.Contains(checkMessage) == true)
    {
    string newExceptionMessage = "مقدار پروپرتیِ مربوط به شناسه ، باید برابر با همان مقدار قبلی و یا در واقع ، برابر با همان مقداری " +
    "که به عنوان پارامتر اول به این متد ارسال شد (برابر با پارامتر entityIdInDatabase) ، باشد و نمیتواند تغییر داده شود .";


    throw new InvalidOperationException(newExceptionMessage, operationException);
    }
    }
    }


    متد FindByLazyLoadingMode در همین کلاس Repository (در بدنه ی کد بالا که فراخونی شد) ، یک Id را میگیره و موجودیتِ مربوط به اون را بصورت Lazy Loading برمیگردونه .


    =============================



    حالا داستان اینه که میخوام شیِ PersonEntity ای که از قبل وجود داشت را ویرایش کنم .
    مثلا از قبل ، شیِ PersonEntity ای بصورت زیر ، در پایگاه داده ذخیره شده بود (کد موجودیت EF اش را مینویسم) :


    PersonEntity personEntity = new PersonEntity();
    personEntity.Id = 1;
    personEntity.FirstName = "احمد";
    personEntity.LastName = "متوسلیان";
    personEntity.MobileNumbers = new List<MobileNumberEntity>();
    personEntity.MobileNumbers.Add(new MobileNumberEntity(){Id = 1, MobileNumber = "0123", PersonEntityId = 1});
    personEntity.MobileNumbers.Add(new MobileNumberEntity() { Id = 2, MobileNumber = "0911", PersonEntityId = 1 });


    که برای شخصی بنام "احمد متوسلیان" با id ئه 1 ، دو تا شماره تلفن ثبت شد که حالا در ادامه قصد ویرایشِ اطلاعات این شخص را دارم (در اینجا ، تمرکزِ تغییر ، فقط روی اطلاعات موجودیتِ شماره تلفن ئه این شخص هست) .
    مثلا میخوام شماره تلفن دومی برای شخص "احمد متوسلیان" را از لیست بالا ، حذف کنم (که این کار ، در واقع ویرایش اطلاعات شخص "احمد متوسلیان" محسوب میشه) .

    میدونم که این کار را میتونم بجای این روش ، از روش حذف و اضافه و تغییر بصورت مستقیم درون خود موجودیتِ "MobileNumberEntity" انجام بدم و در این صورت ، این خطا که در ادامه میگم را نمیده و به درستی کار میکنه اما میخوام بدونم آیا این کار (حذف و اضافه و تغییر در MobileNumberEntity) ، توسط ویرایش اطلاعاتِ PersonEntity هم میتونه انجام بشه یا نه؟

    برای این کار (و این تغییر) هم کد زیر را نوشتم (فعلا حذف در MobileNumberEntity توسط ویرایش PersonEntity) :


    using (IRepositoryBase<PersonEntity> repository = new Repository<PersonEntity>(new PhoneBookDbContext()))
    {
    var entity = repository.FindByLazyLoadingMode<PersonEntity>(1);
    var mne = repository.FindByLazyLoadingMode<MobileNumberEntit y>(2);
    entity.MobileNumbers.Remove(mne);


    repository.Edit(entity.Id, entity);
    repository.SaveChanges();
    }


    متد FindByLazyLoadingMode را که توضیح دادم که یک متدی در Repository هست که یک موجودیت را بصورت Lazy Loading توسط شناسه ی اون موجودیت ای که در نوعِ جنریکِ اون متد (نه در نوع جنریکِ کلاس Repository) تعیین میکنیم ، پیدا میکنه و در متغییر entity ذخیره میکنه که در اینجا همون شنا سه ی 1 هست که شخص "احمد متوسلیان" هست.

    دومین خط که باز این متد فراخوانی شد اما این بار برای پیدا کردن موجودیتِ "شماره تلفن" ، شماره ی تلفن با مقدار "0911" را که دومین شماره تلفن برای شخص "احمد متوسلیان" هست را پیدا میکنه تا اون را حذف کنه .

    سومین خط هم این شماره تلفن پیدا شده را از لیست شماره تلفن های موجود برای اون فرد (entity.MobileNumbers) ، حذف میکنه .

    بعدش متد Edit (در کلاس Repository) برای اجرای این عملیات ویرایش فراخوانی میشه .

    آخرین خط هم که خطا پرتاب میکنه ، برای ذخیره کردن هست (در واقع متد SaveChange در کلاس DbContext ، خطا پرتاب میکنه) .

    ==============================


    خطا اش هم متن زیر هست :

    "
    system.invalidoperationexception: 'the operation failed: the relationship could not be changed because one or more of the foreign-key properties is non-nullable. when a change is made to a relationship, the related foreign-key property is set to a null value. if the foreign-key does not support null values, a new relationship must be defined, the foreign-key property must be assigned another non-null value, or the unrelated object must be deleted.'
    "

    میگه که ارتباط نمیتونه تغییر کنه چون کلید خارجی ، بصورت not null تعریف شد .
    کلید خارجیِ PersonEntityId (که در موجودیتِ MobileNumberEntity هست) ، همونطور که توضیح داده بودم ، بصورت not null تعریف شد ؛ اما عملکرد EF چجوری هه که اگه اشتباه متوجه نشده باشم ، انگار برای حذف یک موجودیتِ ای که بصورت Navigation Property با موجودیتِ دیگه رابطه داره و زمانی که موجودیت والدش که موجودیت شخص هست را میخواین ویرایش کنیم (منظورم حذف یک رکورد از موجودیت شماره تلفن هست) ، انگار نیاز به تغییر در کلیدِ خارجیِ اون موجودیت داره؟!
    دیگه حذف کردنِ یک رکورد ، انگار نباید نیاز به تغییر در چیزی و نیاز به تغییر در مقدار کلید خارجی داشته باشه دیگه !


    کلا دوستان به نظرتون در این روش ، مشکل از کجاست و میشه کاری کرد که کد بالا درست کار کنه (یعنی وقتی که بخوایم موجودیت شخص بالا را ویرایش کنیم ، میتونیم یک رکوردی از شماره تلفن اش را حذف یا اضافه یا ویرایش کنیم) ؟

    تشکر

  2. #2

    نقل قول: خطا موقع به روزرسانی و تغییر در Navigation Collection Property در Entity Framework 6.4

    PersonEntityId بصورت int تعریف شده، اگر میخواهید nullable باشه بایست به صورت int? تعریف بشه.

    entity.MobileNumbers.Remove(mne);

    خط بالا صرفا ارتباط اون شماره تلفن با اون شخص را از بین می برد یعنی PersonEntityId را برای mne نال میکند و اگر nullable نباشه خطای کد شما صادر میشه.
    این خط کد ، اون شماره تلفن رو از جدول شماره تلفن ها حذف نخواهد کرد.

  3. #3

    نقل قول: خطا موقع به روزرسانی و تغییر در Navigation Collection Property در Entity Framework 6.4

    سلام
    خیلی ممنون آقا محمد .
    بله درست میگید .

    وقتی یک موجودیت را از کالکشن حذف میکنیم ، در واقع ارتباط شون را حذف کردیم (ارتباط بین موجودیت های PersonEntity و MobileNumberEntity را حذف میکنه) . بعد از اون ، اگه میخوایم اون رکورد را از موجودیت و جدول طرف مقابل هم حذف کنیم ، باید بصورت مجزا ، اون رکورد را از اون جدول حذف کنیم .

    اون ارور هم فقط برای زمان حذف کردن اتفاق میافته (یعنی برای زمان ویرایش یا اضافه کردنِ موجودیتِ MobileNumberEntity ، توسط ویرایش کردنِ موجودیت والدش یعنی توسط ویرایش کردن موجودیتِ PersonEntity ، این اتفاق و خطا پیش نمیاد) . که برای رفع این خطا موقع حذف کردن ، علاوه بر حذف از کالکشن ، اون رکورد را از اون موجودیت و جدول هم باید حذف و بعدش ذخیره کنیم که این مشکل حل میشه .

    . علاوه بر اون ، بعد از فراخوانی متد SaveChanges ، میتونیم شناسه هایی که به عنوان هر موجودیت اضافه کردیم را از طریق همان شی ، بدست بیاریم .

    کد زیر ، ویرایش یک شی و یک رکورد از PersonEntity هست که هم ویرایش مقادیر پروپرتی scalar و هم حذف و اضافه و ویرایش مقادیر و موجودیت های مربوط به Navigation Property ئه مربوط به اون شخص هست .

    تشکر ازتون .


    using (IRepositoryBase<PersonEntity> repository = new Repository<PersonEntity>(new PhoneBookDbContext()))
    {
    await repository.InitializeDatabaseAsync();


    PersonEntity mainEntity = repository.FindByLazyLoadingMode<PersonEntity>(1);
    // ویرایش پروپرتی های scalar برای موجودیت PersonEntity
    mainEntity.FirstName = "سردار مقاومت احمد";
    mainEntity.LastName = "متوسلیان";


    mainEntity.MobileNumbers = mainEntity.MobileNumbers ?? new List<MobileNumberEntity>();
    // ویرایش ، شامل حذف و اضافه و ویرایش موجودیت MobileNumberEntity
    MobileNumberEntity addMne = new MobileNumberEntity() {MobileNumber = "0112233"};
    mainEntity.MobileNumbers.Add(addMne);


    MobileNumberEntity deleteMne = mainEntity.MobileNumbers.FirstOrDefault(mobileNumb erEntity => mobileNumberEntity.Id == 7);
    mainEntity.MobileNumbers.Remove(deleteMne);
    repository.Remove<MobileNumberEntity>(deleteMne);


    mainEntity.MobileNumbers.FirstOrDefault(mobileNumb erEntity => mobileNumberEntity.Id == 6).MobileNumber = "987654";


    /////////////////////////////
    // ویرایش ، شامل حذف و اضافه و ویرایش موجودیت AddressEntity
    mainEntity.Addresses = mainEntity.Addresses ?? new List<AddressEntity>();


    AddressEntity addAe = new AddressEntity {Province = "تهران", City = "شهر تهران"};
    mainEntity.Addresses.Add(addAe);


    AddressEntity deleteAe = mainEntity.Addresses.FirstOrDefault(addressEntity => addressEntity.Id == 3);
    mainEntity.Addresses.Remove(deleteAe);
    if (deleteAe.Persons.Count == 1)
    repository.Remove<AddressEntity>(deleteAe);


    var editAe = mainEntity.Addresses?.FirstOrDefault(addressEntity => addressEntity.Id == 1);
    editAe.Province = "فلسطین";
    editAe.City = "شهر غزه";


    // انجام ویرایش و ذخیره
    repository.Edit(mainEntity.Id, mainEntity);
    repository.SaveChanges();


    // بازیابی شناسه های مربوط به موجودیت های اضافه شده
    int addAeId = addAe.Id;
    int addMneId = addMne.Id;
    }

تاپیک های مشابه

  1. حرفه ای: پیکربندی Property Mapping ها با استفاده از Fluent API در Entity Framework Code First
    نوشته شده توسط all_time_programmer در بخش C#‎‎
    پاسخ: 0
    آخرین پست: پنج شنبه 23 دی 1400, 15:41 عصر
  2. پاسخ: 4
    آخرین پست: شنبه 24 مهر 1395, 12:12 عصر
  3. تفاوت entity framework ، linq to entity و Entity Framework Code First
    نوشته شده توسط negar.rafie در بخش دسترسی به داده ها (ADO.Net و LINQ و ...)
    پاسخ: 1
    آخرین پست: دوشنبه 24 آذر 1393, 10:43 صبح
  4. سوال: فیلتر کردن Navigation Property در Entity
    نوشته شده توسط مهدیعای در بخش C#‎‎
    پاسخ: 0
    آخرین پست: دوشنبه 04 آذر 1392, 16:53 عصر
  5. آموزش گزارش گیری با entity framework
    نوشته شده توسط firoozi90 در بخش دسترسی به داده ها (ADO.Net و LINQ و ...)
    پاسخ: 1
    آخرین پست: جمعه 17 شهریور 1391, 12:08 عصر

قوانین ایجاد تاپیک در تالار

  • شما نمی توانید تاپیک جدید ایجاد کنید
  • شما نمی توانید به تاپیک ها پاسخ دهید
  • شما نمی توانید ضمیمه ارسال کنید
  • شما نمی توانید پاسخ هایتان را ویرایش کنید
  •