Beragadt tartalom az objectcontextben

Beragadt tartalom az objectcontextben
2015-03-18T23:55:00+01:00
2015-03-25T12:34:09+01:00
2022-10-15T21:50:59+02:00
iolah2
Sziasztok 

EF 5 ObjectContext Generator + EDM segítségével használok (MS SQL) adatbázist. 
Az alábbiban kérnék segítséget:
Van egy Asztalok táblám, List<Asztalok> segítségével tárolom az éppen kiválasztottakat. ! WPF-ben dolgozom.
Először kiválasztom a Bérlőt, majd kiválasztok pár "asztal"-t, melyre ellenőrizgetek.
Ami megy: 1) Ha teljesülnek a feltételek, ctx.savechanges()-el elmentem (a bérlő számot beálítotam a listában), így a Listából az adatbázisba kerül ami kell.
A gondom: 2) Ha nem teljesülnek a feltételek, a listát kinullozom, és a bérlőt is. Ezután újat írok be, a Bérlőt az első, az Asztal listát a második ablakban.
Ilyenkor azt tapasztalom hogy a ctx.savechanges()-el ha elmentem BEÍRJA A KORÁBBAN ROSSZnak itélteket is. Pedig azokat clear-el tőröltem a Listámból, így nem értem honnan veszi.

Utána jártam: a ctx-ben valami add-os rejtett info-nál látható, hogy tényleg várakozik 10-11 rögzítésre. De én töröltem a List<asztal> asztalkak tartalmát, ez akkor miért nem tünik el automatikusan???



Erre szeretnék valami ötletet, mert eddig ilyet még nem láttam.
A .savechanges()-nek nincs valami .DropAllChanges() párja
Mutasd a teljes hozzászólást!
Akkor szépen sorban:

1. Többfelhasználós rendszer

Sajnos nincs tapasztalatom, hogy az EF hogy viselkedik hosszú életű context és több párhuzamos felhasználó esetén. Lehet, hogy működni fog, de én nem merném így használni. Nem ajánlott.

Több felhasználó kezelése egyébként ezen túlmenően is alapos átgondolást igényel, pl. kezelni kell, ha több felhasználó ugyanazt az adatot egyszerre módosítja. Alapesetben az EF optimista konkurenciakezelést használ, azaz ha többen írnak egy rekordot, az utoljára írt adat marad meg.

2. Null-ozás

Eddig termelesLista.clear(), illetve termelesLista = new List<termelés>()-el probálkoztam, de a ctx-ben bennemaradt a termelés.

Normális esetben úgy kéne használni, hogy kiolvasod az adatbázisból a listádat, és onnan kezdve a rekordokat külön példányban a memóriában tárolod, módosítod, aztán amikor a felhasználó ment, akkor új adatbázis context-ben beírod a változásokat. (Amennyiben két felhasználónak lehetősége van egy időben megtennie ezt, akkor a kettő között valamilyen lock-mező használatával jelezned kell a másik felhasználó felé, hogy ezt ő most nem tudja módosítani.)

Valahogy így:

- <context>adatok olvasása</context>
- adatok memóriában, hozzáadás, módosítás, törlés
- <context>adatok írása</context>

Tehát továbbra is ugyanoda lyukadunk ki: le kéne választanod az adatelérési réteget az UI-tól.

Próbáltam keresni egy komplett példát, de attól félek, ez így egyszerre túl sok lesz (a készítője leegyszerűsített mindent, amennyire lehet). Nézd meg ezt a linket, de rögtön jelezném, hogy a cikk írója az adatelérési rétegnél (Services/Services.cs) ugyancsak hosszan élő context-et használ, én ezt az alábbiak alapján módosítottam röviden élőre:

using System.Collections.ObjectModel; using System.Linq; using WPF_MVVMLight_CRUD.Model; namespace WPF_MVVMLight_CRUD.Services { /// <summary> /// The Interface defining methods for Create Employee and Read All Employees /// </summary> public interface IDataAccessService { ObservableCollection<EmployeeInfo> GetEmployees(); int CreateEmployee(EmployeeInfo Emp); } /// <summary> /// Class implementing IDataAccessService interface and implementing /// its methods by making call to the Entities using CompanyEntities object /// </summary> public class DataAccessService : IDataAccessService { public DataAccessService() { } public ObservableCollection<EmployeeInfo> GetEmployees() { using (CompanyEntities context = new CompanyEntities()) { return new ObservableCollection<EmployeeInfo>(context.EmployeeInfoes); } } public int CreateEmployee(EmployeeInfo Emp) { using (CompanyEntities context = new CompanyEntities()) { context.EmployeeInfoes.Add(Emp); context.SaveChanges(); return Emp.EmpNo; } } } }
Itt láthatod, ahogy az adatolvasás/írás az UI-tól teljesen elkülönítve, röviden élő context-ben, tranzakcióban történik. Az adatok adatkötéssel kerülnek az UI-ra: minden szépen el van különítve. Valahogy így kéne kinéznie egy modern WPF/EF alkalmazásnak.

Ajánlom a figyelmedbe a Microsoft ingyenes előadásait!
Mutasd a teljes hozzászólást!

  • Nem lehet, hogy a Detach() vagy DeleteObject() metódust kell meghívnod az összes érintett objektumra?
    Mutasd a teljes hozzászólást!
  • Helló!

    A .savechanges()-nek nincs valami .DropAllChanges() párja

    De van, a Dispose() metódus. De szerintem itt valami elvi problémád lehet.

    A context-et minél rövidebb időre, csak az adatbázis művelet idejére célszerű megnyitni, mivel az egyben tranzakciókat is kezel.

    Nem írtad, hogy pontosan hogyan használod, de nem úgy kéne, hogy megnyitod az ablakod létrehozásakor, és akkor ott szépen éldegél, amíg be nem zárod, időnként pedig mentegetsz rá. A context-nek az a lényege, hogy tükrözi az adatbázis adatainak állapotát, tehát nem "beragad", hanem teszi a dolgát.

    Helyesen úgy kéne használni, hogy amikor a felhasználó megnyomja a mentést, akkor hozod létre egy using blokkban, bevésed a változásokat és rögtön le is zárod. (Ha nincs lehetőséged using-ot használni, nem baj; úgy lett tervezve, hogy anélkül is rendben működjön, de ez az optimális.)

    Az egy másik, programtervezési kérdés, hogy ezt hogyan valósítod meg, de ez az alapelv.
    Mutasd a teljes hozzászólást!
  • Kapcsolódva Kukipapa írásához, egy kis kiegészítés.

    Az EF igazából a detached modellt szereti. (illetve valójában nem az EF, hanem az SQL szerverek )
    Vagyis az EF objektumokat egy olvasásban létrehozod, innen memóriában levő adatokról beszélünk, nincs tovább kapcsolat az adatbázisoddal. [emiatt a legjobb, ha az olvasás egy using blokkban történik]
    A memória objektumaidat kezeled (olvasod, írod) aztán, amikor eljön a pillanat, akkor egy új contexhez felcsatolod és a változást átvezetteted az adatbázison.

    A két művelet között pár pillanat vagy órák is eltelhetnek.
    Mutasd a teljes hozzászólást!
  • Szia
    Az elején a context: AcMasinaEntities ctx = App.ctx;, ezt using helyett mindenhol használom. Így List<Termelés>-be teszek adatokat, melyek később beragadnak, ha a rögzítés helyett kilépek(és másikhoz belépek).
    Most akkor minden ctx használathoz using kellene? S akkoe is kell dispose?

    Ez a List<Termelés> feltöltése ha műveletsor van pl.: 1010, 1020,1030 ... 1070 művszám

    private void ListazMunkReszl(string munkalapAkt, short? kezdMuvSzam, short befMuvSzamM) { var muvSorMunkReszl = ctx.Munkalapok_részletek.Where(t => t.Munkalap == munkalapAkt && t.Műveletszám > kezdMuvSzam && t.Műveletszám <= befMuvSzamM);//Select() foreach (var item in muvSorMunkReszl) { var muveletUj = new Termelés() { Dolgozó_szám = termelesAkt.Dolgozó_szám, Darabszám = termelesAkt.Darabszám,//Ehhez kell a végén kikérni, lehet, hogy emiatt a végére kerül ez a rész Selejt = termelesAkt.Selejt,//Ehhez kell a végén kikérni Munkalap = munkalapAkt, Műveletszám = item.Műveletszám, Technológia_AZ = (int)item.Technológia_AZ, Technológiai_műveletek= ctx.Technológiai_műveletek.Single(t=>t.Technológia_AZ==(int)item.Technológia_AZ), Alkatrész_AZ = (int)item.Munkalapok.Alkatrész_AZ, Mltetel_AZ = item.Mltetel_AZ, SelejtAnyag = false, Selejt_oka = 0, Technológus = 0, BevitelDatuma = DateTime.Parse(DateTime.Now.ToString("yyyy.MM.dd. HH:mm:ss")), Onellenorzes = (bool)onellenorzott.IsChecked }; termelesAktList.Add(muveletUj); var muveletAkt = munkalapAkt + "/" + item.Műveletszám.ToString(); IdeigListFrissit(item.Mltetel_AZ, muveletAkt); nincsListazvaMuvSor = false; } }

    Részlet a WPF-emből

    public partial class VonalkodUc2 : UserControl { AcMasinaEntities ctx = App.ctx; Termelés termelesAkt; Termelesresz termelesForrasTorol; List<Termelés> termelesAktList; List<NormaEgyreStored> normaEgyreStoreList; /// <summary> /// Ezekben eltárolható a műveletsor esetén is /// </summary> List<Termelés> termMuvFelbehagyvaList;// Munkalap/Műv-hez tartozó félbehagyottak(bármely dolgozó által) List<Termelesresz> termMuvMegkezdveList; int darab; //float? muvTervOsszIdo; //float? normaido; short befMuvSzamAkt; bool vanMuvSor; bool nincsListazvaMuvSor; public static VonalkodUc2 vonUC2; // = new VonalkodUc2(); --- Ez az értékadás felesleges? /// <summary> /// VonalkodUc2 konstruktor /// </summary> public VonalkodUc2() { InitializeComponent(); //WPF .xaml betöltés vanMuvSor = false; nincsListazvaMuvSor = false; befMuvSzamAkt = 0; termelesAktList = new List<Termelés>(); termMuvFelbehagyvaList = new List<Termelés>(); termMuvMegkezdveList = new List<Termelesresz>(); vonUC2 = this; //UC tárolása, mely a 3 tabcontrol miatt fontos(3 fül) } /// <summary> /// Kiszámítja( és szétosztja) az időt a Termelés(ek)-nél, /// Félbehagyás esetén Részműveletet is rögzít /// Frissítve 2015 03 17 /// Ellenőrízd: Leginkább lista esetre, de érdemes egy eleműre is megnézni /// </summary> /// <param name="befejeze">befejezés click -> true, felbehagy click -> false</param> /// <returns></returns> private bool AdatBejegyzesTerm(bool befejeze) //!!!Ellenörízni!!! { //milli, mikrosec eldobása DateTime vege = DateTime.Parse(DateTime.Now.ToString("yyyy.MM.dd. HH:mm:ss")); //A kezdésnél is ki van véve a milliseconds( VonUc1 xaml cs 155 ln) #region munkavégzés idejének meghatározása másodpercben TimeSpan muveletidoSpan = new TimeSpan(0, 0, 0, 0); int muvIdSec = 0; //Ezen ellenőrzés azért kell, mert "lehet" null értékű a Kezdő dátum DateTime? kezdo = termelesAkt.Kezdő_dátum; if (kezdo is DateTime) { string messageStr = ""; int szunetSec = 0; if (kezdo > vege) { App.Uzenet("Kezdés ideje korábbi kell legyen a vége idejénél!", false); return false; } SzunetSzamol szunetSzamol = new SzunetSzamol(); szunetSec = szunetSzamol.MunkaidoSzunetekIdejeSec((DateTime)kezdo, (DateTime)vege, out messageStr); if (messageStr != "") { App.Uzenet(messageStr, false); return false; } var eltelt = vege - kezdo; TimeSpan.TryParse(eltelt.ToString(), out muveletidoSpan); muvIdSec = (int)muveletidoSpan.TotalSeconds - szunetSec; } else { App.Uzenet("Kérem javítsa a kezdés dátumát!", false); return false; } #endregion tervOsszIdoEgyre = 0f; if (!vanMuvSor) { termelesAkt.Vége_dátum = vege; termelesAkt.Műveletidő = (float)muvIdSec / 60; var munkReszlMostani = ctx.Munkalapok_részletek.Single(t => t.Mltetel_AZ == termelesAkt.Mltetel_AZ); tervOsszIdoEgyre = NormaSzamitas(munkReszlMostani); //darab = Convert.ToInt32(ctx.Munkalapok.Single(t => t.Munkalap == termelesAkt.Munkalap).Darabszám); //var muvTervOsszIdo = ctx.Munkalapok_részletek.Single(t => t.Mltetel_AZ == termelesAkt.Mltetel_AZ).Össz_idő; //tervOsszIdoEgyre = (float)muvTervOsszIdo / darab; if (!befejeze) { ReszMuveletSzam(termelesAkt, tervOsszIdoEgyre, 100); } } #region Muveletsor else //különben szét kell osztani { #region teljes munkaidő szétosztása (másodpercben) foreach (var item in termelesAktList) { var mLapokReszlAkt = ctx.Munkalapok_részletek.Single(t => (t.Munkalap == item.Munkalap && t.Műveletszám == item.Műveletszám)); float normaAkt = NormaSzamitas(mLapokReszlAkt); //Eltárolom a Műveletszám-norma párosítást, hogy ne kelljen a normát újraszámolni normaEgyreStoreList.Add(new NormaEgyreStored((short)item.Műveletszám, normaAkt)); //Mivel ugyanannyi darabot kezelünk az egész soron, elég a művelet egyre //Ha a darabszám eltért volna: tervOsszIdo = (float)(item.Darabszám * normaAkt); tervOsszIdoEgyre += normaAkt; } int munkaIdKerSecOssz = 0; List<float> normaIdoList = new List<float>(); List<int> munkaIdSecL = new List<int>(); foreach (var item in termelesAktList) { float munkaIdSz; if (tervOsszIdoEgyre == 0) { munkaIdSz = (float)muvIdSec / termelesAktList.Count; normaIdoList.Add(0); } else { var mLapokReszlAkt = ctx.Munkalapok_részletek.Single(t => (t.Munkalap == item.Munkalap && t.Műveletszám == item.Műveletszám)); //float normaAkt = NormaSzamitas(mLapokReszlAkt); //Ha egyezik, utóbbit választom float normaAkt = normaEgyreStoreList.First(t => t.MuvSzam == item.Műveletszám).NormaErtek; munkaIdSz = muvIdSec / tervOsszIdoEgyre * normaAkt; //Ha figyelembe vennénk a darabszámot: munkaIdSz = muvIdSec / tervOsszIdo * ((int)item.Darabszám * normaAkt); normaIdoList.Add(normaAkt); } munkaIdKerSecOssz += (int)munkaIdSz; munkaIdSecL.Add((int)munkaIdSz); } if (muvIdSec > munkaIdKerSecOssz) { var elt = muvIdSec - munkaIdKerSecOssz; int d0 = 0; do { munkaIdSecL[d0++]++; // csak ha esetleg lenne túlcsordulás if (d0 == munkaIdSecL.Count) { d0 = 0; } } while ((--elt) > 0); } //float aktOsszIdoUj = 0;//Csak ellenörzés DateTime kezdes = (DateTime)kezdo; SzunetSzamol szunetSzamol = new SzunetSzamol(); #endregion #region Kezdő Vége dátum + félbehagyás esetén részművelet számítás for (int i = 0; i < termelesAktList.Count; i++) { termelesAktList[i].Kezdő_dátum = kezdes; var munkaIdSec = munkaIdSecL[i]; var munkaIdMin = (float)munkaIdSec / 60; termelesAktList[i].Műveletidő = munkaIdMin; //int elteres = 0; vege = szunetSzamol.VegeIdoSzamit(ref kezdes, munkaIdSec);//, elteres); termelesAktList[i].Vége_dátum = vege; if (!befejeze) { var termMost = termelesAktList[i]; ReszMuveletSzam(termMost, normaIdoList[i], 100f); } } #endregion } #endregion return true; } /// <summary> /// Felbehagyás gomb /// Félbehagyandó műveletek rögzítése a Termelésbe /// Elején átvizsgálom a szemantika-t Darab, Selejt viszony, /// majd leellenörzöm a sikeres időelosztást. /// Ha mindkettő OK -> félbehagyás elvégzése /// </summary> private void Felbehagy_Click(object sender, RoutedEventArgs e) { #region Ellenőríz bool joDbSelejtE = DarabSelejtSzamEll(); if (!joDbSelejtE) { termelesAkt.Darabszám = darab; termelesAkt.Selejt = 0; ElkeszultFocus(e); return; } // (Részművelet, Kezdő, Vége, Műveletidő... rögzítése) if (vanMuvSor && nincsListazvaMuvSor) { ListazMunkReszl(termelesAkt.Munkalap, termelesAkt.Műveletszám, befMuvSzamAkt); } //joE azt adja vissza hogy sikeres-e az időszétosztás bool joE = AdatBejegyzesTerm(false); if (!joE) { FoAblak.KozosDataClear(); AblakBezar(); return; } #endregion //Meglegyzés: ezt az if-else ágat szemléltetésként, jobb átláthatóság céljából nem tettem be az AdatBejegyzesTerm-be if (vanMuvSor) { for (int i = 0; i < termelesAktList.Count; i++) { termelesAktList[i].Felbehagyva = true; //ctx.Termelés.AddObject(termelesAktList[i]); } } else { termelesAkt.Felbehagyva = true; //ctx.Termelés.AddObject(termelesAkt); } //Ha sort teszek be akkor is csak ezt kell törölni ctx.Termelesreszs.DeleteObject(termelesForrasTorol); ctx.SaveChanges(); App.befejezve = false; FoAblak.KozosDataClear(); AblakBezar(); } /// <summary> /// Befejezés gomb /// Befejezendő műveletek rögzítése a Termelésbe /// </summary> private void Befejez_Click(object sender, RoutedEventArgs e) { #region hibakiszűrés és kérdések bool joDbSelejtE = DarabSelejtSzamEll(); if (!joDbSelejtE) { termelesAkt.Darabszám = darab; termelesAkt.Selejt = 0; ElkeszultFocus(e); return; } if (onellenorzott.IsChecked == false) { App.Uzenet("Befejezni csak önellenőrzött műveletet lehet!", false); return; } if (nincsListazvaMuvSor && vanMuvSor) { ListazMunkReszl(termelesAkt.Munkalap, termelesAkt.Műveletszám, befMuvSzamAkt); } //Bármely műveletszám esetén ha szerepel a termelésresz-ben if (termMuvMegkezdveList.Count != 1) { App.Uzenet("A műveleten mások is dolgoznak! Kérem válassza a félbehagy lehetőséget.", false); return; } //Bármely műveletszám esetén ha szerepel a termelés-ben félbehagyva else if (termMuvFelbehagyvaList.Count != 0) { App.Uzenet("A műveletet valaki már félbehagyta. \n Biztos befejezi? (Nem esetén Félbehagyás) ", true); if (App.igenKat == false) { Felbehagy_Click(sender, e); App.igenKat = false; return; } } //Termelések adatainak rögzítése, kivéve a Részművelet, melyet alább számítunk bool joE = AdatBejegyzesTerm(true); if (!joE) { FoAblak.KozosDataClear(); AblakBezar(); //Megse2_Click(sender, e); ////FoAblak.KozosDataClear(); return; } #endregion #region Beviszem a most bejegyzetteket is a termMuvFelbehagyvaList listába, és azzal számolok utána //Hozzáadott műveletsor/művelet rögzítése if (vanMuvSor) { for (int i = 0; i < termelesAktList.Count; i++) { termelesAktList[i].Felbehagyva = true; //Beveszem a félbehagyottak közé termMuvFelbehagyvaList.Add(termelesAktList[i]); } } else { termelesAkt.Felbehagyva = true; termelesAktList.Add(termelesAkt); //Beveszem a félbehagyottak közé termMuvFelbehagyvaList.Add(termelesAkt); } #endregion #region teljes műveleti idő és max darabszám meghatározása //ITT új módszer kell. //1.) összegezzük a listában szereplő produktív technológiával rendelkezők műveleti idejét //(félbehagyottak + most befejezendők) //2.) az így kapott teljesítményt maxDarab * (tervOsszIdoEgyre / teljMuvIdo) * 100 visszaszámoljuk az egyéni idők szerint //Persze ha van Improduktív, akkor itt is csak a Produktív esetben var teljMuvIdo = 0f; var maxDarab = 0; //Itt már az összes benne van a foreach-ben a művelet/műveletsor is //Megvizsgálom hogy a munkalapnak vannak-e nem produktív műveletei List<short?> improdMuvszamokAkt = termelesAktList .Where( t => (t.Munkalap == termelesAkt.Munkalap) && (t.Technológiai_műveletek.Produktív != 1)) .Select(t=>t.Műveletszám).ToList(); if (improdMuvszamokAkt.Count()>0) { tervOsszIdoEgyre=0f; foreach (var item in normaEgyreStoreList) { if (!improdMuvszamokAkt.Contains(item.MuvSzam)) { tervOsszIdoEgyre += item.NormaErtek; } } } //Ha zavaró ez megprobálkozhatunk //float szum = (float)termMuvFelbehagyvaList.Sum(t => t.Műveletidő); //int maxDb = termMuvFelbehagyvaList.Max(t => (int)t.Darabszám); foreach (var item in termMuvFelbehagyvaList .Where(t=>!improdMuvszamokAkt.Contains(t.Műveletszám))) { teljMuvIdo += (float)item.Műveletidő; if ((int)item.Darabszám > maxDarab) { maxDarab = (int)item.Darabszám; } } var teljesitmenyMindre = maxDarab * (tervOsszIdoEgyre / teljMuvIdo) * 100; float normaAktual = 0f; if (!vanMuvSor) { normaAktual = NormaSzamitas(termelesAkt.Munkalapok_részletek); } for (int i = 0; i < termMuvFelbehagyvaList.Count; i++) { termMuvFelbehagyvaList[i].Felbehagyva = false; if (vanMuvSor) { normaAktual = normaEgyreStoreList.First(t => t.MuvSzam == termMuvFelbehagyvaList[i].Műveletszám).NormaErtek; } ReszMuveletSzam(termMuvFelbehagyvaList[i], normaAktual, teljesitmenyMindre); } #endregion ctx.Termelesreszs.DeleteObject(termelesForrasTorol); ctx.SaveChanges(); App.befejezve = true; FoAblak.KozosDataClear(); AblakBezar(); }
    }
    Mutasd a teljes hozzászólást!
  • Helló!

    Amit a using-nál írtál azt nem igen értettem.
    Én úgyanis azt csinálom, hogy beírok a WPF xaml-jébe dolgozószámot, majd arra végrehajtók ellenőrzést, hogy ez a dolgozó létezik-e az sql-ben

    private void DolgozomSzam_PreviewLostKeyboardFocus_1(object sender, KeyboardFocusChangedEventArgs e) { if (!(e.NewFocus is TextBox) || (e.NewFocus as TextBox).Name != "dolgozomSzam") { dolgozoM.Text = ""; if (dolgozomSzam.Text != "") { try { valMunkasAz = Convert.ToInt16(dolgozomSzam.Text); var dolgozoAkt = ctx.Dolgozók.Single(t => t.Dolgozószám == valMunkasAz); FoAblak.dolgozoSzam = valMunkasAz; FoAblak.dolgozoNev = dolgozoAkt.Név; dolgozoM.Text = dolgozoAkt.Név; if (vonalkodFul) { muvMarMegkezd = ctx.Termelesreszs.SingleOrDefault(t => t.Dolgozó_szám == valMunkasAz); } } catch { App.Uzenet("Adja meg létező dolgozó számát!", false); dolgozomSzam.Text = ""; KeepTextBoxFocus(sender, e); } } else { if (e.NewFocus is TabItem) { return; } App.Uzenet("Adja meg a dolgozó számát!", false); //String.Format("Adja meg a dolgozó számát!"+"\n{0}", (e.NewFocus is TabItem).ToString()), false); KeepTextBoxFocus(sender, e); } } }
    Most itt is Using-ot kellene valahogy használni a 
    ctx= App.ctx helyett? Mely globálisan adott az egész xaml usercontrolra?

    Hasonlóan a többihez is?

    Tudsz küldeni valami példát?
    Köszönöm
    Mutasd a teljes hozzászólást!
  • Huh, nem kérsz egyszerűt, mert - minden tisztelettel, de - elég sok alapvető problémát látok a beidézett kódok alapján, mind nyelvhasználati szinten, mind tervezési szinten.

    Példa nyelvhasználati problémára:

    //milli, mikrosec eldobása DateTime vege = DateTime.Parse(DateTime.Now.ToString("yyyy.MM.dd. HH:mm:ss"));
    Vagy a kérdésed, hogy nem érted a using-ot. Erre nem tudok mit mondani, meg kell tanulni, legalábbis ha stabil és karbantartható program írása a cél.

    A tervezésről: normális esetben teljesen szét kéne szedni a programodat különböző részekre (UI, BL, adatelérés), ez teljesen hiányzik, így eleve nem egyszerű megoldást javasolni, de akkor próbáljuk így.

    Az EF élettartam kérdését is kicsit árnyalni kell: igazából kis adatbázisnál (ahol nincs meghatározva, hogy pontosan ez mit jelent) működhet a módszered, miszerint a context-et az egész usercontrol alatt életben tartod, tehát, ha ez a feltétel fennáll, akkor nem szükséges a programodat áttervezni (esetleg később, amikor majd kiderül, hogy százával kéne, hogy használják a programodat).

    Úgyhogy ha helyi, nem többfelhasználós programon dolgozol, akkor megpróbálhatunk valami quick&dirty fix-et a konkrét kérdésedre, de hangsúlyozom, hogy nem így kéne WPF/EF-et használni.

    Próbáltam kiigazodni a kódodon, de nem nagyon látom a konkrét részt, ahol "kinull-ozod a listát és a bérlőt". Ezt közvetlenül az adatbázis objektumon próbálod, ugye?
    Mutasd a teljes hozzászólást!
  • Szia
    Köszi amit írtál.

    Úgy terveztem hogy külön gépeken fut a program és egy másik gépen lévő sql-el kommunikál mindegyik(mind az 5-6 ugyanazzal az eggyel).
    Ez mennyire befolyásolja hogy lehet-e állandó a kapcsolat?

    Próbáltam kiigazodni a kódodon, de nem nagyon látom a konkrét részt, ahol "kinull-ozod a listát és a bérlőt". Ezt közvetlenül az adatbázis objektumon próbálod, ugye?

    Egyik kérdésre a válaszom: 
    Eddig termelesLista.clear(), illetve termelesLista = new List<termelés>()-el probálkoztam, de a ctx-ben bennemaradt a termelés.</termelés>

     
    Én az App class-áan rögzítettem static-usan a kapcsolatot, lehet hogy ez gondot okoz, esetleg példányosítani kellene.
    Utána mind az első, mind a második Usercontrolnak ctx=App.ctx úton adom át az értéket.

    public partial class App : Application { public static AcMasinaEntities ctx; ... /// /// Indításkor fut le /// /// felületkezelési nyelv és adatbáziskapcsolat beállítása!!! protected override void OnStartup(StartupEventArgs e) { ... ... var connStr = ConnectionStrBuilder.ConnectionStrBuilderProc(adatok); //Kapcsolati string megírása(külön .cs-el) ctx = new AcMasinaEntities(connStr); try { //set context timeout?-kell-e kapcsolódási időhatár ctx.Connection.Open(); base.OnStartup(e); } catch { Uzenet("Nincs adatbázis kapcsolat!", false); Application.Current.Shutdown(); } }

    Sajnos csak annyit tudtam, hogy:

    1. illetve 2. usercontrolban hozzáadok a List<Termelés>-hez elemeket, itt gondoltam az  adatok szépen "csücsülnek a termelesLista" listámban, de ctx-en keresztül adatbázishoz csak az AddObject-en keresztül kivánom tenni. Így lepett meg azzal, hogy bár nem kértem, a ctx-el már ekkor összekapcsolta, gondolom a new Termelés miatt. 



    Most akkor ilyen konkrét esetben mit kellene tenni?

    1) Valahogy törölni a ctx-ből, ha nem akarom elmenteni, de újat akarok írni, bár azt nem tudom hogyan

    2) new Termelés, helyett valami TermIdeigl osztályba tegyem, és majd onnan adogattom? Max 10-15 Termelés lehet egy listában.
    3) valahogy a ctx-re kellene valami new? vagy hasonló nullázó frissítés? 

    muveletKezdo = new Termelés() { Dolgozó_szám = adatNyer.Dolgozó_szám, Darabszám = 0, Selejt = 0, Munkalap = valMunkalap, Műveletszám = aktMuvSzam,//valMuveletSzam, Kezdő_dátum = adatNyer.Kezdő_dátum, Technológia_AZ = (int)mlapReszlFirst.Technológia_AZ, Technológiai_műveletek= ctx.Technológiai_műveletek.Single(t=>t.Technológia_AZ == mlapReszlFirst.Technológia_AZ), Alkatrész_AZ = (int)mlapReszlFirst.Munkalapok.Alkatrész_AZ, Mltetel_AZ = mlapReszlFirst.Mltetel_AZ, SelejtAnyag = false, Selejt_oka = 0, Technológus = 0, BevitelDatuma = DateTime.Parse(DateTime.Now.ToString("yyyy.MM.dd. HH:mm:ss")) }; return adatNyer.Tétel_AZ;// tetelAZ = adatNyer.Tétel_AZ; termelesLista.Add(muveletKezdo)
    Köszi 
    István
    Mutasd a teljes hozzászólást!
  • Akkor szépen sorban:

    1. Többfelhasználós rendszer

    Sajnos nincs tapasztalatom, hogy az EF hogy viselkedik hosszú életű context és több párhuzamos felhasználó esetén. Lehet, hogy működni fog, de én nem merném így használni. Nem ajánlott.

    Több felhasználó kezelése egyébként ezen túlmenően is alapos átgondolást igényel, pl. kezelni kell, ha több felhasználó ugyanazt az adatot egyszerre módosítja. Alapesetben az EF optimista konkurenciakezelést használ, azaz ha többen írnak egy rekordot, az utoljára írt adat marad meg.

    2. Null-ozás

    Eddig termelesLista.clear(), illetve termelesLista = new List<termelés>()-el probálkoztam, de a ctx-ben bennemaradt a termelés.

    Normális esetben úgy kéne használni, hogy kiolvasod az adatbázisból a listádat, és onnan kezdve a rekordokat külön példányban a memóriában tárolod, módosítod, aztán amikor a felhasználó ment, akkor új adatbázis context-ben beírod a változásokat. (Amennyiben két felhasználónak lehetősége van egy időben megtennie ezt, akkor a kettő között valamilyen lock-mező használatával jelezned kell a másik felhasználó felé, hogy ezt ő most nem tudja módosítani.)

    Valahogy így:

    - <context>adatok olvasása</context>
    - adatok memóriában, hozzáadás, módosítás, törlés
    - <context>adatok írása</context>

    Tehát továbbra is ugyanoda lyukadunk ki: le kéne választanod az adatelérési réteget az UI-tól.

    Próbáltam keresni egy komplett példát, de attól félek, ez így egyszerre túl sok lesz (a készítője leegyszerűsített mindent, amennyire lehet). Nézd meg ezt a linket, de rögtön jelezném, hogy a cikk írója az adatelérési rétegnél (Services/Services.cs) ugyancsak hosszan élő context-et használ, én ezt az alábbiak alapján módosítottam röviden élőre:

    using System.Collections.ObjectModel; using System.Linq; using WPF_MVVMLight_CRUD.Model; namespace WPF_MVVMLight_CRUD.Services { /// <summary> /// The Interface defining methods for Create Employee and Read All Employees /// </summary> public interface IDataAccessService { ObservableCollection<EmployeeInfo> GetEmployees(); int CreateEmployee(EmployeeInfo Emp); } /// <summary> /// Class implementing IDataAccessService interface and implementing /// its methods by making call to the Entities using CompanyEntities object /// </summary> public class DataAccessService : IDataAccessService { public DataAccessService() { } public ObservableCollection<EmployeeInfo> GetEmployees() { using (CompanyEntities context = new CompanyEntities()) { return new ObservableCollection<EmployeeInfo>(context.EmployeeInfoes); } } public int CreateEmployee(EmployeeInfo Emp) { using (CompanyEntities context = new CompanyEntities()) { context.EmployeeInfoes.Add(Emp); context.SaveChanges(); return Emp.EmpNo; } } } }
    Itt láthatod, ahogy az adatolvasás/írás az UI-tól teljesen elkülönítve, röviden élő context-ben, tranzakcióban történik. Az adatok adatkötéssel kerülnek az UI-ra: minden szépen el van különítve. Valahogy így kéne kinéznie egy modern WPF/EF alkalmazásnak.

    Ajánlom a figyelmedbe a Microsoft ingyenes előadásait!
    Mutasd a teljes hozzászólást!
  • Szia
    Egyenlőre egy TermTerv osztályban rögzítettem az ideiglenes adatokat, mely nem ctx fájl és utána csak a végén adom át innen az értéket. De igyekszem megérteni amiket leírtál. 
    ObservableCollection-t már láttam, de leginkább, ahogy jelezted te is a gyakorlati példa hiányzik(remélem amit küldtél, azzal megértem).

    Egy gépen egy felhasználó van, továbbá mivel dolgozó-művelet párhoz kötött a rögzítés és a dolgozószám beolvasással történik, így az lehetetlen hogy több gépen ugyanazt vigyék be egyszerre, így ezen rész szerintem elhanyagolható.

    Köszi a segítséget majd elfogadom valószínűleg ezt válaszként!
    Csak egy kicsit átgondolom
    István
    Mutasd a teljes hozzászólást!
  • Egyenlőre egy TermTerv osztályban rögzítettem az ideiglenes adatokat, mely nem ctx fájl és utána csak a végén adom át innen az értéket.

    Na jó, nézzünk egy konkrét, egyszerűsített példát, hogy egyértelmű legyen, hogyan működik, mert valami nem stimmel.

    Itt a Northwind adatbázis, importáld be egy SQL Server-be.

    Konzol program, EF hozzáadva NuGet-tel. Projecten Add.../New Item.../Data/ADO.NET Entity Data Model: neve: NorthWindModel (EF Designer from database). Legenerálja.

    Attól, hogy létrehozol a generált osztályból egy példányt, az nem fogja az adatbázishoz hozzáadni. Példa:

    static void Main(string[] args) { Product product = new Product() { ProductName = "Büdös hal", SupplierID = 20, CategoryID = 8, Discontinued = true }; using (NORTHWNDEntities context = new NORTHWNDEntities()) { // nem adja hozzá context.SaveChanges(); } }
    Ha jól értettem a leírásod, ezt vártad (TermTerv osztály), de nem ez történt. Nem tudom, miért nem, de ez így működik, ahogy itt látod.

    Ha hozzá akarod adni, plusz egy sor kell:

    static void Main(string[] args) { Product product = new Product() { ProductName = "Büdös hal", SupplierID = 20, CategoryID = 8, Discontinued = true }; using (NORTHWNDEntities context = new NORTHWNDEntities()) { // most már hozzáadja context.Products.Add(product); context.SaveChanges(); } }
    Ha a context-ed hosszú életű, előfordulhat, hogy az egyik adatbázis objektum másolatán dolgozol, akkor azt hiába null-ozod ki, megmarad az eredeti. Lehet, hogy ez a problémád, de azt állítod, hogy az ideiglenes adataid függetlenek a context-től. Az itteni példán látod, mikor függetlenek valóban az ideiglenes adatok (product példány) az adatbázistól.

    Ehhez képest mi történik nálad, amikor "beragad az objektum"?
    Mutasd a teljes hozzászólást!
  • Nekem pontosan az okoz gondot, hogy WPF-en textBox-okban viszek be adatokat, melyekre ellenőrzéseket végzek.
        Pl.: Az adott dolgozószámhoz van-e dolgozó, létezik-e a termelés
           Majd mikor megkezdem a rögzítést: Van-e már művelete amit megkezdett.
         Ezért választottam az aktív kapcsolatot, hiszen különben minden ilyen vizsgálatlépésnél using(... ctx) kellene.

    Megprobáltam using( )-ba átírni, de volt olyan using-om ahol elakadt, ilyenkor rögzíthetem az eredményeket using-on kivül generált List<Entities>-ben?


    Így pedig hogy az App.Xaml.Cs-en rögzítettem egy public static ctx-et, melyett ...Entites ctx = App.ctx-el adtam át a Usercontrol változóinak deklarásához, így benne marad a korábbi érték, ha nem hajtom végre a savechanges methodust  

    Amit eddig írtál, abból azt veszem ki, hogy mivel a ctx aktív mikor létrehozom mondjuk a new Product()-al ekkor már belekerülne a ctx 
    Ezt arra alapoztam, hogy a ctx ->base -> ObjectStateMasanger -> Non-Public Members->_addedEntityStore  - nál megtalálom a beragadt elemeket. Egyszer találtam is rá valami törlési módszert, de az 4 részre építi fel és hosszúnak tűnt.
    Mutasd a teljes hozzászólást!
  • Majd mikor megkezdem a rögzítést: Van-e már művelete amit megkezdett.

    Miért nem egyben kéred le az adatbázisból az összes UI-hoz szükséges adatot? Ez már csak a konzisztencia miatt is szükséges lenne, azaz egy context nyitásával összetartozó adatokat fogsz kapni.

    Megprobáltam using( )-ba átírni, de volt olyan using-om ahol elakadt, ilyenkor rögzíthetem az eredményeket using-on kivül generált List<Entities>-ben?

    Határozottan igen. Még egyszer: kezeld külön az adatokat a memóriában, amikor a felhasználó szerkeszti, illetve az adatbázisba írást! Ne kombináld ezeket!

    Használhatod az EF által generált osztályokat is, de WPF alatt eleve az az ajánlott megközelítés, hogy a ViewModel tárolja a változtatni kívánt adataidat (fenti példa alapján pl. ha csak a termék nevét változtatod, akkor csak azt + az ID-t; nem kell a teljes Product osztályt használni), és aztán egy parancsra (pl. mentés gomb) az adatkezelő réteg megkapja, hogy mit kell módosítani, na akkor nyitsz egy context-et és írod be a változtatásokat.

    Az "elakadt" using-ról nem tudok mit mondani. Hogy világos legyen: a using egy syntactic sugar a Dispose() helyes használatára (azaz, hogy ne kelljen annyit gépelni).

    Plusz egy megjegyzés: listákat WPF alatt célszerű ObservableCollection-be rakni, nem List-be, hiszen pont az a lényege, hogy követi a változásokat.

    mivel a ctx aktív mikor létrehozom mondjuk a new Product()-al ekkor már belekerülne a ctx

    A fenti példa a Northwind adatbázissal pont arra mutat rá, hogy attól, hogy létrehozol egy új Product példányt, nem kerül bele a context-be. Ha nálad belekerül, az valamilyen bug eredménye lehet.

    Sajnos még egy ilyen egyszerű program is elég bonyolult tud lenni, ha mindent egybegyúrsz. Ezt úgy tudod kezelni, hogy szétválasztod az egyes részeit: az UI-kód csak az UI-val foglalkozik, adatot nem tárol egyáltalán; a ViewModel tárolja az adatokat a memóriában és vezérli a konkrét viselkedést (pl. gombnyomásra mentés induljon stb.); az adatelérési réteg nem tud semmit az UI-ról, vagy a ViewModel-ről, csak az a dolga, hogy konzisztensen kezeli az adatbázist (kérésre visszaadja az összetartozó objektumokat vagy kap egy parancsot, hogy adja hozzá az új terméket stb.).
    Mutasd a teljes hozzászólást!
  • a Northwind adatbázissal pont arra mutat rá, hogy attól, hogy létrehozol egy újProduct példányt, nem kerül bele a context-be.

    Az ilyen "tanult" tudás helyett szeretem, ha az alany (fejlesztő) először elmondja hogy valósították meg ezt korábban, lásd típusos dataset, majd az EF korábbi változatai, ahol nem volt POCO így egy csomó generált tulajdonságot kellett kerülgetni. Ezek miért voltak és most hogy POCO van, és ilyenek nincsenek, vajon HOGYAN oldja meg az EF, hogy a POCO objektumon működjenek a dolgok? [hiszen valahol ugyanazt csinálja, csak kívülről látszik másnak (egyszerűbbnek)].
    Vagyis egy szóval: nem használni kell megtanulni, hanem érteni hogyan működik.)
    Mutasd a teljes hozzászólást!
  • Szerintem amúgy az alapgond ott lehet, hogy static-usan rögzített context-et használok. 
    public static AcMEntities ctx; ....
    Lehet, hogy példányosítva jobb lenne, de igyekszem kiprobálni amit írtál.

    Miért nem egyben kéred le az adatbázisból az összes UI-hoz szükséges adatot? Ez már csak a konzisztencia miatt is szükséges lenne, azaz egy context nyitásával összetartozó adatokat fogsz kapni.

    Az első kérdésedre: Textbox-ok elhagyásakor tartok külön-külön ellenörzést, 100ezres adatmennyiséggel rendelkező táblákra. Ezért nem kérek le minden adatot az elején.

    ViewModel-t annak idején pont a nagy adatmennyiség miatt nem kedveltem, nekem nagyon lassúnak tűnt.

    De majd átnézem amiket összegyűjtöttél nekem. Meg ígyekszem a using -ot kiértelmezni.
    Köszi a segítséget
    Mutasd a teljes hozzászólást!
  • Textbox-ok elhagyásakor tartok külön-külön ellenörzést, 100ezres adatmennyiséggel rendelkező táblákra. Ezért nem kérek le minden adatot az elején.

    Hát nem is az egész adatbázis előolvasására gondolt 
    Minden az adott ablak megjelenítéséhez szükséges dolgot előre beolvasol. Természetesen a foreign key-hez tartozó szükséges rekordot is (pl. az int/guid PK helyett kód vagy megnevezés megjelenítése okán), de értelem szerűen nem minden lehetséges táblaelemet. [na jó, ritkán változó kódok, pl. ÁFA csoportok memóriába kerülnek, sőt, közös static tárolásra.]

    Mondjuk én csinálok olyat hogy nem a teljes adatmennyiséget olvasom elő ViewModel-be a Window mwgnyitása előtt, hanem a ViewModel azonnal látható részét, a többit háttér szálból tölti és amikor megjön az adat, akkor megjelenik a tartalom is (láthatóvá válik)

    Ugyanígy a textbox elhagyásakor lehetséges háttérszálból ellenőrizni a tartalmat, csak ez így több meló és nagyobb odafigyelés a szinkronizálásokkal (szóval amíg egy szálban nincs egy kiforrott módszertanod, addig maradj is ott).

    A ViewModel-t annak idején pont a nagy adatmennyiség miatt nem kedveltem, nekem nagyon lassúnak tűnt.

    Azért vicces, ha az adatbázis kérés+diszk művelet+hálózati forgalom sebessége összevethető néhány memória művelettel (ViewModel példányosítás, bele ORM objektumokból adatok másolása).
    Valamit elszúrtál!

    ígyekszem a using -ot kiértelmezni.

    Javaslom Tray Nash C# 2008 könyvének elolvasását. (mára talán 2010 vagy több is van, de ez is jó, alig van olyan nagy dobás, amit már ne tárgyalna) Pont attól jó a fazon, hogy mindennek az okát akarja megmutatni, a hátteret nem egyszerűen a lehetőségeket sorolja.
    Nem referencia, nem tankönyv, hanem profiknak szánt írás.
    Mutasd a teljes hozzászólást!
  • static-usan rögzített context-et használok

    Megint a háttértudás hiánya.
    Egy context példány egy tranzakció (esetedben statikus, vagyis app szinten egy)  
    [egy tranzakció: hacsak nem varázsolsz, de most ezt hagyjuk].

    Vagyis az alkalmazásod minden művelete egyetlen nagy tranzakció.
    Egyrészt ez többfelhasználós környezetben önt*k*nl*vés másrészt ezzel nem segíted az adatbáziskezelőt, inkább nehezíted a dolgát.

    Csinálj egy statikus factory-t, ami ctx példányokat ad vissza (így a jelenlegi statikus ctx használata egy pillanat alatt cserélhető szintaktikusan, persze a működést úgyis át kell tervezni).
    Alapelvként pedig csak indokolt (és kódban dokumentált) esetben legyen olyan, hogy a context nem egy using blokkban található!
    Mutasd a teljes hozzászólást!
Tetszett amit olvastál? Szeretnél a jövőben is értesülni a hasonló érdekességekről?
abcd