Laden...

Forenbeiträge von BlackMatrix Ingesamt 218 Beiträge

17.12.2020 - 15:27 Uhr

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 😃

17.12.2020 - 13:30 Uhr

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

17.12.2020 - 12:04 Uhr

@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.

16.12.2020 - 15:43 Uhr

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 😃

04.11.2018 - 11:21 Uhr

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.

03.11.2018 - 22:33 Uhr

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; }
        }
    }
}
03.11.2018 - 14:28 Uhr

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

28.03.2018 - 23:07 Uhr

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.

26.03.2018 - 11:48 Uhr

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?

26.03.2018 - 01:30 Uhr

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

Wieso ist das standardmäßig true -.-

26.03.2018 - 01:22 Uhr

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.

26.03.2018 - 00:23 Uhr

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.

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...|

24.03.2018 - 16:27 Uhr

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?

20.03.2018 - 15:39 Uhr

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?

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.

20.03.2018 - 00:48 Uhr

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?

19.03.2018 - 21:12 Uhr

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

19.03.2018 - 19:03 Uhr

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?

06.01.2018 - 22:36 Uhr

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

28.12.2017 - 12:28 Uhr

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.

17.10.2016 - 16:59 Uhr

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 😃

22.07.2016 - 12:58 Uhr

Das ging ja fix. Vielen Dank 😃

22.07.2016 - 12:40 Uhr

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?

11.07.2016 - 12:08 Uhr

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

26.08.2015 - 11:03 Uhr

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.

20.08.2015 - 14:06 Uhr

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

15.06.2015 - 16:53 Uhr

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?

09.06.2015 - 16:14 Uhr

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

03.06.2015 - 10:10 Uhr

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

28.05.2015 - 12:51 Uhr

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

21.05.2015 - 13:01 Uhr

Hallo,

ich möchte eine Pipeline entwickeln, die als Aufgabe die Bearbeitung von Bildern hat. Dabei gibt es 3 Tasks (LoadAsync, EditAsync, RecognizeAsync), wovon die ersten beiden Tasks mit maximaler Parallelität nacheinander abgearbeitet werden können. Der letzte Task darf nicht parallel abgearbeitet werden und darf nachdem die ersten beiden Tasks verarbeitet wurden, immer nur durch einen Thread abgearbeitet werden.
Wie erreiche ich das?

Ich gebe zu, dass meine async/await Kenntnisse noch nicht ausgereift sind, daher wäre es schön, wenn ihr mich auch auf allgemeine Fehler im folgenden Code hinweisen könntet.
Der Ablauf sieht so aus, dass der Nutzer Dateien (Paths) auswählen kann, dann die Möglichkeit optional eine Filtermethode auszuwählen (SelectedFilterMethod) und zu guter Letzt eine Analyse (RecognizeAsync) durchführen. Diese Methode darf immer nur von einem bearbeitet werden. Sobald eine Verarbeitung vollständig ist, soll der Path aus der Oberfläche gelöscht werden und das Ergebnis angezeigt werden.


	public class BatchViewModel : ViewModelBase
	{
		private readonly ObservableCollection<FilterMethodBase> _filterMethods = new ObservableCollection<FilterMethodBase>();
		private readonly ObservableCollection<ResultViewModel> _results = new ObservableCollection<ResultViewModel>();
		private readonly ObservableCollection<string> _paths = new ObservableCollection<string>();
		private RelayCommand _executeCommand;

		public ObservableCollection<ResultViewModel> Results
		{
			get { return _results; }
		}

		public ObservableCollection<string> Paths
		{
			get { return _paths; }
		}
		public ObservableCollection<FilterMethodBase> FilterMethods
		{
			get { return _filterMethods; }
		}

		public ICommand ExecuteCommand
		{
			get { return _executeCommand ?? (_executeCommand = new RelayCommand(ExecuteAsync)); }
		}

		private async void ExecuteAsync()
		{
			Results.Clear();

			var dic = Paths.ToDictionary(ProcessAsync);
			while (dic.Count > 0)
			{
				var task = await Task.WhenAny(dic.Keys);
				Paths.Remove(dic[task]);
				dic.Remove(task);

				var result = await task;

				Results.Add(result);
			}
		}

		private async Task<ResultViewModel> ProcessAsync(string path)
		{
			byte[] imageToProceed;
			byte[] edit = null;
			var source = await LoadAsync(path);

			if (SelectedFilterMethod == null)
			{
				imageToProceed = source;
			}
			else
			{
				edit = await EditAsync(source);
				imageToProceed = edit;
			}

			var result = await RecognizeAsync(imageToProceed);

			return new ResultViewModel
			{
				Source = GetImageSource(source),
				Edit = edit == null ? null : GetImageSource(edit),
				Result = result
			};
		}

		private async Task<byte[]> LoadAsync(string path)
		{
			using (var fs = new FileStream(path,FileMode.Open, FileAccess.Read, FileShare.Read,4096, true))
			{
				var bytes = new byte[fs.Length];
				await fs.ReadAsync(bytes, 0, bytes.Length);
				return bytes;
			}
		}

		private Task<byte[]> EditAsync(byte[] bytes)
		{
			return SelectedFilterMethod.ExecuteAsync(bytes);
		}

		private async Task<string> RecognizeAsync(byte[] bytes)
		{
			string result;

// Dieser Task darf keine Parallelität aufweisen

			return result;
		}
}

18.05.2015 - 10:53 Uhr

Mir geht es nicht um irgendwelche Obfuskatoren, die den Code unleserlich machen. Denn das hat mein Obfuskator schon so gut gemacht, dass mein Cracker da gar nicht weiter versucht hat, den Quellcode auszulesen, sondern ist direkt mit Debuggern rangegangen und hat wohl explizit nach Methoden gesucht, die true zurückliefern.
Nun hat er gemeint, man sollte mehrere Methoden erstellen, die das gleiche Ergebnis liefern, aber die Berechnung unterschiedlich lösen. Solche Dinge, die mir auch noch nicht so richtig klar sind und ich Hilfe benötige.

18.05.2015 - 10:45 Uhr

Hallo,

mir ist klar, dass es keinen 100 prozentigen Schutz gegen Cracker geben wird, schon gar nicht bei .NET Binaries, dennoch würde mich interessieren, wie man die eigene Anwendung möglichst effizient gegen solche Unternehmungen schützt. Das Erwerben einer Lizenz sollte kostengünstiger sein, als die Software zu cracken.

Eine unserer Software benötigt Internetverbindung um funktionieren zu können, daher hatte ich an ein Onlinelizenzsystem gedacht, welches beim Start der Anwendung eine verschlüsselte Verbindung zu unserem Server aufbaut, die Lizenz prüft und im Erfolgsfall eine SessionID an den Client (Software) zurückliefert. Die Features des Clients werden freigegeben und die SessionID wird alle x Minuten an den Server geschickt und es wird überprüft, ob noch andere mit dieser Lizenz online gehen.
Ich denke, die Vorgehensweise ist relativ sicher (falls nicht, korrigiert mich), aber das Problem ist beispielsweise die initiale Authentifizierung. Asynchron wird am Server geprüft, aber das einzige was mein Testcracker trotz obfusktierten Code gemacht hat, ist "Task<bool>" durch "Task<true>" zu ersetzen und schon waren die Features freigeschalten.

Irgendwelche Tipps?

Viele Grüße

BlackMatrix

29.04.2015 - 12:14 Uhr

Ohne virtual wird quasi eager-loading erzwungen, sowohl bei Navigation Properties wie auch bei Scalar Properties.
Würde mich ja doch sehr wundern, wenn das seit dem EF6 anders sein soll (auch wenn es einem POCO eher gerecht wird).

Das ist falsch. Das Weglassen von virtual sorgt dafür, dass Lazy Loading deaktivert wird, nicht aber, dass Eager Loading aktiviert wird.

https://msdn.microsoft.com/en-us/data/jj574232.aspx

22.04.2015 - 12:40 Uhr

Erst einmal vielen Dank für eure Antworten.

  1. [...] Ich nenne meine DB Entitäten zB "PersonEntity" und das Objekt, mit dem die Software dann in der Business-Logik arbeitet, dann zB "Person".

Das ist gut, das habe ich nämlich auch so gemacht. Mich stört allein schon eine ID irgendwelcher Objekte in meiner BL. Evtl. für die Suche mittels Kundennummer, aber grundsätzlich ist es mir in der BL z.B. egal welche ID die Adresse meiner Person hat. Allein aus diesem Grund gibt es bei mir Address und AddressEntity.

  1. Sehe ich das richtig, das ein Unique-Constraint über eine oder mehrere Spalten erst mit dem EF 6.1 eingeführt worden ist? Ich habe es nämlich so gelöst:
class IPEndPointEntityMap : EntityTypeConfiguration<IPEndPointEntity>
	{
		public IPEndPointEntityMap()
		{
			HasKey(x => x.Id);

			Property(x => x.Address)
				.IsRequired()
				.HasColumnAnnotation(IndexAnnotation.AnnotationName, new IndexAnnotation(new IndexAttribute("IX_IPEndPoint", 1) {IsUnique = true}));

			Property(x => x.Port)
				.IsRequired()
				.HasColumnAnnotation(IndexAnnotation.AnnotationName, new IndexAnnotation(new IndexAttribute("IX_IPEndPoint", 2) { IsUnique = true }));
		}

Das sorgt erstmal dafür, dass in die DB keine gleichen Objekte eingefügt werden können. Grundsätzlich empfiehlt es sich dann dennoch in der BL abzufragen, ob der IPEndPoint schon vorhanden ist, damit keine Exceptions fliegen.

Noch eine Frage zur Implementierung von Entitäten. Aus welcher Sicht heraus entwickle ich diese? Meine PersonEntity hat z.B. einen Vornamen. Nun könnte ich dieser einer string als Property spendieren, so wie es mein reales Personobjekt auch aufweist, oder aber ich lege eine FirstNameEntity an um auf Normalisierung in der DB zu achten und jeden Vornamen nur einmal in der Datenbank zu haben und diesen mittels ID zu referenzieren. Auf der anderen Seite habe ich aber gehört, dass man mit dem EF sich keine Gedanken mehr um die Datenbank machen braucht. Evtl. gibt es für solche Fälle auch wieder Eintstellmöglichkeiten im EF um keine extra Entität einzuführen. Wenn ja welche, aber letztendlich müsste ich, wenn ich eine FirstNamEntity einführe auch wieder ein Uniquekonstrukt (wie oben) für den Vornamen einführen (der wirklich erst mit EF 6.1 hinzugekommen sein soll)?

Viele Grüße

BlackMatrix

15.04.2015 - 14:42 Uhr

Ich habe ein paar unspezifische Fragen zum Schichtenmodell und dem Entity Framework.

Ich nutze EF 6.1.3 und das generische Repository Framework Framework. Das Framework bietet generische Basisklassen und Interfaces für Repository, Services und dem Unit of Work Pattern.

Das Grundprinzip hinter EF ist ja zunächst einmal die Daten einer Anwendung zu speichern. Ich habe in meiner .NET Anwendung CLR-Objekte, die ich mittels Entitäten abbilden will und dabei sollte es Aufgabe sein nur so viele Informationen abzuspeichern, dass man im Anschluss die CLR-Objekte nach Neustart wiederherstellen kann.

  1. Ich habe nun bei fast jedem CLR-Objekt gemerkt, dass ich in meiner Serviceschicht eigentlich immer Konvertierungen Entity <--> CLR-Objekt anbieten muss. Sieht man bei den Beispielen im Netz eigentlich nie, da wird direkt mit Entitäten gearbeitet und ich frage mich, ob ich hier evtl. was falsch mache.

  2. Wenn man die Entität IPEndPointEntity als Beispiel zu Rate zieht und möchte, dass in der Datenbank jeweils jede IP-Adresse + Port nur einmal vorhanden ist. Ist dies dann Aufgabe vom Entity Framework, dass der Constraint von IPAddress und Port Unique ist oder muss das in der Repositoryschicht abgefangen werden? Oder vielleicht sogar von beiden? Wie erreicht man dies im EF mittels FluentAPI?

  3. Wenn ich bereits Objekte in meiner Datenschicht habe und ich von außen neue hinzufügen bzw. updaten möchte, bei denen ich noch nicht weiß, ob exakt diese Objekte schon in der Datenbank vorhanden sind. Ist es dann Aufgabe der BL, dass zunächst das mögliche, bereits vorhandene Objekt geholt wird, um dieses dann upzudaten?

  4. Wenn ich ein CLR-Objekt habe, welches mehrere CLR-Objekte als Properties haben, die jeweils auch letztendlich auch als Entitäten in der DAL hinterlegt werden sollen, welche Schicht ich dann dafür zuständig, sodass diese dann korrekt in der Datenbank landen?

So, ich glaube das war es erstmal 😃

23.02.2015 - 12:00 Uhr

Ich danke euch.

ITK ist wohl aber C++. Also kommt das für mich nicht in Frage.

AForge.NET scheint schön dokumentiert zu sein. Bei OpenCV.NET müsste man sich wahrscheinlich an der C++ Doku festhalten. Eine .NET Doku konnte ich nicht finden.

20.02.2015 - 11:56 Uhr

Hallo,

da ich mich jetzt häufiger mit der programmatischen Bearbeitung von Bildern beschäftige, stellt sich mir die Frage, ob es schon irgendwelche ausgereiften Bibliotheken für verschiedene Bildalgorithmen gibt? Ich habe das Gefühl, dass es überhaupt relativ wenige Bibliotheken zu diesem Thema gibt, die einzige brauchbare, die ich bisher gefunden habe ImageProcessor

Trotzdem habe ich mir noch jede Menge manuell zusammengesucht oder selbst geschrieben, Floodfillen, Kontrast bestimmen, Farbverteilung bestimmen.

Kennt irgendjemand weitere gute Bibliotheken? Müssen nicht Hochperformant sein, nur möglichst korrekt.

Viele Grüße

BlackMatrix

23.01.2015 - 11:25 Uhr

Nun, die E-Mails sollen anschließend sowieso mittels Regex durchsucht werden können, daher wäre es von Vorteil, wenn man eben nicht alle E-Mails herunterläd, sondern eine gewisse Filterung auf Basis des Regex beim Server bereits auslösen kann. Zusätzlich bedarf es dann auch nur die Eingabe eines Pattern.

Wenn der Regex ausschließlich aus variablen (Meta-)Zeichen besteht, dann natürlich keine Filterung beim Server, alles andere vorzufiltern wäre schön.

Direkte Frage habe ich keine, mir ging es um den Ansatz. Ich hatte auch daran gedacht mir ein paar Codeteile über Reflection aus dem .NET Framework zu holen.

Evtl. gibt es auch schon irgendwo eine Funktion, die mir aus einem Pattern, die statischen Teile herauszieht.

Edit:

Ich glaube ich habe einen recht guten Ansatzpunkt gefunden. Der interne RegexParser stellt eine Methode Parse zur Verfügung. Dadurch wird der Regexbaum aufgespannt und jedes Element enthält den Wert, Anzahl der Wiederholungen (n,m) und einen Type. Damit könnten sich die statischen Anteile ermitteln lassen.

22.01.2015 - 12:03 Uhr

Es hängt wohl auch davon ab, in wie weit man den Regex auf IMAP-Ebene nachbauen möchte. Ich glaube es macht wenig Sinn, wenn man für "mycsharp\w" ein OR-Command für alle als "\w" definierten Zeichen (Buchstaben, Ziffern, ...) auslöst. Zumal sicherlich auch die Länge eines solchen Commands begrenzt ist.

Geht man davon aus, dass man nur '(', ')', '|', '{n}', '{n,m}', '+', ... als möglich anerkennt, bräuchte man, wie bei Regex einen Baum bei denen die Kinder ohne Kinder die unveränderlichen Patternteile als Teilcommand zusammenführen.

21.01.2015 - 13:36 Uhr

Hallo,

Ich habe einen Regex, den ich auf einen IMAP-Server loslassen will. Das IMAP Protokoll unterstützt keine regulären Ausdrücke, daher will ich einen möglichst guten Vorfilter entwickeln, der auf Basis eines Regex ein Searchcommand beim IMAP-Server veranlasst.

Um mal ein Beispiel zu nennen. Wird auf dem Client mit dem Pattern: "mycsharp|myexample" im Betreff der E-Mail gesucht, soll auf dem Server "OR SUBJECT mycsharp SUBJECT myexample" ausgeführt werden.

Mir ist klar, dass nicht die komplette Regexfunktionalität damit abgebildet werden kann, daher soll auch nur ein möglichst guter Filter für die in Frage kommende Mails erstellt werden.

Ich hab zwar schon eine Idee, wäre aber gerne für eure alternative Ansätze dankbar.

08.01.2015 - 13:11 Uhr

Ich glaube, das bringt mir relativ wenig, da der Code wie oben bereits erwähnt funktioniert und der Automapper mir das Ganze wahrscheinlich nur vereinfacht. Wenn ich aber das Ganze auf async/await umstelle, dann funktioniert, wie angedeutet, überhaupt nichts mehr, weil die komplette async-Funktionalität auf den primitiven Typen aufgebaut ist.

Es muss doch eine ganz einfache Möglichkeit geben, meinen ExtendedIPEndPoint als ExtendedIPEndPointEntity abzubilden. Die sind doch eigentlich das Gleiche. Ich mache diesen ganzen Hickhack doch nur, weil die IPAddress des IPEndPoint sich nicht serialisieren lässt und IPEndPoint keinen parameterlosen Konstruktor implementiert.

Mein Verständnis:
Die IPAddress in der BL soll beim Speichern eines ExtendedIPEndPoint im DAL als IPAddress.ToString() gemappt werden. Beim Laden muss aus IPAddressAsString und Port ein neues ExtendedIPEndPoint-Objekt erzeugt werden und die Properties übernommen werden.

08.01.2015 - 09:21 Uhr

Genau, es geht um die Konvertierung von IPEndPointEntity zum CLR-IPEndPoint und umgekehrt. IIPEndPoint habe ich nur aus diesem Grund eingeführt, weil ExtendedIPEndPoint schon von IPEndPoint ableitet.

Und das geht doch meiner Meinung nach nur, indem ich die einzelnen Properties zuweise oder wie bekomme ich sonst das Mapping hin?


IPEndPointEntity iPEndPointEntity = // ...

var endPoint = new ExtendedIPEndPoint(iPEndPointEntity.Address,iPEndPointEntity.Port);
endPoint .LastChecked=extendedIPEndPoint.LastChecked;
// ...

07.01.2015 - 17:34 Uhr

Das ist schon richtig, aber ob nun die #region Type conversion in einer eigenen Klasse/Layer unterbringe oder nicht ist erst einmal irrelevant.

Ich muss in jedem Fall IPEndPointEntity zu IPEndPoint wandeln und IPEndPoint zu IPEndPointEntity. Dafür muss ich die ganzen Properties des einen Objekts den Properties des andere zuweisen (AssignProperties). Oder wie soll sonst das "Mapping" funktionieren?

interface IIPEndPoint
	{
		DateTime LastAccess{ get; set; }
		bool IsOnline { get; set; }
		EndPointType Type { get; set; }
	}
namespace Data.DataAccess
{
	public class IPEndPointEntity:IIPEndPoint
	{
		[PrimaryKey, AutoIncrement]
		public int Id { get; set; }
		public string Address { get; set; }
		public int Port { get; set; }
		public DateTime LastAccess{ get; set; }
		public public bool IsOnline { get; set; }
		public EndPointType Type { get; set; }
	}
}

public class ExtendedIPEndPoint: IPEndPoint,IIPEndPoint
	{
		public ExtendedIPEndPoint(long address, int port) : base(address, port)
		{
		}

		public ExtendedIPEndPoint(IPAddress address, int port) : base(address, port)
		{
		}
		public DateTime LastAccess{ get; set; }
		public public bool IsOnline { get; set; }
		public EndPointType Type { get; set; }
	}

Mir scheint das irgendwie nicht richtig. Umständlich und hässlich. Hinzu kommt, dass wenn ich auf Async umsteige nicht wüsste, wie ich beispielsweise aus einem AsyncTableQuery<IPEndPointEntity> ein Task<IEnumerable<IPEndPoint>> oder vergleichbares bekomme ohne im Speicher eine Kopie beispielsweise über AsyncTableQuery.ToListAsync() zu erstellen.

Der theoretische Teil ist mir schon einigermaßen klar, aber das zu coden scheint mir nicht trivial, obwohl man meinen sollte, dass man sowas dutzende Mal im Netz finden müsste.

05.01.2015 - 13:43 Uhr

Puh, das haut mich ja jetzt um. Ich bin bisher immer davon ausgegangen, dass der Code nach dem await immer erst dann ausgeführt wird, wenn der Task abgeschlossen ist.

Das heißt:


            var client = new HttpClient();

            var response = await client.GetAsync("http://www.example.net/",_cts.Token).ConfigureAwait(false); // First access

            if (response.StatusCode == HttpStatusCode.OK)
            {
               var message = await client.PostAsync("http://www.example.net/", new StringContent("example=true"), _cts.Token).ConfigureAwait(false);
            }

würde definitiv nacheinander ausgeführt werden, aber bei


            var client = new HttpClient();

            var response = await client.GetAsync("http://www.example.net/",_cts.Token).ConfigureAwait(false); // First access

            if (response.StatusCode == HttpStatusCode.OK)
            {
            			//...
            }

            var message = await client.PostAsync("http://www.example.net/", new StringContent("example=true"), _cts.Token).ConfigureAwait(false);

muss das nicht der Fall sein, selbst dann nicht, wenn mir der erste Request Cookies liefert, die ich beispielsweise für den 2. Request brauche bzw. den Request erst möglich machen?

Also

var client = new HttpClient();

durch

var client = new HttpClient(new HttpClientHandler{CookieContainer = new CookieContainer()});

ersetze?

05.01.2015 - 12:43 Uhr

@Abt:

Habe ich etwas verpasst oder warum sollte HandleConfig gleichzeitig die Konfiguration behandeln? Das geschieht doch erst, nachdem die configuration geholt wurde.

Mir geht es halt darum, dass ich an jede asynchrone Methode den Token mitgebe und den Awaiter konfugiere, immer gleich und sehr häufig in den Methoden.

Der Originalcode sieht in etwa so aus. Wie gesagt, es interessiert mich eigentlich nur, ob der Code komplett durchgelaufen ist oder er abgebrochen wurde. Vor der Umstellung auf async/await war das Ganze nur in einen Task.Run mit synchronem WebRequest/WebResponse gekapselt und mittels Abort() abgebrochen. Nun wird jedes Mal das Await konfiguriert und das CancellationToken mitgegeben, was zu vielen Codedopplungen führt.


	class Program
	{
		private static readonly CancellationTokenSource _cts = new CancellationTokenSource();
		static void Main(string[] args)
		{
			try
			{
				StartAsync().Wait();
			}
			catch (TaskCanceledException)
			{
				// ...
			}
			catch (Exception)
			{
				// ...
			}
		}


		private static async Task StartAsync()
		{
			var client = new HttpClient();
			var response = await client.GetAsync("http://www.example.net/",_cts.Token).ConfigureAwait(false); // First access

			if (response.StatusCode == HttpStatusCode.OK)
			{
				// ...
			}
			else if (response.StatusCode == HttpStatusCode.ExpectationFailed)
			{
				// ...
			}

			var message = await client.PostAsync("http://www.example.net/", new StringContent("example=true"), _cts.Token).ConfigureAwait(false);

			var parser = new JsonParser();
			var json = parser.Parse(await message.Content.ReadAsStringAsync());

			if (!json.Successful)
			{
				throw new Exception();
			}

			response = await client.GetAsync("http://www.example.net/" + json.PathAndQuery, _cts.Token).ConfigureAwait(false);;

			// ...
		}
	}
04.01.2015 - 23:24 Uhr

Nuja, die API bietet nur die asynchronen Methoden an und das ist meiner Meinung nach auch richtig, denn diese benutzt intern die HttpClient des .NET-Frameworks, der auch nur die asynchronen mit Cancellationsupport anbietet.

04.01.2015 - 18:29 Uhr

Frohes Neues,

ich stelle meinen Code gerade auf async/await um und habe Tasks, deren Aufbau in etwa so ausschaut:


public async Task StartAsync(CancellationToken cancellationToken)
{
            var request = // ...

            var configuration = await _client.GetConfigurationAsync(request, cancellationToken).ConfigureAwait(false);

            // Fun with configuration

            var response = await _client.SetConfigurationAsync(configuration, cancellationToken).ConfigureAwait(false);

            // Fun with response

            // ... cancellationToken).ConfigureAwait(false);
            // ... cancellationToken).ConfigureAwait(false);
}

Ich gebe also jedes Mal das CancellationToken mit, jedes Mal konfiguriere ich den Awaiter dahingehend, dass er im ThreadPool laufen darf. Das ganz bläht den Code ganz schön auf und ich frage mich, kann man das nicht irgendwie andes bewerkstelligen? Zumal mich ja sowieso nur interessiert, ob Start vollständig durchgelaufen ist oder vom Benutzer explizit abgebrochen wurde.

23.12.2014 - 14:53 Uhr

Hallo,

ich stehe eigentlich vor einem ganz simplen Problem, dennoch scheint es mir irgendwie als unlösbar.

Für meine Persistenz nutze ich die Bilbliothek sqlite-net. Mein CLR-Objekt ist ein IPEndPoint mit zusätzlichen Eigenschaften ala "IsAvailable", "WasAvailableAt" etc. Dieses Objekt leidet von IPEndPoint ab und delegiert die parametrisierten Konstruktoren einfach nur an die Basisklasse weiter. Einen parameterlosen Konstruktor gibt es nicht.

Nun sollen die meisten dieser Eigenschaften serialisiert werden. IPEndPoint lässt sich nicht direkt serialisieren, da die Eigenschaft Address vom IPAddress ist und diese sich über sqlite-net nicht serialisieren lässt.

Also habe ich mir eine IPEndPointEntity Klasse erstellt, die das interface IPEndPointEntity implementiert, welche die Entität und das CLR-Objekt implementieren. Und nun mache ich für meine Begriffe unschöne Dinge, aber seht selbst:

	public class IPEndPointDBContext
	{
		private readonly SQLiteConnection _connection;

		public IPEndPointDBContext(string dataBasePath)
		{
			_connection = new SQLiteConnection(dataBasePath);
		}

		public IEnumerable<IPEndPoint> GetIPEndPoints()
		{
			return _connection.Table<IPEndPointEntity>().AsEnumerable().Select(AsIPEndPoint);
		}

		#region Type conversion

		private IPEndPoint AsIPEndPoint(IPEndPointEntity endPointEntity)
		{
			var endPoint = new IPEndPoint(IPAddress.Parse(endPointEntity.Address), endPointEntity.Port);
			AssignProperties(endPointEntity, endPoint );
			return endPoint ;
		}

		private IPEndPoint AsIPEndPointEntity(IPEndPoint endPoint)
		{
			var endPointEntity= new IPEndPointEntity
			{
				Address = endPoint.Address.ToString(),
				Port = endPoint.Port,
			};

			AssignProperties(endpoint, endPointEntity);

			return endPointEntity;
		}

		private void AssignProperties(IIPEndPoint from, IIPEndPoint to)
		{
			to.IsAvailable = from.IsAvailable;
			to.WasAvailableAt = from.WasAvailableAt;
			// ...
		}
		#endregion
	}

Nun, das ganze funktioniert zwar, aber als ich dann GetIPEndPoints auf async/await umstellen wollte und den nativen Support von sqlite-net ausnutzen will war das ganze "Design" dahin. Eigentlich will ich doch nur, dass die IPAddress als string in der Datenbank abgespeichert werden 🤔

05.12.2014 - 14:07 Uhr


>

Eben und deswegen funktioniert dein Code so nicht, weil die übergebenen Tasks ein TResult zurückliefern müssen und SendRequestAsync kein Result liefert.

Klar ist mir der Unterschied zwischen Tasks und Threads bewusst. Ich sprach über Threads im Zusammenhang mit dem WebRequest. Der Parallel.ForEach Wrapper im Zusammenhang mit WebRequest legt mir jeden Request auf einen Thread. Es werden unnötig Threads verbraucht.