Robert Pająk - Blog http://rpajak.com Inżynieria oprogramowania, .NET i bazy danych Fri, 28 Nov 2014 16:59:31 +0000 pl-PL hourly 1 http://wordpress.org/?v=4.0.1 Jakość oprogramowania i Extreme Programming http://rpajak.com/jakosc-oprogramowania-i-extreme-programming/ http://rpajak.com/jakosc-oprogramowania-i-extreme-programming/#comments Fri, 28 Nov 2014 16:58:48 +0000 http://rpajak.com/?p=262 Jakieś pół roku temu miałem przyjemność zakończyć studia podyplomowe z zarządzania projektami na AGH.
Większość zajęć uważam za bardzo pożyteczne, nawet jeśli niektóre mi się nie podobały (np. związane z budżetowaniem), to dały mi dość szeroką wiedzę na temat różnych aspektów związanych z zarządzaniem projektami, jak również organizacjami. Znaczna część kadry pedagogicznej reprezentuje naprawdę wysoki poziom!

Jedną z rzeczy, które zostały mi po studiach jest moja praca dyplomowa (rzadkość na studiach podyplomowych), którą chętnie się podzielę: Jakość a XP. Zapraszam do lektury – komentarze mile widziane! Uwaga: byłem zmuszony zmieścić się z tematem w maksymalnie 32 stronach.

]]>
http://rpajak.com/jakosc-oprogramowania-i-extreme-programming/feed/ 1
IFactory = abstrakcja na Func<Owned<T>> http://rpajak.com/ifactory-abstrakcja-na-func/ http://rpajak.com/ifactory-abstrakcja-na-func/#comments Mon, 27 Oct 2014 21:58:40 +0000 http://rpajak.com/?p=253 Omawiana w poprzednim poście zależność w najprostszej postaci charakteryzuje się pewnym nieprzyjemnym problemem (nie mówiąc o samym fakcie konieczności korzystania z takiej abstrakcji), który postaram się przedstawić na przykładzie.

public interface IFoo
{
	void Save();
}

public class ClassWithResource : Disposable, IFoo
{
	// ...
}

public class ServiceImpl : Disposable, IService
{
	public ServiceImpl(IOwned<IFoo> foo)
	{
		Foo = foo;
	}

	public IOwned<IFoo> Foo { get; private set; }

	public void Add()
	{
		// ...
		Foo.Value.Save();
		// ...
	}

	protected override void DisposeManaged()
    {
        Foo.Dispose();
    }
}

public class ControllerImpl : Disposable, IController
{
	public ControllerImpl(IOwned<IService> service)
	{
		Service = service;
	}

	public IOwned<IService> Service { get; private set; }

	public void HandleRequest()
	{
		// ...
		Service.Value.Add();
		// ...
	}

	protected override void DisposeManaged()
    {
        Service.Dispose();
    }
}

Jak widać korzystanie z „czystego” IOwned poprzez Constructor Injection powoduje, że uzyskujemy łańcuszek zależności IOwned oraz klas implementujących IDisposable – wygląda to co najmniej okropnie. Rozwiązaniem tego problemu jest jak możliwie najszybsze przerwanie tego łańcucha. Najlepiej według mnie to zrobić poprzez połączenia IOwned z wzorcem Factory. Dzięki takiemu podejściu możemy otrzymać następujący kod:

public interface IFoo
{
	void Save();
}

public class ClassWithResource : Disposable, IFoo
{
	// ...
}

public class ServiceImpl : IService
{
	public ServiceImpl(IFactory<IFoo> fooFactory)
	{
		Foo = fooFactory;
	}

	public IFactory<IFoo> FooFactory { get; private set; }

	public void Add()
	{
		// ...
		using (IOwned<IFoo> foo = FooFactory.Create())
		{
			foo.Value.Save();
		}
		// ...
	}
}

public class ControllerImpl : IController
{
	public ControllerImpl(IService service)
	{
		Service = service;
	}

	public IService Service { get; private set; }

	public void HandleRequest()
	{
		// ...
		Service.Add();
		// ...
	}
}

Poniżej przedstawiam sposób w jaki zaimplementowałem IFactory, tak aby można było go zarejestrować w kontenerach, które obsługują wstrzykiwanie Func.

public interface IFactory<out T>
{
	IOwned<T> Create();
}

public interface IFactory<in T, out TOut>
{
	IOwned<TOut> Create(T input);
}

public class Factory<T> : IFactory<T>
{
	private readonly Func<T, IOwned<T>> ownedFactory;
	private readonly Func<T> creator;

	public Factory(Func<T, IOwned<T>> ownedFactory, Func<T> creator)
	{
		this.ownedFactory = ownedFactory;
		this.creator = creator;
	}

	public Factory(Func<T> creator)
		: this(Owned.Create, creator)
	{
	}

	public IOwned<T> Create()
	{
		T ownedInstance = creator();
		IOwned<T> result = ownedFactory(ownedInstance);
		return result;
	}
}

public class Factory<T, TOut> : IFactory<T, TOut>
{
	private readonly Func<TOut, IOwned<TOut>> ownedFactory;
	private readonly Func<T, TOut> creator;

	public Factory(Func<TOut, IOwned<TOut>> ownedFactory, Func<T, TOut> creator)
	{
		this.ownedFactory = ownedFactory;
		this.creator = creator;
	}

	public Factory(Func<T, TOut> creator)
		: this(Owned.Create, creator)
	{
	}

	public IOwned<TOut> Create(T input)
	{
		TOut ownedInstance = creator(input);
		IOwned<TOut> result = ownedFactory(ownedInstance);
		return result;
	}
}

Kod z testami:
Na GitHub w solucji DiProblem.

]]>
http://rpajak.com/ifactory-abstrakcja-na-func/feed/ 3
IOwned = IDisposable + Dependency Injection http://rpajak.com/iowned-aby-nasze-abstrakcja-byly-mozliwie-czysta/ http://rpajak.com/iowned-aby-nasze-abstrakcja-byly-mozliwie-czysta/#comments Thu, 23 Oct 2014 20:13:29 +0000 http://rpajak.com/?p=243 Założenie:
Chcemy mieć możliwie wysoką zgodność z SOLID + Depedency Injection

Problem:
1. Piszemy implementację, która zawiera jakieś zasoby i musi je zwalniać (czyli implementacja IDisposable)
2. Omawiane zasoby są bardzo obciążające i nie chcemy je jak najszybciej zwalniać
3. Nie chcemy mieć interfejsu dziedziczącego po IDisposable, bo najczęściej jest to Leaky Abstraction

Rozwiązanie:
Nicholas Blumhardt w poście na temat różnych rodzajów zależności przedstawioną zależność opisuje jako „Owned”, którą jest zaimplementowana np. w Autofac.

Jednak:
1. Nie powinniśmy korzystać z klasy Owned w innych miejscach niż Compostion Root, bo wtedy wszystkie nasze klasy byłby zależne od Autofac’a
2. Może nie mamy AutoFac’a? A może nawet nie używamy kontenera tylko preferujemy Pure DI

Rozwiązanie:
Zaimplementowanie własnej abstrakcji dla tej zależności.

public interface IOwned<out T> : IDisposable
{
    T Value { get; }
}

public class Owned<T> : IOwned<T>
{
    public Owned(T value)
    {
        Value = value;
    }

    public T Value { get; private set; }

    public void Dispose()
    {
        var disposable = Value as IDisposable;
        if (disposable != null)
        {
            disposable.Dispose();
        }
    }
}

public static class Owned
{
    public static IOwned<T> Create<T>(T instance)
    {
        return new Owned<T>(instance);
    }
}

Można z podanej implementacji korzystać na dwa sposoby:
1. Zarejestrować w kontenerze
2. Jawne tworzenie w CompositionRoot za pomocą Owned.Create (syntax sugar, aby nie było konieczności podawania generyka)

Implementacja oparta o Autofac’owy Owned:

public class AutofacOwned<T> : IOwned<T>
{
    private readonly Autofac.Features.OwnedInstances.Owned<T> owned;

    public AutofacOwned(Autofac.Features.OwnedInstances.Owned<T> owned)
    {
        this.owned = owned;
    }

    public T Value
    {
        get { return owned.Value; }
    }

    public void Dispose()
    {
        owned.Dispose();
    }
}

Zalety:
Autofac’owy Owned nie zwalnia samego obiektu, ale cały graf związany z obiektem. Czyli jeśli dany obiekt zawierał jakieś referencje na dowolnej głębokości, które implementują IDisposable, to je też zwolni! W przypadku innych kontenerów można łatwo stworzyć implementację IOwned opartą o „child container”. Aby zrobić coś podobnego całkowicie samodzielnie, to mój Owned musiałby być już tak naprawdę kontenerem, który zarządza czasem życia wszystkich obiektów – za trudne :)

Kod z testami:
Na GitHub w solucji DiProblem

Podsumowanie:
To że dana implementacja zarządza jakimiś zasobami i że trzeba po niej posprzątać jest jego cross-cutting concern (problematyka AOP) i nie jest możliwe proste rozwiązanie tego programu w językach zorientowanych obiektowo. Wykorzystane rozwiązanie ma kilka zalet:
1. Nie zmuszamy wszystkich implementacji danego interfejsu, aby implementował Dispose
2. Jesteśmy w stanie sprzątać zasoby jeśli jest taka potrzeba
3. Dzięki dodatkowej abstrakcji i Depedency Injection widziemy po samych sygnaturach, które klasy zawierają zależności, które wymagają posprzątania
ALE NIESTETY:
1. Klasa „kliencka” musi sama wiedzieć, że ma do czynienia z referencją, którą trzeba zwolnić – jak zapomnimy nasz intefejs opakować w IOwned i go zwolnić – to nic nam nie pomoże (no chyba, że napisaliśmy „sprytną” implementację IDisposable albo używamy finalizatorów)
2. Wprowadzamy dodatkową abstrakcję przez co rośnie złożoność kodu

Widziałem też rozwiązanie tego problemu polegającego na próbie rzutowania danej klasy do IDisposable i ew. jego zwolnieniu (przykład). Takie rozwiązanie mi się jednak nie podoba, ponieważ wymaga rzutowania w wielu miejscach aplikacji. Mnie się udało scentralizować rzutowania do klasy Owned.

]]>
http://rpajak.com/iowned-aby-nasze-abstrakcja-byly-mozliwie-czysta/feed/ 5
MVVM Utils 1.1.5404 http://rpajak.com/mvvm-utils-1-1-5404/ http://rpajak.com/mvvm-utils-1-1-5404/#comments Sat, 18 Oct 2014 12:52:15 +0000 http://rpajak.com/?p=239 Ostatnio miałem trochę czasu i zrobiłem porządki w biblioteczce, która zawiera parę Utilsów/Helperów przydatnych podczas kodzenia w WPF.
MVVM Utils 1.1.5404
Ostatnie zmiany polegały głównie na refaktoryzacji API.

Jak ktoś pracuje w WPF, to zapraszam do przejrzenia – może coś się przyda. Licencja MIT, więc można robić z kodem co się tylko podoba. Jest też paczka na NuGet – może kiedyś coś dodam albo poprawię – nigdy nie wiadomo :)

]]>
http://rpajak.com/mvvm-utils-1-1-5404/feed/ 1
Implementowanie IDisposable http://rpajak.com/implementowanie-idisposable/ http://rpajak.com/implementowanie-idisposable/#comments Sat, 27 Sep 2014 15:08:56 +0000 http://rpajak.com/?p=227 Nieraz zdarza się, że musimy samodzielnie napisać jakąś klasę implementującą interfejs IDisposable. Wszyscy na pewno znają wzorzec implementowania Dispose. Jednak moim zdaniem ma on kilka wad:

  • Nie jest bezpieczny wątkowo
  • Daje możliwość wielokrotnego sprzątania zasobów – nie zabezpiecza klienta przed wielokrotnym wywołaniem Dispose
  • Jak chcemy mieć finalizator to musimy go sobie sami napisać i wywołać w nim Dispose(false)
  • Nieszczęsna metoda Dispose(bool disposing) sprowadzającego się do tego, że 90% zasobów zwalniamy w bloku „if (disposing)”, a konstrukcja „if (!disposing)” jest w ogóle pozbawiona jakiekolwiek sensu – ogólnie moim zdaniem ten sposób implementacji jest bardzo nieczytelny. Jedną jego zaletą jest możliwość decydowania o dokładnej kolejności zwalania zasobów

Z tego względu proponuję następującą bazową implementację IDisposable:

    public class Disposable : IDisposable
    {
        private const int DisposedFlag = 1;

        private readonly StackTrace creationStackTrace;

        private int _isDisposed;

        public Disposable(bool withFinalizer = false)
        {
#if DEBUG
            creationStackTrace = new StackTrace();
#else
            if (!withFinalizer)
            {
                GC.SuppressFinalize(this);
            }
#endif
        }

        ~Disposable()
        {
            DisposeUnmanaged();
#if DEBUG
            Debug.Fail(GetType() + " in not disposed" + Environment.NewLine + creationStackTrace);
#endif
        }

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        [SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly", Justification = "Dispose is implemented correctly")]
        public void Dispose()
        {
            int wasDisposed = Interlocked.Exchange(ref _isDisposed, DisposedFlag);
            if (wasDisposed != DisposedFlag)
            {
                DisposeResources();
            }
        }

        protected virtual void DisposeManaged()
        {
        }

        protected virtual void DisposeUnmanaged()
        {
        }

        protected void RequireNotDisposed()
        {
            if (IsDisposed)
            {
                string typeName = GetType().FullName;
                throw new ObjectDisposedException(typeName);
            }
        }

        public bool IsDisposed
        {
            get
            {
                Thread.MemoryBarrier();
                return _isDisposed == DisposedFlag;
            }
        }

        private void DisposeResources()
        {
            DisposeManaged();
            DisposeUnmanaged();
            GC.SuppressFinalize(this);
        }
    }

Zalety klasy Disposable:

  • Bezpieczna wątkowo
  • Zabezpieczenie przed wielokrotnym zwalnianiem zasobów
  • Posiada pomocniczą metodę RequireNotDisposed, którą można wykorzystać w metodach publicznych klasy
  • W konfiguracji Debug zawsze tworzony jest finalizator, który może być użyty podczas śledzenia, czy czasami nie zapomnieliśmy wywołać Dispose
  • Jak mamy zasoby zarządzane, to przeciążamy metodę DisposeManaged. Z kolei w przypadku zasobów niezarządzanych – DisposeUnmanaged
  • Możliwość sprawdzenia czy obiekt ma już zwolnione zasoby poprzez IsDisposed
  • W razie potrzeby posiadania finalizatora wystarczy wykorzystać konstruktor z parameterem (wiem, że bool jest brzydki – można zrobić enuma)

Na marginesie jakby ktoś był zmuszony dziedziczyć (sic!) po klasie dziedziczącej po Disposable to radzę to robić w taki sposób – link. Dzięki temu nie musimy się martwić, że ktoś zapomni wywołać base.Dispose(disposing) albo base.DisposeManagedResources.

Jeżeli, ktoś nie chce albo nie może wykorzystywać dziedziczenia, to proponuję wykorzystać wzorzec delegacji w połączeniu z następującą klasą:

    public class Disposer : Disposable
    {
        private readonly Action action;

        public Disposer(Action action)
        {
            if (action == null) throw new ArgumentNullException("action");
            this.action = action;
        }

        protected override void DisposeManaged()
        {
            action();
        }
    }

Powyższa konstrukcja oparta o delegat jest poprawna tylko w przypadku zasobów zarządzanych. Nie można wykorzystać podobnego mechanizmu dla DisposeUnmanaged. Obiekt Action jest zarządzany i może dojść do zwolnienia jego zasobów zanim wywoła się finalizator obiektu, który posiada do niego referencję.

]]>
http://rpajak.com/implementowanie-idisposable/feed/ 5
[TFS] Team Foundation Service / Visual Studio Online + Code Contracts http://rpajak.com/team-foundation-service-code-contracts/ http://rpajak.com/team-foundation-service-code-contracts/#comments Fri, 27 Sep 2013 10:08:06 +0000 http://rpajak.com/blog/?p=163 Jakiś czas temu postanowiłem, że zrobię mały projekcik MVVM Utils, aby liznąć: Git, Team Foundation Service, Codeplex, GitHub, Code Contracts.

Wszystko szło pięknie dopóki nie chciałem zrobić Build’a na TFS projektów, które korzystają z Code Contracts. Niestety TFS tego domyślnie nie wspiera. Znalezione rozwiązanie problemu jest nieaktualne, ponieważ obecnie nie można edytować Build process template. Ale udało mi się to objeść odpowiednio parametryzując definicję builda.

Poniżej instrukcja, która pokazuje krok-po-kroku, co należy zrobić w Visual Studio, aby umożliwić Team Foundation Service budowanie projektów z Code Contracts:

  1. Skopiowanie folderu C:\Program Files (x86)\Microsoft\Contracts do repozytrium np. do $/CustomBuildBinarier
  2. Dodanie do plików csproj korzystających z Code Contracts importu: <Import Condition=”‚$(CodeContractsImported)’ != ‚true’ AND ‚$(DontImportCodeContracts)’ != ‚true'” Project=”$(CodeContractsInstallDir)\MsBuild\v4.0\Microsoft.CodeContracts.targets” />
  3. Edit Build Definition… ->Process ->4. Advanced -> MSBuild arguments:  /p:CodeContractsInstallDir=C:\a\src\CustomBuildBinaries\CodeContracts\

Team Foundation Service + Code Contracts

 

]]>
http://rpajak.com/team-foundation-service-code-contracts/feed/ 0
[WPF] ToolTip w DataGridColumnHeader http://rpajak.com/datagridcolumnheader-tooltip/ http://rpajak.com/datagridcolumnheader-tooltip/#comments Tue, 05 Mar 2013 08:44:06 +0000 http://rpajak.com/blog/?p=124 Aby wyświetlić ToolTip w nagłówku kolumny DataGrid należy wykorzystać styl:

<Style TargetType="DataGridColumnHeader">
    <Style.Triggers>
        <Trigger Property="IsMouseOver" Value="True">
            <Setter Property="ToolTip" Value="{Binding Column.(ToolTipService.ToolTip), RelativeSource={RelativeSource Self}}"/>
        </Trigger>
    </Style.Triggers>
</Style>

Przykład wykorzystania:

<DataGridCheckBoxColumn Header="Wymagania" ToolTipService.ToolTip="Czy profil spełnia minimalne wymagania?" Binding="{Binding DoesMeetRequirement}" />
]]>
http://rpajak.com/datagridcolumnheader-tooltip/feed/ 0
[EF] Obsługa zmian w DbContext http://rpajak.com/obsluga-zmian-w-dbcontext/ http://rpajak.com/obsluga-zmian-w-dbcontext/#comments Sat, 21 Apr 2012 12:36:48 +0000 http://rpajak.com/blog/2012/04/obsluga-zmian-w-dbcontext/ Mechanizm śledzenia zmian

Dopóki nie ma wyraźniej potrzeby, to nie należy niczego zmieniać. Jakakolwiek modyfikacja, prawdopodobnie wniesie mało (jeśli w ogóle) korzyści, przy czym mamy impelementując samoodzielnie taki mechanizm jest duże prawdobieństwo, że gdzieś popełnimy błąd.

Jednak warto rozumieć mechanizm śledzenia zmian Entity Framework, aby efektywnie pisać kod.

Automatycznie wywoływane DetectChanges

Domyślnie sprawdzanie zmian encji jest dokonywane poprzez DetectChanges, który porównuje aktualny stan poszczególnych encji z zapamiętanym snapshotem.

DetectChanges jest automatycznie wywoływany podczas korzystania z metod:

  • DbSet.Find
  • DbSet.Local
  • DbSet.Remove
  • DbSet.Add
  • DbSet.Attach
  • DbContext.SaveChanges
  • DbContext.GetValidationErrors
  • DbContext.Entry
  • DbChangeTracker.Entries

Walidacja

ValidateEntry jest wykorzystywany do przeprowadzania własnych walidacji podczas GetValidationErrors lub SaveChanges.

Przykład zwiększania efektywności dokonywania zmian w EF

Zamiast:

public void AddPosts(List posts)
{
using (var context = new AnotherBlogContext())
{
posts.ForEach(p => context.Posts.Add(p));
context.SaveChanges();
}
}

Można napisać wg arykułu:

public void AddPosts(List posts)
{
    using (var context = new AnotherBlogContext())
    {
        try
        {
            context.Configuration.AutoDetectChangesEnabled = false;
            posts.ForEach(p => context.Posts.Add(p));
        }
        finally
        {
            context.Configuration.AutoDetectChangesEnabled = true;
        }
        context.SaveChanges();
    }
}

Chociaż osobiście do scenariuszów tego typu stworzyłem klasę ChangesScope implementującą IDisposable:

public class ChangesScope : IDisposable
{
    private DbContext dbContext;

    public ChangesScope(DbContext dbContext)
    {
        this.dbContext = dbContext;
        dbContext.Configuration.AutoDetectChangesEnabled = false;
    }

    public void Dispose()
    {
        dbContext.Configuration.AutoDetectChangesEnabled = true;
        dbContext.ChangeTracker.DetectChanges();
    }
}

Wtedy wspomniany przykład można by napisać w następujący sposób:

public void AddPosts(List posts)
{
    using (var context = new AnotherBlogContext())
    {
        using (new ChangesScope(context))
        {
            posts.ForEach(p => context.Posts.Add(p));
        }
        context.SaveChanges();
    }
}

Niestety w tym wypadku DetectChanges() będzie tak naprawdę wywołane 2 razy, ale wydaje mi się, że kod jest bardziej czytelny (bliżej mu do jednego poziomu abstrakcji).

Bardzo dobrze umówione aspekty zwiększania efektywności zmian encji są opisane w http://blog.oneunicorn.com/2012/03/12/secrets-of-detectchanges-part-3-switching-off-automatic-detectchanges/

Zasady

Zasady zachowania stanu:

Skąd wiadomo, czy można wyłączyć automatyczne DectectChanges na dłuższy okres. Ważna jest kontrola dwóch zasad:

1. Żadne wywołanie kodu EF nie zostawi kontekstu w stanie, w którym DetectChanges będzie potrzebne do wywołania, jeśli wcześniej wywołanie nie było wymagane

2. Za każdym razem kiedy nieEF kod zmieni jakąś właściwość encji albo złożony obiekt – wtedy DetectChanges musi być wywołany

Zasady zarządzania stanem:

1. Nie wyłączaj automatycznego DetectChanges jeśli naprawdę tego nie potrzebujesz

2. Jeśli musisz wyłączyć DetechChanges, to najlepiej rób to lokalnie w blokach try/finalny

3. Stosuj DbContext property API, aby zmieniać encje bez konieczności wywoływania DetectChanges

Perełki:

1. Traktuj właściwości byte[] jako pola immutable. Zawsze jak chcesz zmienić taką właściwość, to przypisuj jej nową instancję.

Cofanie zmian

W obecnej wersji EntityFramework metodę cofającą zmiany można zaimplementować w następujący sposób:

public virtual void RejectChanges()
{
    foreach (var entry in ChangeTracker.Entries())
    {
        if (entry.State == EntityState.Modified)
        {
            entry.State = EntityState.Unchanged;
        }
        else if (entry.State == EntityState.Added)
        {
            entry.State = EntityState.Detached;
        }
        else if (entry.State == EntityState.Deleted)
        {
            entry.Reload();
        }
    }

    SaveChanges();
}

Niestety metoda Reload używana dla encji, które zostały usunięte polega na tym, że zostaje odświeżony stan encji na podstawie bazy danych (czyli łączymy się z bazą danych).

Chociaż tak naprawdę lepiej unikać korzystania z takiej metody i po prostu tworzyć nową instancję DbContext i wszystko odświeżać na jej podstawie.

Zarządzanie życiem DbContext

Nie powinno się stosować DbContextu w taki sposób, że operujemy na jednej instancji przez cały czas. Wszystkie operacje powinny być wykonywane na nowych instancjach DbContext. Operacje takie jak odświeżanie bądź cofanie, powinny być wykonywane jako stworzenie nowej instancji DbContext.

Ważna literatura: http://stackoverflow.com/questions/3653009/entity-framework-and-connection-pooling/3653392#3653392

Odnośniki

]]>
http://rpajak.com/obsluga-zmian-w-dbcontext/feed/ 1
[EF] Sekwencje w DbContext http://rpajak.com/sekwencje-w-dbcontext/ http://rpajak.com/sekwencje-w-dbcontext/#comments Tue, 10 Apr 2012 19:54:16 +0000 http://rpajak.com/blog/?p=109 Wstęp

DbContext (FluentAPI) nie wspiera mapowania właściwości do sekwencji bazodanowych. Z tego powodu, aby mieć możliwość z korzystania ze sekwencji należy zastosować jedno z możliwych obejść.

Przykładowe rozwiązania

public partial class CustomerEntities : DbContext
{
    public override int SaveChanges()
    {
        Database.Connection.Open();
        CustomerIdSequence();
        return base.SaveChanges(); ;
    }
        
    /// <summary>
    /// Wykonanie akcji podczas dodawania encji określonego typu.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="action"></param>
    protected void OnEntityAdd<T>(Action<T> action) 
        where T : class
    {
        var addedEntities = ChangeTracker.Entries<T>().Where(e => e.State == System.Data.EntityState.Added);
        foreach (var entity in addedEntities)
        {
            action(entity.Entity);
        }
    }

    /// <summary>
    /// Uzupełnia Id nowych encji.
    /// </summary>
    private void CustomerIdSequence()
    {
        OnEntityAdd<Customer>(customer => customer.Id = GetSequence("SYSADM.SEQ_CUSTOMER_PK"));
    }

    /// <summary>
    /// Zwraca kolejną wartość danej sekwencji.
    /// </summary>
    /// <param name="dbConnection"></param>
    /// <returns></returns>
    private string GetSequence(string sequenceName)
    {
        using (var command = Database.Connection.CreateCommand())
        {
            command.CommandText = string.Format("SELECT {0}.NEXTVAL FROM DUAL", sequenceName);
            return command.ExecuteScalar().ToString();
        }
    }
}

Uwaga

Na marginesie dodam, że sekwencje nie podlegają mechanizmowi transakcji, dlatego nie warto próbować, aby pobieranie wartości sekwencji było we wspólnej transakcji wraz z innymi operacjami.

]]>
http://rpajak.com/sekwencje-w-dbcontext/feed/ 0
[WinForms] Zapamiętanie edycji komórek w DataGridView http://rpajak.com/zapamietanie-edycji-komorek-w-datagridview/ http://rpajak.com/zapamietanie-edycji-komorek-w-datagridview/#comments Thu, 08 Mar 2012 22:16:39 +0000 http://rpajak.com/blog/?p=95 Poniżej przedstawię sposób w jaki możemy zapamiętać zmieniające się wartości pól w kontrolce DataGridView

Potrzebne nam będzie:

  • pole do zapamiętania zmieniającej się wartości
  • zapamiętanie wartości podczas zdarzenia CurrentCellDirtyStateChanged dla warunku (DataGridView)sender.IsCurrentRowDirty
  • odnotowanie zmiany w zdarzeniu CellValueChanged

Przykład:

private string oldValue;

void dgvElement_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
  DataGridView dgv = sender as DataGridView;
  if (dgv.IsCurrentRowDirty)
  {
    // zapamiętanie stanu przed edycją wiersza
    Element editedItem = dgv.CurrentRow.DataBoundItem as Element;
    oldValue = editedItem.Value;
  }
}

void dgvElement_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
  DataGridView dgv = sender as DataGridView;
  Element editedItem = dgv.CurrentRow.DataBoundItem as Element;
  
  // oldValue zawiera wartość elementu przed zmianą
  // editedItem.Value zawiera wartość elementu po zmianie
}
]]>
http://rpajak.com/zapamietanie-edycji-komorek-w-datagridview/feed/ 0