Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 | Suche | FAQ

Hauptmenü
myCSharp.de
» Startseite
» Forum
» Suche
» Regeln
» Wie poste ich richtig?

Mitglieder
» Liste / Suche
» Wer ist online?

Ressourcen
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Microsoft Docs

Team
» Kontakt
» Cookies
» Spenden
» Datenschutz
» Impressum

  • »
  • Portal
  • |
  • Mitglieder
Beiträge von BlackMatrix
Thema: Mit welchem Pattern kann ich generische Links zu versch. (Finanz)Websites herstellen?
Am im Forum: Rund um die Programmierung

Deine Implementierung mit string.Format funktioniert aber eben nur dann, wenn es einen einzigen Parameter gibt, der in die URL eingefügt werden muss. Spätestens, wenn die Heimatbörse nicht wie bei Yahoo Finance an der gleichen Stelle eingefügt werden muss, wird das Prinzip auch schon wieder gebrochen.

Deine Argumentation zur Symbolklasse verstehe ich nicht. Der Sinn der Klasse ist es doch genau für jeden der ein Symbol braucht, sei es als Parameter oder in irgendeiner Implementierung, dass er davon ausgehen kann ein valides Symbol bereits vorliegen zu haben, was zumindest vom Format her bereits valide ist. Ein Symbol ist ein 1-5 stelliger String (zumindest ist mir noch kein anderes vorgekommen). Da du dich auf Yahoo beziehst, vermute ich, du meinst, dass an das Symbol noch die Heimatbörse gehangen wird. Das ist dann kein Symbol mehr, sondern eben eine weitere Datenklasse, die aus Symbol und Heimatbörse besteht und der generische Typparameter (TInput) bei meinem IStockWebsiteLinkCreator werden würde.

Deine Optionsinjection finde ich aber ein spannenendes Muster und muss ich mir mal genauer zu Gemüte führen :)

Thema: Mit welchem Pattern kann ich generische Links zu versch. (Finanz)Websites herstellen?
Am im Forum: Rund um die Programmierung

Das erachte ich nur als sinnvoll, wenn wirklich ein string.Format gebraucht wird und nicht nur für das Anhängen eines Strings.

Thema: Mit welchem Pattern kann ich generische Links zu versch. (Finanz)Websites herstellen?
Am im Forum: Rund um die Programmierung

@T-Virus

Ich glaube du hast das mit deinem Codebeispiel etwas falsch verstanden. Der Grundgedanke ist immer aus einem Eingabeparameter wie etwa dem Aktiensymbol den entsprechenden (klickbaren) Link zu unterschiedlichen Finanzwebseiten zu generieren. Das können so keine Propertys wie in deinem Bsp. sein. Die ISIN ist nur ein weiterer bzw. anderer Inputparameter. Er ersetzt das Aktiensymbol, welches normalerweise in 95% der Webseiten notwendig ist.
Im 2. Schritt sollen dann alle Implementierungen aufgesammelt werden um eine Liste aller Links anbieten zu können.

Ich habe nun eine geeignete, brauchbare Lösung gefunden, wie man zumindest die einzelne Linkgenerierung sauber aufbaut. Man nutzt mein generisches Interface von oben, implementiert diese aber nicht als string-Parameter aufgrund der Verwechslungsgefahr und dem zu generischen Parameternamen, sondern wrappt es in die eigentliche Isin und Aktiensymbol-Klasse und kann dann im Konstruktor zusätzlich gleich die Validierung vornehmen.

Hab das eben mal schnell runterprogrammiert:


public interface IStockWebsiteLinkCreator<TInput>
{
    Uri CreateLink(TInput input);
}

public class MarketWatchLinkCreator : IStockWebsiteLinkCreator<StockSymbol>
{
    Uri CreateLink(StockSymbol input)
    {
        return new Uri("https://www.marketwatch.com/investing/stock/" + input.Symbol);
    }
}

public class ComDirectLinkCreator : IStockWebsiteLinkCreator<Isin>
{
    Uri CreateLink(Isin input)
    {
        return new Uri("https://www.comdirect.de/inf/aktien/" + input.Isin);
    }
}

public class StockSymbol
{
    public StockSymbol(string symbol)
    {
        if (symbol.Length < 1 || symbol.Length > 5)
        {
            throw new FormatException(nameof(symbol));
        }

        Symbol = symbol;
    }

    public string Symbol { get; set; }
}

public class Isin
{
    public Isin(string isin)
    {
        if (/* Validierung */)
        {
            throw new FormatException(nameof(symbol));
        }

        Isin = isin;
    }

    public string Isin { get; set; }
}

Finde das sehr sauber. Nun muss nur noch ein schöner Weg für die Istanziierung gefunden werden.

Thema: Mit welchem Pattern kann ich generische Links zu versch. (Finanz)Websites herstellen?
Am im Forum: Rund um die Programmierung

Hallo,

entschuldigt den etwas unpräzisen Betreff des Threads, aber mir ist keine treffende Beschreibung eingefallen.

Ich versuche kurz meine Gedankengänge für den folgende Problem darzulegen und erhoffe mir ein paar Anregungen eurerseits, da ich schon des Öfteren auf ähnliche Themen gestoßen bin.

Im aktuellen Fall gilt es Links zu unterschiedliche Finanzwebseiten bereitzustellen. Dabei ist der Eingabeparameter in den meisten Fällen ein Aktiensymbol, dass zu einem Link gewandelt werden soll. So wird aus MSFT (Aktiensymbol für die Microsoft Aktie) bei der Webseite Marketwatch zu https://www.marketwatch.com/investing/stock/msft.

Im einfachsten Fall würde man eine statische Methode anbieten, die die Erstellung macht:

public static class MarketWatchLinkCreator
{
    public static Uri CreateLink(string symbol)
    {
        return new Uri("https://www.marketwatch.com/investing/stock/" + symbol);
    }
}

Nun ist das zwar ganz nett, weil man keine Instanzen erzeugen muss für die Linkgenerierung, aber es wäre ja auch schön, wenn man weitere Finanzwebseiten unter einer einheitlichen Schnittstelle generieren könnte. So könnte man SeekingAlpha in dem Gerüst unterbringen:

public interface ISymbolLinkCreator
{
     Uri CreateLink(string symbol);
}

public class SeekingAlphaLinkCreator : ISymbolLinkCreator
{
    public Uri CreateLink(string symbol)
    {
        return new Uri("https://seekingalpha.com/symbol/" + symbol);
    }
}

Jetzt gibt es aber Webseiten, wo das Aktiensymbol alleine nicht ausreicht. Man braucht dort bspw. noch die Heimatbörse um einen Link generieren zu können. Jetzt könnte man weitere Parameter entweder über den Konstruktor der Implementierung reinreichen (finde ich unsauber), oder man erweitert ISymbolLinkCreator um einen generischen Parameter.

public interface ISymbolLinkCreator<TInput>
{
     Uri CreateLink(TInput input);
}

sodass TInput in diesem speziellen Fall eine Klasse aus Symbol und Heimatbörse wird.

Hat meiner Meinung nach einige Nachteile, ich habe irgendwie eine zu generische Benamung des Parameters und darf den auch laut einigen Code-Analyzern nicht in der Implementierung umbenennen. Ein "input" bleibt ein Input und wird besonders dann kritisch, wenn man z.B. zu Comdirect kommt. Dort kann man auch so einen Link generieren und man kann das gleiche Interface

ISymbolLinkCreator<string>
implementieren, jedoch ist dort "input" nicht das Aktiensymbol wie in 95% der anderen Fälle, sondern die ISIN. Das scheint für mich als Aufrufer verwirrend. Weiterhin bleibt die Instanzierung, die ich zum Schluss ansprechen möchte.

Angenommen, ich möchte alle Linkgeneratoren instanzieren und habe alle Daten da, wie würdet ihr eine Implementierung vornehmen um sowas zu vermeiden:


public class LinkCreator
{
    private readonly IDataService _dataService;

    public LinkCreator(IDataService dataService)
    {
        _dataService = dataService;
    }

    public IReadOnlyCollection<Uri> GetLinks(string symbol)
    {
        var links = new List<Uri>();

        var seekingAlphaLink = new SeekingAlphaLinkCreator().CreateLink(symbol);
        var marketWatchLink = new MarketWatchLinkCreator().CreateLink(symbol);

        var isin = await _dataService.GetIsinAsync(symbol);

        var comdirectLink = new ComDirectLinkCreator().CreateLink(isin);

        var homeExchange = await _dataService.GetHomeExchangeAsync(symbol);

        var furtherLink = new FurtherLinkCreator().CreateLink(new ComplexInput
        {
            HomeExchange = homeExchange,
            Symbol = symbol
        });

        links.Add(seekingAlphaLink);
        links.Add(marketWatchLink);
        links.Add(comdirectLink);
        links.Add(furtherLink);

        return links;
    }
}

Gerade was die Instanziierung angeht, habe ich mir überlegt, ob man das evtl. über eine Factory-Methode mit generischen Parametern anbietet und dann Activor.CreateInstance verwendet, aber man hätte 2 Typparameter verwenden müssen, was mir weniger intutiv vorkommt.

Vielleicht habt ihr ja eine Meinung. Ich wäre sehr dankbar :)

Thema: Mit EF Core Concurrency auf Datenbankebene umsetzen?
Am im Forum: Datentechnologien

Wie meinst du das, ich verwende den eingebauten Mechanismus nicht? Ich muss sicherstellen, dass während die Entität verarbeitet wird

ProcessAsync
sie von keinem anderen Prozess auch verarbeitet wird. Und falls die Bearbeitung aus irgendeinem Grund hart abbricht, kann ich an dieser Bearbeitungszeit festmachen, dass ich sie nach einer gewissen Zeit wieder entsperren kann. Wenn nun die Entität aus der DB geholt wird, sollte das aber auch immer nur einer tun, daher hab ich ProcessingDateTime auch als IsConcurrencyToken definiert.

Thema: Mit EF Core Concurrency auf Datenbankebene umsetzen?
Am im Forum: Datentechnologien

Die Seite ist mir bekannt.

Ich hätte jetzt eigentlich nicht vermutet, dass ich die Spalte, die für den Start des Bearbeitungszeitpunkts ist, zusätzlich auch noch als Concurrency Token verwenden kann. Ich bin davon ausgegangen, dass zunächst alle SQL Statements abgefeuert werden und am Ende mit einem Select-Statement gegengeprüft wird, ob die Spalte noch den Wert hat, den es am Beginn der Transaktion ausgelesen hat. Es wird aber ein Select-Statement abgefeuert und nur im Update-Statement gegengeprüft. Ich frage mich, ob das dann threadsafe ist?


SELECT "x"."Id", "x"."ProcessingDateTime"
FROM "Entities" AS "x"
WHERE "x"."ProcessingDateTime" = '0001-01-01 00:00:00'
LIMIT 1

UPDATE "Entities" SET "ProcessingDateTime" = '2018-11-03 22:23:34'
WHERE "Id" = 1 AND "ProcessingDateTime" = '0001-01-01 00:00:00';
SELECT changes();

Leider konnte ich es keine DbUpdateConcurrencyException provozieren, vermutlich weil SQLite Lockingmechanismen auf Dateiebene vornimmt. Aber wäre das so korrekt? Den Transaktionscope bei dem Save kann ich ja weglassen, weil nur eine Operation, oder?


namespace ConsoleApp
{
    internal class Program
    {
        private static readonly Logger Log = LogManager.GetCurrentClassLogger();

        private static void Main(string[] args)
        {
            Log.Info("Start...");

            const int threadCount = 100;

            var threads = Enumerable.Range(0, threadCount).Select(_ => new Thread(async () =>
            {
                while (true)
                {
                    var entity = await GetAsync();
                    if (entity != null)
                    {
                        await ProcessAsync(entity);

                        await SaveAsync(entity);
                    }
                }
            }));

            foreach (var thread in threads)
            {
                thread.Start();
            }

            Console.ReadKey();
        }

        private static async Task SaveAsync(Entity entity)
        {
            using (var context = new DataContext())
            {
                context.Entities.Attach(entity);

                entity.ProcessingDateTime = DateTime.MinValue;

                await context.SaveChangesAsync();
            }
        }

        private static async Task ProcessAsync(Entity entity)
        {
            // Processing...
        }

        private static async Task<Entity> GetAsync()
        {
            // Entered by multiple threads...

            using (var context = new DataContext())
            {
                using (var transaction = await context.Database.BeginTransactionAsync())
                {
                    while (true)
                    {
                        try
                        {
                            var entity = await context.Entities.FirstOrDefaultAsync(x => x.ProcessingDateTime == DateTime.MinValue);
                            if (entity == null)
                            {
                                return null;
                            }

                            entity.ProcessingDateTime = DateTime.Now;

                            await context.SaveChangesAsync();

                            transaction.Commit();

                            return entity;
                        }
                        catch (DbUpdateConcurrencyException)
                        {

                        }
                    }
                }
            }
        }

        public class DataContext : DbContext
        {
            public DbSet<Entity> Entities { get; set; }

            protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
            {
                optionsBuilder.UseSqlite("Data Source=data.db");
            }

            protected override void OnModelCreating(ModelBuilder modelBuilder)
            {
                base.OnModelCreating(modelBuilder);

                modelBuilder
                    .Entity<Entity>()
                    .Property(x => x.ProcessingDateTime)
                    .IsConcurrencyToken();
            }
        }

        public class Entity
        {
            public int Id { get; set; }
            public DateTime ProcessingDateTime { get; set; }
        }
    }
}

Thema: Mit EF Core Concurrency auf Datenbankebene umsetzen?
Am im Forum: Datentechnologien

verwendetes Datenbanksystem: EF Core 2.1

Ich möchte gerne meine Tabelle, auf die mehrere Prozesse zugreifen können, durch einen Concurrency Mechanismus erweitern.

Ich frage mich gerade, ob ich für das folgende Szenario zwei Spalten benötige oder ob das auch mit einer gelöst werden kann.

1. Es sollen Daten aus der Tabelle geholt werden, die beim Abrufen mit einem "in Bearbeitung Zeitstempel" markiert werden sollen.
2. Dann folgt die Bearbeitung der Daten.
3. Nach Abschluss wird dieser Zeitstempel zurückgesetzt.

Kann ich jetzt an dieser einen Spalte den Bearbeitungszeitpunkt + optimistischen Concurrency Check festmachen, wenn 1. in einer Transaktion stattfindet oder brauche ich dafür eine weitere Spalte?
Ein Service ist zwar dazwischengeschaltet, aber sollte man das nicht eher auf Datenbankebene abhandeln, falls dieser mal irgendwie abstürzt?

Viele Grüße

Thema: Wie entwickle ich ein Plugin-System mit Dependency Injection?
Am im Forum: Rund um die Programmierung

Hallo.

Ich möchte mir ein Plugin-System zusammenbauen bei dem ich Funktionalität hinzufügen kann. Da stellt sich mir die Frage wie man das unter heutigen Gesichtspunkten entwickelt? Ich habe viel über Dependency Injection gelesen und bin mir ziemlich sicher, dass das auch da Anwendung findet. Jedoch lass ich mich gerne eines besseren Belehren.

Ich habe mir das wie folgt gedacht:


using Microsoft.Extensions.DependencyInjection;
// ...

namespace Bootstrapper
{
    internal class Program
    {
        private static void Main()
        {
            var serviceCollection = new ServiceCollection();

            foreach (var plugin in LoadPlugins())
            {
                plugin.Initialize(serviceCollection);
            }

            var provider = serviceCollection.BuildServiceProvider();

            foreach (var activity in provider.GetServices<IActivity>())
            {
                activity.Execute();
            }
        }

        private static IEnumerable<IPlugin> LoadPlugins()
        {
            return Directory
                .EnumerateFiles("Plugins", "*.dll")
                .Select(LoadPlugin<IPlugin>);
        }

        public static T LoadPlugin<T>(string path)
        {
            var assembly = Assembly.LoadFrom(path);

            foreach (var type in assembly.GetExportedTypes())
            {
                if (type.GetInterfaces().Contains(typeof(T)))
                {
                    return (T)Activator.CreateInstance(type);
                }
            }

            return default(T);
        }
    }
}


    namespace PluginAScope
    {
        public class PluginA : IPlugin
        {
            public void Initialize(IServiceCollection serviceCollection)
            {
                serviceCollection.AddSingleton<IActivity, ActivityA>();
            }
        }

        public class ActivityA : IActivity
        {
            public void Execute()
            {
                Console.WriteLine("ActivityA");
            }
        }
    }


    namespace PluginBScope
    {
        public class PluginB : IPlugin
        {
            public void Initialize(IServiceCollection serviceCollection)
            {
                serviceCollection.AddSingleton<IActivity, ActivityB>();
            }
        }

        public class ActivityB : IActivity
        {
            public void Execute()
            {
                Console.WriteLine("ActivityB");
            }
        }
    }


    namespace Contracts
    {
        public interface IPlugin
        {
            void Initialize(IServiceCollection serviceCollection);
        }

        public interface IActivity
        {
            void Execute();
        }
    }

Wie ist das zu bewerten? Ist das so angedacht? Gibt es vielleicht auch schon komplette Frameworkx, die da einem noch weitere Arbeit abnehmen? Ich entwickle als NETStandard Bibliothek, daher auch die Microsoft.Extensions.DependencyInjection Abhängigkeit. Ist ein ServiceLocator das gleiche wie ein ServiceProvider in diesem Namespace? Welche Services lege ich dann alle in den ServiceProvider, alle Datenbankservices, alle ViewModels, einfach alles? Glaube das wäre es erstmal :)

Vielen Dank für eure Antworten.

Thema: Dataflow LinkTo TransformBlock ist langsam
Am im Forum: Basistechnologien und allgemeine .NET-Klassen

Mit Setzen auf false ist das Problem damit behoben.

Widerspricht das nicht dem ganzen Konzept, wenn das standardmäßig true ist? Erst kann ich die Parallelisierung mittels MaxDegreeOfParallelism hochschrauben und dann warte ich doch auf den langsamsten Task?

Thema: Dataflow LinkTo TransformBlock ist langsam
Am im Forum: Basistechnologien und allgemeine .NET-Klassen

OMG! Lass mich raten mit ExecutionDataflowBlockOptions.EnsureOrdered ist das Problem behoben -.-

Wieso ist das standardmäßig true -.-

Thema: Dataflow LinkTo TransformBlock ist langsam
Am im Forum: Basistechnologien und allgemeine .NET-Klassen

Ich entschuldige mich, wenn ich mich falsch ausgedrückt habe.

Mir geht es halt darum, dass es mit der LinkTo-Variante zu lange dauert, bis die eigentliche Message/Daten in dem 2. Block bzw. Waitblock ankommen/entern. Anstatt eine fertige Message direkt an den WaitBlock weiterzuleiten, gibt es scheinbar einen sehr niedrig priorisierten Task, der dann mit einmal alle fertig bearbeiteten Messages des I/O-Blocks in den WaitBlock "schiebt". Weiß jemand wann das ausgeführt wird?
Die Reihenfolge ist mir egal, es ist mir aber wichtig, dass die Messages nicht so blockweise transferiert werden. (Nein, es ist nicht wegen des fixen Delays wie bei Stackoverflow gedacht wird). Mit dem direkten Aufruf der Post-Methode kann ich dieses Problem umgehen, aber scheint mir wie gesagt ein Hack zu sein.

Thema: Dataflow LinkTo TransformBlock ist langsam
Am im Forum: Basistechnologien und allgemeine .NET-Klassen

Zitat von gfoidl
Zitat
ich konnte bei Stackoverflow leider noch keine passende Antwort
Kannst du dann wenigstens die andere Frage verlinken?

Kann ich :)

TPL Dataflow LinkTo TransformBlock is very slow

Hatte den Code oben mal mit Logeinträgen versehen:
https://pastebin.com/zRYYWDGx

Resultat:
https://pastebin.com/i3QLSc6Z

Es dauert eben bis zu 50 Sekunden ein fertiges Ergebnis des I/O Blocks an den Waitblock übergeben wird.
Zitat
2018-03-21 15:37:03.6352|DEBUG|Starter.StackoverFlowTest|Exit I/O block 29...|
2018-03-21 15:37:56.9446|DEBUG|Starter.StackoverFlowTest|Enter wait block 29...|

Thema: Dataflow LinkTo TransformBlock ist langsam
Am im Forum: Basistechnologien und allgemeine .NET-Klassen

Thema: Dataflow LinkTo TransformBlock ist langsam
Am im Forum: Basistechnologien und allgemeine .NET-Klassen

Also mir fehlt noch ein bisschen der Durchblick bei den Pipelines. Ich hab mir jetzt gedacht, es müssen 3 Buffer werden. Der 1. Buffer nimmt erstmal alle Datenelemente entgegen und ist unlimitert. Der 2. Buffer müsste der I/O Buffer werden, der dann auf 50 mittels bounded capacity begrenzt wird. Nun ist die Frage von welchem Datentyp dieser sein müsste. Entweder wieder ein BlockingCollection<Data>, der eben auf 50 limitert ist. Dann hätte man so etwas was ich im letzten Beispiel in der Methode ConsumeIOBuffer versucht hatte. Ich glaube da baue ich aber etwas nach, was eigentlich schon durch die BlockingCollection erreicht werden sollte. Oder aber man baut einen Buffer ala BlockingCollection<Task<...>>, dann hätte ich aber das Problem, dass ich nicht gezielt fertiggestellte/completed Tasks aus der BlockingCollection nehmen (Take) kann.
Übersehe ich noch etwas oder wo ist mein Denkfehler?

Thema: Dataflow LinkTo TransformBlock ist langsam
Am im Forum: Basistechnologien und allgemeine .NET-Klassen

Zitat von Abt
Ich sehe auch den Pipeline Pattern aus Architektur-sicht weit dem Dataflow überlegen, da ich viel isolierter arbeiten kann und das Skalieren viel einfacher umsetzbar ist.
Während ich bei Pipelines die Paralellisierung von Außen dynamisch bestimmen und kontrollieren kann, geht das bei DataFlow nur umständlich und auch nur von Innen.

Ja, wenn das der Fall wäre, dann müsste man sich tatsächlich über eine Alternative Gedanken machen. Dataflow funktioniert am besten, wenn man statische Blöcke hat, bei denen die der Parallelisierungsgrad fix ist.

Ich habe jetzt mal auf TPL Pipelines umgebaut und ich muss ganz ehrlich sagen, da ist viel Code dabei, den ich bei Dataflow eindeutig nicht schreiben muss. Beim Schreiben des Codes erinnert mich das ein wenig an meine zweite Variante mit dem direkten TransformBlock.Post anstatt dem Linken. Hinzu kommt, dass ich noch nicht den Fall implementiert habe, wenn die IO-Pipeline (Max. 50 Pipeline) mal nicht prall gefüllt ist. Scheinbar muss ich da noch einen weiteren Buffer einführen mit BoundedCapacities limitiert auf 50.

Ist denn der erstmal so angedacht mit TPL Pipelining und entspricht in etwa dem von oben oder siehst du da noch irgendwelche Fallstricke, die ich (noch) nicht sehe?


        public void Start()
        {
            var ioBuffer = new BlockingCollection<Data>();
            var waitBuffer = new BlockingCollection<Tuple<Data, MetaData>>();

            ConsumeIOBuffer(ioBuffer, waitBuffer);
            ConsumeWaitBuffer(waitBuffer, ioBuffer);

            foreach (var data in Enumerable.Range(0, 2000).Select(i => new Data(i)))
            {
                ioBuffer.Add(data);
            }
        }

        private static void ConsumeWaitBuffer(BlockingCollection<Tuple<Data, MetaData>> waitBuffer, BlockingCollection<Data> ioBuffer)
        {
            Task.Run(() =>
            {
                while (true)
                {
                    var dataMetaData = waitBuffer.Take();

                    var _ = WaitAndAddAsync(dataMetaData, ioBuffer);
                }
            });
        }

        private static async Task WaitAndAddAsync(Tuple<Data, MetaData> dataMetaData, BlockingCollection<Data> ioBuffer)
        {
            var data = dataMetaData.Item1;
            var metaData = dataMetaData.Item2;

            if (metaData.Repost)
            {
                await Task.Delay(TimeSpan.FromMinutes(1)).ConfigureAwait(false);
                ioBuffer.Add(data);
             }

            
        }

        private void ConsumeIOBuffer(BlockingCollection<Data> ioBuffer, BlockingCollection<Tuple<Data, MetaData>> waitBuffer)
        {
            Task.Run(async () =>
            {
                var tasks = new Dictionary<Task<MetaData>, Data>();

                const int maxDegreeOfParallelism = 50;

                while (true)
                {
                    var data = ioBuffer.Take();

                    var task = ReadAsync(data);

                    tasks[task] = data;

                    if (tasks.Count ≥ maxDegreeOfParallelism)
                    {
                        var finishedTask = await Task.WhenAny(tasks.Keys).ConfigureAwait(false);

                        var metaData = await finishedTask.ConfigureAwait(false);

                        var dataMetaData = new Tuple<Data, MetaData>(tasks[finishedTask], metaData);

                        waitBuffer.Add(dataMetaData);

                        tasks.Remove(finishedTask);
                    }
                }
            });
        }

Edit:

So wie ich das sehe, wenn man den Fall kleiner maxDegreeOfParallelism betrachtet, wird es richtig kompliziert. Jedes Hinzufügen eines Elements zum IOBuffer müsste erneut schauen, welcher Task fertig ist (Task.WhenAny) um ihn an den WaitBuffer weiterzugeben?
Zitat von Abt
Sehe wenig Nutzen derzeit am Dataflow Feature von TPL und würde es in meinen Projekten auch nicht verwenden.

Es würde mich ja schon mal interessieren, wie das in meinem Fall mit TPL Pipelines mit evtl. minimal größerem Aufwand bewerkstelligt werden könnte.

Thema: Dataflow LinkTo TransformBlock ist langsam
Am im Forum: Basistechnologien und allgemeine .NET-Klassen

Wo genau liegt jetzt der Unterschied zwischen den beiden? Auf den ersten Blick scheint mir TPL Pipelines veraltet und weniger "griffig". Auch scheint der Grad der Parallelisierung nicht mit wenig Aufwand gesetzt werden zu können?

Thema: Dataflow LinkTo TransformBlock ist langsam
Am im Forum: Basistechnologien und allgemeine .NET-Klassen

Zitat von Abt
Du baust hier irgendwie TPL Pipelining nach, kann das sein?
Wenn ja - warum?

Was meinst du mit nachbauen? Ich nutze sie?

System.Threading.Tasks.Dataflow
https://docs.microsoft.com/de-de/dotnet/standard/parallel-programming/walkthrough-creating-a-dataflow-pipeline

Thema: Dataflow LinkTo TransformBlock ist langsam
Am im Forum: Basistechnologien und allgemeine .NET-Klassen

Hallo,

ich konnte bei Stackoverflow leider noch keine passende Antwort zu einer ähnlichen Frage erhalten, daher probiere ich es hier.

Ich habe zwei TransformBlock, die in einem Kreislauf angeordnet sind und ihre Daten weiterreichen. TransformBlock 1 stellt einen Block dar, der I/O Operationen durchführt und auf maximal 50 parallel arbeitende Tasks beschränkt werden soll. Danach sollen die eingelesenen Daten + Metadaten des Einlesens an den nächsten Block weitergereicht werden, der dann aufgrund der Metadaten entscheidet, ob diese erneut (nach einer gewissen Wartezeit) in den I/O Prozess geschickt werden sollen oder aber ob sie aussortiert werden.
Der I/O Block ist auf 50 begrenz (MaxDegreeOfParallelism = 50), der Warte/Aussortierblock ist unlimitiert.
Nun habe ich das Phänomen, schicke (TransformBlock.Post) ich eine große Anzahl von Elementen in den I/O Block und sind fertig bearbeitet, habe ich festgestellt, dass es eine halbe Ewigkeit dauert, bis diese in den 2. Block weitergegeben werden. Ich habe es mit Logeinträgen mal beobachtet und gesehen, dass etwa 10 Minuten kein Eintrag "gelinkt" wird, dann aber gut 1000 Einträge mit einmal. Teilweise sind die Auswirkungen so groß, dass dann mein 1. Block unausgelastet ist, weil der 2. Block viel zu spät seine Daten erhält.

Normalerweise würde ich es so implementieren, weil es sich so "richtiger" anfühlt:


    public void Start()
    {
        _ioBlock = new TransformBlock<Data,Tuple<Data, MetaData>>(async data =>
        {
            var metaData = await ReadAsync(data).ConfigureAwait(false);

            return new Tuple<Data, MetaData>(data, metaData);

        }, new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 50 });

        _waitBlock = new TransformBlock<Tuple<Data, MetaData>,Data>(async dataMetaData =>
        {
            var data = dataMetaData.Item1;
            var metaData = dataMetaData.Item2;

            if (!metaData.Repost)
            {
                return null;
            }

            await Task.Delay(TimeSpan.FromMinutes(1)).ConfigureAwait(false);

            return data;

        }, new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = DataflowBlockOptions.Unbounded });

        _ioBlock.LinkTo(_waitBlock);
        _waitBlock.LinkTo(_ioBlock, data => data != null);
        _waitBlock.LinkTo(DataflowBlock.NullTarget<Data>());

        foreach (var data in Enumerable.Range(0, 2000).Select(i => new Data(i)))
        {
            _ioBlock.Post(data);
        }
    }

Aufgrund des beschriebenen Problems muss ich es aber so schreiben, damit es meiner Vorstellung entspricht. Dadurch werden die Daten schneller an den nächsten Block weitergeleitet. Ich finde aber, dass es eher ein "Hack" ist:


 public void Start()
    {
        _ioBlock = new ActionBlock<Data>(async data =>
        {
            var metaData = await ReadAsync(data).ConfigureAwait(false);

            var dataMetaData= new Tuple<Data, MetaData>(data, metaData);

            _waitBlock.Post(dataMetaData);

        }, new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 50 });

        _waitBlock = new ActionBlock<Tuple<Data, MetaData>>(async dataMetaData =>
        {
            var data = dataMetaData.Item1;
            var metaData = dataMetaData.Item2;

            if (metaData.Repost)
            {
                await Task.Delay(TimeSpan.FromMinutes(1)).ConfigureAwait(false);

                _ioBlock.Post(data);
            }
        }, new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = DataflowBlockOptions.Unbounded });

        foreach (var data in Enumerable.Range(0, 2000).Select(i => new Data(i)))
        {
            _ioBlock.Post(data);
        }
    }

Weiß jemand Rat?

Thema: Multithread Variable mittels Entity Framework speichern
Am im Forum: Datentechnologien

Hi.

Welchen Weg würdet ihr gehen um eine Multithreading Variable, die von vielen Threads beschrieben wird, bestmöglichst in die Datenbank zu schieben?
Ich nutze EF Core + Sqlite und bisher habe ich für so ein Vorhaben immer einen Timer verwendet, der mir alle x ms die aktuelle Variable in die Datenbank schreibt. Aber evtl. gibt es einen geeigneteren Weg? Das Öffnen und schließen eines Kontext bei einer Änderung der Variable scheint mir zu viel Overhead. Ich suche das Pendant zu


                var count = 0;
                Interlocked.Increment(ref count);

für den Datenbank Kontext.

Viele Grüße

Thema: Debug/Release Binaries referenzieren
Am im Forum: Entwicklungs- und Laufzeitumgebung (Infrastruktur)

Hallo Gemeinde,

ich frage mich gerade welche geeignete und vor allem aktuelle Wege es gibt um einen Release Build mit Release Builds der Binaries außerhalb der Solution zu bauen und Debug Builds mit den Debug Binaries.
Von allen Binaries bin ich der Entwickler und mir steht auch der Sourcecode zur Verfügung, jedoch sind es Kernkomponenten, die ich in einer seperaten Solution entwickle. Wenn ich Änderungen an der Kernkomponente vornehme, möchte ich auch gerne die aktuelle Version für mein Projekt ziehen.

Ich hatte überlegt, das über Nuget Packages zu regeln, aber ich vermute, da gehört einiges an Arbeit dazu und VSTS scheint das in der Free Version nicht anzubieten.

Ich bin noch etwas unerfahren was Buildprozesse angeht, evtl. könnt ihr mir weiterhelfen.

Vielen Dank.

Thema: Wie Task.WhenAll für eine Liste implementieren, sodass man die Liste nachher bearbeiten kann?
Am im Forum: Basistechnologien und allgemeine .NET-Klassen

Hallo,

mir fallen zwar ein paar Möglichkeiten ein wie man das Problem lösen könnte, aber irgendwie bin ich noch nicht auf eine Best practice Lösung im Internet gestoßen, obwohl ich das eigentlich als etwas Fundamentales erachte.
Task.WhenAll bietet nur Überladungen für IEnumerable<Task> bzw. feste Arrays ala Task[]
Wie würdest ihr Task.WhenAll für eine Liste implementieren, sodass man nachträglich Elemente hinzufügen bzw. entfernen kann?

Folgendes Codebeispiel mit erwartetem Debug.Assert (der so wie er jetzt dort steht, fehlschlägt)


        private async void Click()
        {
            var tasks = new ConcurrentBag<Task>(Enumerable.Range(0, 10).Select(i => Task.Delay(i*1000)));

            var thread = new Thread(() =>
            {
                Thread.Sleep(1000);
                tasks.Add(Task.Delay(30000));
            });
            thread.Start();

            var sw = Stopwatch.StartNew();

            await Task.WhenAll(tasks);

            sw.Stop();

            Debug.Assert(sw.ElapsedMilliseconds > 30000);
        }

Ich freue mich auf eure Ideen :)

Thema: Threadsafety bei unveränderlichem Dictionary und Zugriff mittels Key?
Am im Forum: Basistechnologien und allgemeine .NET-Klassen

Das ging ja fix. Vielen Dank :)

Thema: Threadsafety bei unveränderlichem Dictionary und Zugriff mittels Key?
Am im Forum: Basistechnologien und allgemeine .NET-Klassen

Hallo,

ich habe bisher folgendes Dictionary:


var dic = new Dictionary<string, ConcurrentBag<Person>>();
dic["key1"]=new ConcurrentBag<Person>();
dic["key2"]=new ConcurrentBag<Person>();

Von mehreren Threads aus, sollen nun die Personen mittels Key hinzugefügt werden. Die Anzahl der Keys im Dictionary bleibt unveränderlich. Es werden keine Einträge hinzugefügt oder gelöscht, nur die "Liste" zu dem jeweiligen Key wird aktualisiert.

Mein Zugriff innerhalb der Threads würde dann bspw. so aussehen:

dic["key1"].Add(person);

Kann ich, dadurch, dass die Dictionary-Keys unveränderlich sind auf ein einfaches Dictionary mittels Key zurückgreifen oder müsste ich auch hier ein ConcurrentDictionary verwenden?

Wozu ich auch im Netz nichts richtiges findet konnte, kann ich auf das ConcurrentBag mittels Contains zugreifen, wenn mir ein Snapshot der aktuellen Liste ausreicht oder sollte ich lieber TryPeek verwenden?

Thema: Schichtentrennung am Beispiel Entity Framework Core
Am im Forum: Datentechnologien

Hallo Community,

ich habe mal wieder eine Frage zu der Schichtentrennung mithilfe des Entity Frameworks. Ich habe mich nun dazu entschlossen das Entity Framework Core zu verwenden, aber meine Frage ist eher allgemeiner Natur. Sicher wurde auch die ein oder andere Frage auch schon mal hier im Forum beantwortet, aber so richtig zufriedenstellend war das für mich leider noch nicht.

Ich versuche gerade meine Anwendung sauber aufzutrennen. Korrigiert mich, wenn ich irgendetwas falsch mache.
Ich habe eine Datenschicht, die das EF benutzt. Für jede Tabelle, die später in der Datenbank erstellt werden soll, habe ich eine Entität angelegt. Das heißt auch, dass ich dabei auf Normalisierung geachtet habe. Zum Beispiel habe ich zwar eine "Personen"-Tabelle und damit auch eine "Person"-Entität, aber da z.B. der Vorname einer Person in eine eigene Tabelle soll um Redundanzen in der DB zu vermeiden, gibt in der PersonEntity auch eine FirstName-Property vom Type FirstNameEntity. Gleiches gilt für weitere Daten der Person, die ich gerne extra in Tabellen auslagern möchte. Vorname, Nachname, Adresse, Username, Mailadresse, Useragent, OS, ...

Dann habe ich eine über der Datenschicht liegende Schicht. Nennen wir sie Geschäftslogik. Diese ist dafür zuständig, dass Personen zu richtigen Person-Objekten werden. D.H. auch wenn in der Datenschicht der Vorname bswp. in einer eigenen Tabelle ausgelagert ist, hat die richtige "CLR-Person" nur einen String als Vornamen und hat auch keine Navigationproperties mehr, die in der Datenschicht zum Einsatz kommen. In dieser Schicht wird auch die Funktionalität bereitgestellt bswp. CLR-Personen hinzuzufügen.

Die letzte Schicht würde ich als ViewModel-Schicht bezeichnen. Der Nutzer legt bspw. eine neue Person in der Oberfläche an, das ViewModel validiert die Eingaben und erzeugt letztendlich ein CLR-Personobjekt.

Nun wird die CLR-Person an die Geschäftslogik weitergereicht:


        public interface IPersonService
        {
             Task AddPersonsAsync(IEnumerable<Person> persons);
        }
        public class PersonService
        {
             public async Task AddPersonsAsync(IEnumerable<Person> persons)
             {
                // ...
             }
        }

Jedenfalls scheint es mir nun ein Krampf zu sein, die CLR-Person(en) in die Datenbank einzutüten. Und ich habe das Gefühl, dass ich mir damit all die Vorzüge vom EF verbaue, weil ich die Entitäten nicht direkt in meinen darüberliegenden Schichten verwende.

  using (var dataContext = new DataContext())
            {
                var alreadyTrackedLastNames = new List<LastNameEntity>();
                var alreadyTrackedFirstNames = new List<FirstNameEntity>();
                var alreadyTrackedOperatingSystems = new List<OperatingSystemEntity>();
                
                foreach (var person in persons)
                {
                    var personEntity = person.ToEntity();

                    // Firstname
                    var foundFirstName = alreadyTrackedFirstNames.FirstOrDefault(x => x.Gender == personEntity.FirstName.Gender && x.FirstName == personEntity.FirstName.FirstName);
                    if (foundFirstName == null)
                    {
                        foundFirstName = await dataContext.FirstNames.SingleOrDefaultAsync(x => x.Gender == personEntity.FirstName.Gender && x.FirstName == personEntity.FirstName.FirstName);
                    }
                    if (foundFirstName == null)
                    {
                        alreadyTrackedFirstNames.Add(personEntity.FirstName);
                    }
                    else
                    {
                        personEntity.FirstName = foundFirstName;
                    }

                    // Lastname
                    var foundLastName = alreadyTrackedLastNames.FirstOrDefault(x => x.LastName == personEntity.LastName.LastName);
                    if (foundLastName == null)
                    {
                        foundLastName = await dataContext.LastNames.SingleOrDefaultAsync(x => x.LastName == personEntity.LastName.LastName);
                    }
                    if (foundLastName == null)
                    {
                        alreadyTrackedLastNames.Add(personEntity.LastName);
                    }
                    else
                    {
                        personEntity.LastName = foundLastName;
                    }                   

                    // OperatingSystem
                    var foundOperatingSystem = alreadyTrackedOperatingSystems.FirstOrDefault(x => x.Value == personEntity.OperatingSystem.Value);
                    if (foundOperatingSystem == null)
                    {
                        foundOperatingSystem = await dataContext.OperatingSystems.SingleOrDefaultAsync(x => x.Value == personEntity.OperatingSystem.Value);
                    }
                    if (foundOperatingSystem == null)
                    {
                        alreadyTrackedOperatingSystems.Add(personEntity.OperatingSystem);
                    }
                    else
                    {
                        personEntity.OperatingSystem = foundOperatingSystem;
                    }

                    // Address
                    var foundAddress = await dataContext.Addresses.SingleOrDefaultAsync(x => string.Equals(x.Street, person.Address.Street, StringComparison.OrdinalIgnoreCase) &&
                                                                                             string.Equals(x.HouseNumber, person.Address.HouseNumber, StringComparison.OrdinalIgnoreCase) &&
                                                                                             string.Equals(x.ZipCode, person.Address.ZipCode, StringComparison.OrdinalIgnoreCase) &&
                                                                                             string.Equals(x.City, person.Address.City, StringComparison.OrdinalIgnoreCase) &&
                                                                                             string.Equals(x.CountryCode, person.Address.CountryCode, StringComparison.OrdinalIgnoreCase));
                    if (foundAddress != null)
                    {
                        personEntity.Address = foundAddress;
                        personEntity.Address.Person = personEntity;
                    }

                    // ...

                    dataContext.Persons.Add(personEntity);
                }

                await dataContext.SaveChangesAsync();

Muss das so kompliziert sein? Irgendwie sehe ich nicht mehr so richtig durch und das ist nur ein Objekt von vielen das ich in die Datenbank überführen muss...

Viele Grüße

BlackMatrix

Thema: Fangen von Exceptions, die durch externe Prozesse ausgelöst werden?
Am im Forum: Basistechnologien und allgemeine .NET-Klassen

So wie sich das bei dir anhört, werde ich vermutlich auch wieder auf Tesseract als externen Prozess umschwenken.

Ich hatte die 3.02, die mit der aktuellen Version des genannten Wrappers ausgeliefert wird, am Anfang eigentlich ziemlich gute Erfahrungen gemacht, aber mittlerweile stürzt die Anwendung so häufig ab, dass es sich gar nicht mehr lohnt diesen einzusetzen.

Ich hab wie gesagt auch keine Ahnung wie ich da irgendwelche Exceptions abfangen könnte um meine Anwendung aufrecht zu erhalten. Der Fall, der im Moment ziemlich häufig auftritt, es fliegt irgendeine C++ Exception in der libtesseract302.dll

Es gab ja dann 2 verschiedene Versionen von Google. 1x die 3.03 und einmal die 3.04, wobei beide den Release Candidate Status aufweisten. Ich habe den Wrapper angepasst und die 3.04 eingebunden. Der hat mir dann direkt keine Exceptions mehr geworfen, war aber für meine Begriffe langsamer und das größte Problem, es enstand dann an einer komplett anderen Stelle eine OutOfMemoryException in meiner Anwendung. Nun hat Google die 3.04 als Release markiert und der Entwickler des Wrappers hat angekündigt, dass die nächste Version (v2.5) das neue Release einbinden wird. Darauf warte ich nun sehnsüchtig.

Thema: Fangen von Exceptions, die durch externe Prozesse ausgelöst werden?
Am im Forum: Basistechnologien und allgemeine .NET-Klassen

Hallo Forengemeinde,

ich nutze für meine Anwendung den Tesseract Wrapper für .NET, welches Tesseract (C++) in der Version 3.02 einbindet.

Nun kommt es sporadisch zu C++ und C# Exceptions und ich weiß nicht wie ich die fangen soll. Manchmal passiert es, dass ich Tesseract benutze und dann in einem komplett anderen Modul meiner C# Anwendung eine OutOfMemoryException bekomme, obwohl Tesseract nicht mehr läuft.
Der andere Fall ist, das ab und zu eine C++ Exception kommt und immer wenn die Anwendung abschmiert und ich den Debugger anhängen will in einer debugger_hook.c lande. Im Callstack sehe ich dann, dass die Exception wohl in der libtesseract302.dll geflogen ist. Ich hab nur keine Ahnung, wie ich diese Exception fangen soll, ob der Wrapper evtl. das Bild schon falsch an Tesseract weitergibt und deswegen die Exception kommt. Ich würde bei so einem Fehler am liebsten nur den OCR Vorgang neustarten wollen. Die Fehler treten vorrangig bei Multithreading auf, Tesseract an sich ist multithreadingfähig.

Könnt ihr mir irgendwie helfen?

Viele Grüße

BlackMatrix

Thema: Async Task Pipelining
Am im Forum: Basistechnologien und allgemeine .NET-Klassen

Sollte das genannte Problem nicht ausschließlich durch eine geschickte Anwendung von async/await und Task.WhenAny und Task.WhenAll gelöst werden können?

Thema: TFS: Änderungen nach Offlinemode einchecken
Am im Forum: Entwicklungs- und Laufzeitumgebung (Infrastruktur)

Hallo,

ich habe an meinem Projekt jetzt lange Zeit außerhalb meines Heimrechners gearbeitet und hatte mir vor langer Zeit mal einen Stand des Quellcodes auf meinen anderen Rechner gezogen.
Meine erste Frage, wie deaktiviert man eigentlich auf dem 2. Rechner die Abfrage, ob man sich zu dem eigentlichen TFS Server verbinden möchte, wenn man die Projektmappe öffnet.

Und zweitens, wie checke ich die Änderungen am besten ein, wenn der alte und der neue Code sich auf meinem Heimrechner in unterschiedlichen Ordnern befindet und ich von dem Heimrechner aus einchecken möchte?

Viele Grüße

BlackMatrix

Thema: ViewModel Binding von readonly Dependency Property
Am im Forum: GUI: WPF und XAML

Hallo,

ich habe ein Control gebraucht, bei dem mein Nutzer die Farbe eines Pixels von einem Bild auswählen kann. Ich habe dazu etwas sehr brauchbares bei CodeProject gefunden.
Man wählt also ein Pixel im Bild aus und hat im SelectedColor Dependency Property, die Farbe des aktuell ausgewählten Pixels. Für meine Begriffe macht das auch Sinn, diese Property readonly zu machen, jedoch wie bindet man diese dann vernünftig an mein ViewModel?

Viele Grüße

BlackMatrix

Thema: GIF Animation bestmöglichst aus einem Bytearray laden?
Am im Forum: Grafik und Sound

Hallo,

ich habe ein Bytearray, welches eine GIF Animation repräsentiert und möchte gerne alle einzelnen Frames in einzelne Bitmaps laden. Nun, trotz geschlossenem Stream scheint das Bitmap keine Bearbeitung zu vertragen und wohl auch ein heiß diskutiertes Thema im Netz. Aber wie lade ich nun denn am besten so ein Bitmap um anschließend auch noch ungestört meine "Bearbeitungen" durchzuführen? In Anführungszeichen deshalb, weil ich eigentlich nur die aktiven Frames selektiere und ich dahinter eigentlich keine Bearbeitung vermute.

Hier mein Code, der einen GDI+ Fehler wirft:


		private static void Main(string[] args)
		{
			var frames = GetFramesAsync("http://upload.wikimedia.org/wikipedia/commons/2/2c/Rotating_earth_(large).gif").Result;
		}

		private static async Task<IEnumerable<Bitmap>> GetFramesAsync(string gifSource)
		{
			var client = new HttpClient();
			var bytes = await client.GetByteArrayAsync(gifSource);

			Image image;
			using (var ms = new MemoryStream(bytes, false))
			{
				image = Image.FromStream(ms);
			}

			var numberOfFrames = image.GetFrameCount(FrameDimension.Time);

			return Enumerable.Range(0, numberOfFrames).Select(i =>
			{
				image.SelectActiveFrame(FrameDimension.Time, i);
				return (Bitmap) image.Clone();
			}).ToArray();
		}

Viele Grüße

BlackMatrix