6 messaggi dal 09 gennaio 2013
Nella mia applicazione ho due entita': Person e Post. L'entita' Post contiene una relazione multipla. Questo e' il mio codice:

public class Post
    {
        public int Id { get; set; }
        public string Title { get; set; }
        public virtual Person CreatedBy { get; set; }
        public virtual Person UpdatedBy { get; set; }
    }

    public class Person
    {
        public int Id { get; set; }
        public string Name { get; set; }
        [InverseProperty("CreatedBy")]
        public ICollection<Post> PostsWritten { get; set; }
        [InverseProperty("UpdatedBy")]
        public ICollection<Post> PostsUpdated { get; set; }
    }

    public class MyContext : DbContext
    {
        public DbSet<Post> Posts { get; set; }
        public DbSet<Person> People { get; set; }
    }


Quando interrogo l'entita' principale, tramite Lazy Loading ottengo anche il risultato dell' entita' correlata, quindi: Post, CreatedBy e UpdatedBy.

private void button1_Click(object sender, EventArgs e)
        {
            using (var ctx = new MyContext())
            {

                var a = (from d in ctx.Posts
                         where d.Id == 1
                         select d).Single();

                postBindingSource.DataSource = a;
            }
        }

Il codice mi mappa correttamente il DB. Tuttavia ho notato un comportamento anomalo sia se sviluppo in Winform, quindi faccio uso del BindingSource, sia se sviluppo tramite WPF.

Infatti, solo e soltanto in cui CreatedBy e UpdatedBy hanno la stessa identica chiave, quando provo a modificare il textbox Name di CreatedBy automaticamente, a video mi copia quanto da me editato, anche nel textbox UpdatedBy. Ma questo accade solo se CreatedBy e UpdatedBy hanno la stessa ed identica key. Diversamente questa "stranezza" non accade. Dove sbaglio?
11.886 messaggi dal 09 febbraio 2002
Contributi
ciao, benvenuto nel forum!

PieDatt80 ha scritto:

quando provo a modificare il textbox Name di CreatedBy automaticamente, a video mi copia quanto da me editato, anche nel textbox UpdatedBy

già, è normale che sia così perché, sia che tu acceda dalla proprietà di navigazione CreatedBy che da UpdateBy, stai modificando la stessa entità Person.
Entity Framework implementa il pattern Identity Map, ciò vuol dire che nel DbContext non possono coesistere due istanze diverse di Person che abbiano la stessa chiave primaria.

Se vuoi cambiare la Person assegnata al CreatedBy o UpdateBy, devi necessariamente creare una nuova istanza (che verrà poi inserita nel database al SaveChanges) oppure selezionarne un'altra tra quelle che già esistono.

buona domenica
Modificato da BrightSoul il 30 giugno 2013 17.43 -

Enjoy learning and just keep making
6 messaggi dal 09 gennaio 2013
Grazie per la rapida risposta, non ero a conoscenza del pattern Identity Map. Tuttavia, ho fatto vari tentativi per poter creare una nuova istanza , come da tuo suggerimento, ma proprio non riesco ad evitare che appena scrivo qualcosa in un TextBox mi modifica anche l'altro.
Premetto che sebbene sono riuscito ad implementare un Generic Repository ed una Unit Of Work il mio livello di conoscenza del c# è di tipo "Amatoriale". Ma voglio migliorare...

Dove posso approfondire?
Sono un felice possessore di "C# 4 - Guida completa per lo sviluppatore". Se utile a risolvere il mio problema potrei investire anche su "Entity Framework 4 in Action"
11.886 messaggi dal 09 febbraio 2002
Contributi
ciao,

PieDatt80 ha scritto:

Tuttavia, ho fatto vari tentativi per poter creare una nuova istanza

Prova a postare il codice che hai usato, magari riusciamo a risolverlo insieme. In linea teorica è sufficiente fare un new Person e assegnarlo alla proprietà CreatedBy o UpdatedBy.

Forse però bisogna fare un passo indietro e rivalutare l'interfaccia grafica. Quella che stai realizzando è una form di modifica POST, quindi è inusuale che si possa contestualmente modificare anche un oggetto Person. Infatti, proprio come è successo, l'utente potrebbe credere che cambiando il Name della Person, la modifica sia relativa solo al Post che sta vedendo a schermo. Ma abbiamo visto che non è così. Qui c'è il rischio che l'utente vada a cambiare l'identità della Person senza rendersene conto, e tutti gli altri Post correlati ad essa subiranno la stessa modifica.

Per non generare confusione, prova a considerare quest'altro layout:
In corrispondenza dei campi CreatedBy e UpdatedBy c'è una ComboBox (non editabile) che ti lascia scegliere tra tutte le person disponibili. Se sono molte, puoi anche lasciare che l'utente digiti un nome e suggerirgli dei risultati con l'autocompletamento.
Ogni qualvolta l'utente sceglie qualcosa dalla ComboBox, l'istanza di Person assegnata a CreatedBy o UpdatedBy cambia.
Opzionalmente puoi mettergli un tastino di fianco alla ComboBox che faccia apparire una nuova form come popup per la modifica della Person selezionata. In questo modo forse è più chiaro che il contesto è un altro - ora siamo in una seconda form intitolata "Modifica Person" e a rafforzare questo concetto puoi scriverci: "Attenzione, le modifiche interesseranno tutti i Post legati a questa Person".

Se l'utente volesse aggiungere una nuova Person, puoi aggiungere un ulteriore tasto "Nuovo" che aprirà la stessa form ma con i campi vuoti.

Pensi che possa andar bene nel tuo caso?

PieDatt80 ha scritto:

Dove posso approfondire?

Se lavori esclusivamente con Entity Framework 4, sì, puoi acquistare Entity Framework 4 in Action.
Se nel prossimo futuro inizierai un altro progetto, allora studia su qualcosa che comprenda anche le novità di Entity Framework 5. Puoi andare su PluralSight dove trovi i corsi di Julie Lerman, una delle autorità su EF. Copre anche le versioni 4 e 5.

ciao

Enjoy learning and just keep making
6 messaggi dal 09 gennaio 2013
Effettivamente il problema principale è proprio l'interfaccia.
Il mio scopo principale è quello di caricare contemporaneamente l'entità principale ed i suoi dettagli. E per questo Lazy Loading lavora egregiamente.

Questo è il codice dell'interfaccia grafica in WPF:
<Window
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:Wpf" mc:Ignorable="d" x:Class="Wpf.MainWindow"
        Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded">
    <Window.Resources>
        <CollectionViewSource x:Key="postViewSource" d:DesignSource="{d:DesignInstance {x:Type local:Post}, CreateList=True}"/>
    </Window.Resources>
    <Grid>

        <Grid x:Name="grid1" VerticalAlignment="Top" Margin="166,57,0,0" HorizontalAlignment="Left" DataContext="{StaticResource postViewSource}">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="Auto"/>
            </Grid.ColumnDefinitions>
            <Label VerticalAlignment="Center" Grid.Row="0" Margin="3" HorizontalAlignment="Left" Grid.Column="0" Content="Id:"/>
            <TextBox x:Name="idTextBox" Width="120" VerticalAlignment="Center" Text="{Binding Id, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}" Grid.Row="0" Margin="3" Height="23" HorizontalAlignment="Left" Grid.Column="1"/>
            <Label VerticalAlignment="Center" Grid.Row="1" Margin="3" HorizontalAlignment="Left" Grid.Column="0" Content="Title:"/>
            <TextBox x:Name="titleTextBox" Width="120" VerticalAlignment="Center" Text="{Binding Title, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}" Grid.Row="1" Margin="3" Height="23" HorizontalAlignment="Left" Grid.Column="1"/>
            <Label VerticalAlignment="Center" Grid.Row="2" Margin="3" HorizontalAlignment="Left" Grid.Column="0" Content="Id:"/>
            <TextBox x:Name="idTextBox1" Width="120" VerticalAlignment="Center" Text="{Binding CreatedBy.Id, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}" Grid.Row="2" Margin="3" Height="23" HorizontalAlignment="Left" Grid.Column="1"/>
            <Label VerticalAlignment="Center" Grid.Row="3" Margin="3" HorizontalAlignment="Left" Grid.Column="0" Content="Name:"/>
            <TextBox x:Name="nameTextBox" Width="120" VerticalAlignment="Center" Text="{Binding CreatedBy.Name, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}" Grid.Row="3" Margin="3" Height="23" HorizontalAlignment="Left" Grid.Column="1"/>
            <Label VerticalAlignment="Center" Grid.Row="4" Margin="3" HorizontalAlignment="Left" Grid.Column="0" Content="Id:"/>
            <TextBox x:Name="idTextBox2" Width="120" VerticalAlignment="Center" Text="{Binding UpdatedBy.Id, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}" Grid.Row="4" Margin="3" Height="23" HorizontalAlignment="Left" Grid.Column="1"/>
            <Label VerticalAlignment="Center" Grid.Row="5" Margin="3" HorizontalAlignment="Left" Grid.Column="0" Content="Name:"/>
            <TextBox x:Name="nameTextBox1" Width="120" VerticalAlignment="Center" Text="{Binding UpdatedBy.Name, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}" Grid.Row="5" Margin="3" Height="23" HorizontalAlignment="Left" Grid.Column="1"/>
        </Grid>
        <Button Content="Button" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75" Margin="10,10,0,0" Click="Button_Click"/>

    </Grid>
</Window>


e questo il suo adattamento:
private void Button_Click(object sender, RoutedEventArgs e)
        {
            using (var ctx = new MyContext())
            {
                var a = (from d in ctx.Posts
                            where d.Id == 1
                            select d).Single();

                grid1.DataContext = a;
            }
        }
11.886 messaggi dal 09 febbraio 2002
Contributi
ciao,
prova a fare questa modifica, poi da lì decideremo come andare avanti:
al posto delle 4 TextBox che hai creato per Id e Name, metti 2 ComboBox (una per CreatedBy e una per UpdatedBy) che popolerai con tutte le Person esistenti.
Segui questo esempio, lì puoi vedere come la ComboBox sia messa in binding direttamente con la proprietà di navigazione, che nel tuo caso è CreatedBy e UpdatedBy.

Quando questo funziona, vedremo come poter consetire all'utente di creare una nuova person, o come ottimizzare la scelta della Person usando l'autocomplete.

PieDatt80 ha scritto:

per questo Lazy Loading lavora egregiamente.

Certo, non ne dubito, anche se tu sai già dall'inizio che i dati delle due Person di CreatedBy e UpdatedBy ti serviranno di lì a poco.

Anziché usare il Lazy Loading, puoi ricorrere all'eager loading, ovvero al caricamento contestuale di entità principale (Post) ed entità correlate (le due Person). Entity Framework dunque recupererà tutti questi dati con una sola query al database, ottimizzando le prestazioni. Usa il metodo Include per questo:
var a = (from d in ctx.Posts.Include("CreatedBy").Include("UpdatedBy")
where d.Id == 1 select d).Single();


ciao

Enjoy learning and just keep making
6 messaggi dal 09 gennaio 2013
Grazie per il consiglio sull'eager loading. Effettivamente fa al caso mio.
Tra i vari tentativi, credo di aver trovato la soluzione. Mi spiego.
Ho modificato lo XAML in questo modo:
<Label Content="Id:" Grid.Column="0" HorizontalAlignment="Left" Margin="3" Grid.Row="0" VerticalAlignment="Center"/>
            <TextBox x:Name="idTextBox" Grid.Column="1" HorizontalAlignment="Left" Height="23" Margin="3" Grid.Row="0" Text="{Binding Id, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}" VerticalAlignment="Center" Width="120"/>
            <Label Content="Title:" Grid.Column="0" HorizontalAlignment="Left" Margin="3" Grid.Row="1" VerticalAlignment="Center"/>
            <TextBox x:Name="titleTextBox" Grid.Column="1" HorizontalAlignment="Left" Height="23" Margin="3" Grid.Row="1" Text="{Binding Title, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}" VerticalAlignment="Center" Width="120"/>
            <Label Content="Id:" Grid.Column="0" HorizontalAlignment="Left" Margin="3" Grid.Row="2" VerticalAlignment="Center"/>
            <Label Content="Name:" Grid.Column="0" HorizontalAlignment="Left" Margin="3" Grid.Row="3" VerticalAlignment="Center"/>
            <TextBox x:Name="CreatedBy" Grid.Column="1" HorizontalAlignment="Left" Height="23" Margin="3" Grid.Row="3" VerticalAlignment="Center" Width="120"/>
            <Label Content="Id:" Grid.Column="0" HorizontalAlignment="Left" Margin="3" Grid.Row="4" VerticalAlignment="Center"/>
            <Label Content="Name:" Grid.Column="0" HorizontalAlignment="Left" Margin="3" Grid.Row="5" VerticalAlignment="Center"/>
            <TextBox x:Name="UpdatedBy" Grid.Column="1" HorizontalAlignment="Left" Height="23" Margin="3" Grid.Row="5" VerticalAlignment="Center" Width="120"/>

rimuovendo i riferimenti di Binding al CreatedBy e UpdatedBy. Mentre a livello di codice ho scritto:

using (var ctx = new MyContext())
            {
                ctx.Configuration.LazyLoadingEnabled = false;

                var a = (from d in ctx.Posts
                             .Include("CreatedBy")
                             .Include("UpdatedBy")
                         where d.Id == 1
                         select d).Single();

                grid1.DataContext = a;

                CreatedBy.Text = a.CreatedBy.Name.ToString();
                UpdatedBy.Text = a.UpdatedBy.Name.ToString();
            }

Il tutto funziona correttamente, ma spero che anche come approccio sia corretto. Resto in attesa di ulteriori consigli e suggerimenti.

Torna al forum | Feed RSS

ASPItalia.com non è responsabile per il contenuto dei messaggi presenti su questo servizio, non avendo nessun controllo sui messaggi postati nei propri forum, che rappresentano l'espressione del pensiero degli autori.