Pokazywanie postów oznaczonych etykietą group. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą group. Pokaż wszystkie posty

poniedziałek, 15 grudnia 2014

[C#|Visual Studio] ASP .NET SignalR: Hubs

Kod serwerowy można pisać na dwóch poziomach abstrakcji. Niskopoziomowo możemy wykorzystać tzw. Persistent Connection, natomiast prostszym modelem, wystarczającym do większości potrzeb są Huby. Hub można postrzegać jako zbiór metod wystawionych przez serwer do dwukierunkowej komunikacji z klientem. Nazwy metod powinny odpowiadać biznesowym operacjom, dlatego zasadna wydaje się analogia do kontrolerów ze wzorca MVC. Nie powinniśmy nigdzie w kodzie przechowywać referencji do naszych hubów - nie mamy kontroli nad ich cyklem życia, a zatem nie warto także przechowywać stanu bezpośrednio w hubie. Każda publiczna metoda może być wołana z zewnątrz (zostanie dodana do dynamicznie generowanego klienckiego proxy). Serwer może też wołać dowolną metodę kliencką dzięki Dynamic Language Runtime. Wywołanie takie sprowadzi się do serializacji metody i jej parametrów. Domyślnym serializatorem jest JSON.NET.

[HubName("modernChat")]
public class ModernChatHub : Hub
{
    public void SendMessage(string message)
    {
        Clients.All.newMessageFrom(Context.ConnectionId);
        Clients.Caller.acknowledged();
        Clients.Others.postMessage(message);
    }

    public void Subscribe(string groupName)
    {
        Groups.Add(Context.ConnectionId, groupName);
    }

    public void Unsubscribe(string groupName)
    {
        Groups.Remove(Context.ConnectionId, groupName);
    }

    public void Hello(string groupName)
    {
        var msg = string.Format("Welcome from {0}",
            groupName);
        Clients.Group(groupName).greetings(msg);
    }

    public override Task OnConnected()
    {
        var dependencyResolver = GlobalHost.DependencyResolver;
        var connectionManager = dependencyResolver.Resolve<IConnectionManager>();
        var hubContext = connectionManager.GetHubContext<ModernChatHub>();
        var all = hubContext.Clients.All;
        all.connected(Context.ConnectionId);
        return base.OnConnected();
    }
}

Mamy do dyspozycji kilka możliwości przetwarzania wiadomości. Możemy skorzystać z property Clients, wysyłając wiadomości do nadawcy, do wszystkich, do wszystkich poza nadawcą. Można także selektywnie wybierać odbiorców po id połączenia. Property Context zawiera także cookies, tożsamość użytkownika czy query string.

Kolejnym modelem są grupy agregujące użytkowników wg pewnego klucza. Trzeba pamiętać, że SignalR w żaden sposób nie przechowuje stanu po stronie serwera, co otwiera znakomite możliwości skalowalności, ale też nie zapamięta nam tego, kto był w której grupie przy restarcie aplikacji. Grupa jest zatem jedynie zrzeszeniem kilku połączeń. Grupy są tworzone w sposób dynamiczny (jeżeli łączymy się do grupy, która nie istnieje, to zostanie założona). Jedno połączenie może być skojarzone z wieloma grupami.

Ostatni przykład, to zdarzenia, do których mamy dostęp z poziomu Hub-a, takie jak OnConnected, OnDisconnected oraz OnReconnected. Wewnątrz nich możemy wołać dowolny kod. Jeżeli chcemy notyfikować podłączonych klientów spoza Huba, możemy skorzystać z DependencyResolvera (odpowiednik ServiceLocatora).

wtorek, 22 kwietnia 2014

[C#|Visual Studio] LINQ: join

Łączenie dwóch kolekcji w LINQ można wykonać na kilka sposobów:
  • inner join
  • group join
  • left outer join
Podstawowy i najczęściej używany sposób to inner join, czyli po prostu zwykłe złączenie kolekcji, przy czym jeżeli istnieją elementy, które nie mają swoich odpowiedników w drugiej kolekcji, to zostaną pominięte.

internal class Team
{
    public int Id { get; set; }
    public string Name { get; set; }
}

internal class Player
{
    public int Id { get; set; }
    public int TeamId { get; set; }
    public string Name { get; set; }
}

//...

var teams = new Team[3];
for (int i = 0; i < teams.Length; i++)
{
    teams[i] = new Team(){Id = i};
}

teams[0].Name = "Liverpool";
teams[1].Name = "Chelsea";
teams[2].Name = "Arsenal";

var players = new Player[3];
for (int i = 1; i <= players.Length; i++)
{
    players[i-1] = new Player() { Id = i - 1, TeamId =  i};

}

players[0].Name = "Hazard";
players[1].Name = "Ramsey";
players[2].Name = "Rooney";

var inner = from player in players
            join team in teams on player.TeamId equals team.Id
            select new
            {
                player.Name,
                TeamName = team.Name
            };

Console.WriteLine("Inner join:");
foreach (var result in inner)
{
    Console.WriteLine("{0}, {1}", result.Name, result.TeamName);
}    

Wypisane zostaną tylko dwa elementy, te, których indeksy TeamId pokrywają się z identyfikatorami drużyn (player[0], player[1])

Group join to konstrukcja sprowadzająca wynik do niepłaskiej struktury przypominającej wynik operacji group by. Operacja ta związana jest ze słowem kluczowym into, a jej wynik jest typu IEnumerable<IEnumerable<T>>. Możemy na tym oczywiście wywołać select spłaszczający wyniki.

var group = from team in teams
            join player in players on team.Id equals player.TeamId
                into playersWithTeams
            select new { Key = team.Name, Count = playersWithTeams.Count() };

foreach (var item in group)
{
    Console.WriteLine("{0}, {1}", item.Key, item.Count);
}

Left outer join przydaje się tam, gdzie z jakichś powodów chcemy dostać wszystkie elementy z kolekcji będącej lewą stroną operacji. Problemem jest, jak przedstawić elementy, które nie mają swojego odpowiednika w kolekcji, będącej prawą stroną join-a. W takim przypadku LINQ dostarcza extension metodę DefaultIfEmpty<T>, gdzie możemy podać, jak ma wyglądać taki "sztuczny" element.

var outer = from player in players
            join team in teams on player.TeamId equals team.Id
                into playersWithTeams
            from item in playersWithTeams.DefaultIfEmpty(new Team() {Name = "<Empty>"})
            select new
                       {
                           item.Name,
                           Player = player.Name
                       };

foreach (var item in outer)
{
    Console.WriteLine("{1}, {0}", item.Name, item.Player);
}            

Zostaną wypisane wszystkie trzy elementy kolekcji players.

sobota, 25 sierpnia 2012

[WPF] ICollectionView

WPF oferuje mechanizm wspierający podstawowe standardy dotyczące wyświetlania kolekcji, takie jak filtrowanie, sortowanie czy grupowanie. Mechanizm jest następujący : w momencie, gdy następuje data binding do obiektu typu IEnumerable, tworzony jest pomiędzy źródłem a celem tworzony jest niejawnie widok. Jest nim obiekt implementujący interfejs ICollectionView. Wspiera on wspomniane wcześniej operacje.
  • Sortowanie
Tworzymy obiekt klasy SortDescription, gdzie jako parametry konstruktora podawane jest property, po którym ma się odbyć sortowanie, oraz kierunek (rosnąco lub malejąco).


var sort1 = new SortDescription("Surname", ListSortDirection.Descending);   
c1.Items.SortDescriptions.Add(sort1);

  • Grupowanie
Aby móc w pełni wykorzystać grupowanie, należy nieco rozszerzyć wygląd ListBoxa o to, jak prezentowany będzie nagłówek grupowania.

<ListBox.GroupStyle>
    <GroupStyle>
        <GroupStyle.HeaderTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal">
                    <Label Content="Age = " />
                    <Label Content="{Binding}"/>
                </StackPanel>                                
            </DataTemplate>
        </GroupStyle.HeaderTemplate>
    </GroupStyle>
</ListBox.GroupStyle>

Po dodaniu wyglądu nagłówka, można przejść do dodania mechanizmu grupującego. Najbardziej przydatne wydaje się być grupowanie po property.


var group1 = new PropertyGroupDescription("Age");
c2.Items.GroupDescriptions.Add(group1);

  • Filtrowaanie
 Aby móc filtrować kolekcję należy stworzyć delegata zwracającego wartość logiczną.


c3.Items.Filter = o =>
                        {
                            var p = o as Person;
                            if (p == null)
                                return false;
                            return p.Age > 27;
                        };