I am trying to build a small operating system using c# and Cosmos
At fisrt this went pretty good only from one moment to the other it stoped working and gave me the following error:
Error occurred while invoking NAsm.
OSBoot.asm Line: 241424 Code: push dword System_Void__System_IO_Stream_Dispose__
OSBoot.asm Line: 242633 Code: push dword System_Void__System_IO_Stream_Dispose__ t
OSBoot.asm Line: 247640 Code: push dword System_Void__System_IO_Stream_Dispose__
OSBoot.asm Line: 248618 Code: push dword System_Void__System_Collections_Generic_List_1_Enumerator__Commands_ICommand_ICommandBase_Dispose_
I even installed a new installation of Microsoft Visual studio with cosmos on a new system and started over from scratch, at first it worked but asgain withoud any chage it stopped working
using System;
using Commands;
using Sys = Cosmos.System;
namespace OS
{
public class Kernel : Sys.Kernel
{
private Env SysEnv { get; set; }
private InputHandeler InputHandler { get; set; }
protected override void BeforeRun()
{
Console.WriteLine("Creating Enviriment...");
SysEnv = new Env();
InputHandler = new InputHandeler(new CommandLister());
Console.WriteLine("Enviriment build");
Console.Clear();
Console.WriteLine("Os booted successfully.");
}
protected override void Run()
{
Console.Write(SysEnv.ConsolCommandPrefix);
var input = Console.ReadLine();
try
{
InputHandler.ExecuteInput(input);
} catch(Exception)
{
//TODO good exeption and the use of it
}
}
}
}
Env class
namespace OS
{
public class Env
{
public string CurrentUser { get; set; }
public string CurrnetDir { get; set; }
private string prefix;
public string ConsolCommandPrefix
{
get { return CurrentUser + "#" + CurrnetDir + prefix; }
set { prefix = value; }
}
public Env()
{
ConsolCommandPrefix = "> ";
CurrentUser = "Admin";
CurrnetDir = "/";
}
}
}
CommandLister class
using System;
using System.Collections.Generic;
using Commands.Command;
using Commands.Command.SystemCommands;
using Commands.ICommand;
namespace Commands
{
public class CommandLister
{
private List<ICommandBase> KnownCommands { get; set; }
private Unkown UnkownCommand { get; set; }
public CommandLister()
{
KnownCommands = new List<ICommandBase>();
addCommand(new Help());
UnkownCommand = new Unkown();
}
public void addCommand(ICommandBase command)
{
KnownCommands.Add(command);
}
public ICommandBase getCommand(String input)
{
foreach (var command in KnownCommands)
{
if(true)
{
return command;
}
}
return UnkownCommand;
}
}
}
The other classes only contain some text and only print something to the Console
I have two questions:
- What is causing this
- How can i solve this problem
Edit: i tried it again ron an other OS (i used vista the first time), this time i used Windows 7. The same thing happend at first it worked (i started from scratch again..) but after a few builds and runs it somehow broke again. even when undoing the changes i made. it wont build anymore.
Kind regards,
Edo Post
Related
This question already has answers here:
My console app shutdown prematurely when using async / await?
(4 answers)
Program exits upon calling await
(3 answers)
Closed 2 years ago.
Before you all go on a rampage about how this is a duplicate question, I have spent two days working on this issue, watching youtube tutorials on asynchronous programming, surfing similar stackoverflow posts etc, and I cannot for the life of me figure out how to apply Asynchronous Parallel Downloading of files into my project.
First things first, some background:
I am creating a program that, when given a query input via the user, will make a call to the twitch API and download clips.
My program is two parts
1- A web scraper that generates a .json file with all details needed to download files and
2 - A downloader.
Part 1 works perfectly fine and generates the .json files no trouble.
My Downloader contains reference to a Data class that is a handler for common properties and methods like my ClientID, Authentication, OutputPath, JsonFile, QueryURL. It also contains methods to give values to these properties.
Here are the two methods of my FileDownloader.cs that are the problem:
public async static void DownloadAllFiles(Data clientData)
{
data = clientData;
data.OutputFolderExists();
// Deserialize .json file and get ClipInfo list
List<ClipInfo> clips = JsonConvert.DeserializeObject<List<ClipInfo>>(File.ReadAllText(data.JsonFile));
tasks = new List<Task>();
foreach(ClipInfo clip in clips)
{
tasks.Add(DownloadFilesAsync(clip));
}
await Task.WhenAll(tasks);
}
private async static Task DownloadFilesAsync(ClipInfo clip)
{
WebClient client = new WebClient();
string url = GetClipURL(clip);
string filepath = data.OutputPath + clip.id + ".mp4";
await client.DownloadFileTaskAsync(new Uri(url), filepath);
}
This is only one of my many attempts of downloading files, one which I got the idea from this post:
stackoverflow_link
I have also tried methods like the following from a YouTube video by IAmTimCorey:
video_link
I have spent many an hour tackling this problem, and I honestly can't figure out why it won't work with any of my attempts. I would vastly appreciate your help.
Thanks,
Ben
Below is the entirety of my code, should anyone need it for any reason.
Code Structure:
The only external libraries I have downloaded is Newtonsoft.Json
ClipInfo.cs
using System;
using System.Collections.Generic;
using System.Text;
namespace Downloader
{
public class ClipInfo
{
public string id { get; set; }
public string url { get; set; }
public string embed_url { get; set; }
public string broadcaster_id { get; set; }
public string broadcaster_name { get; set; }
public string creator_id { get; set; }
public string creator_name { get; set; }
public string video_id { get; set; }
public string game_id { get; set; }
public string language { get; set; }
public string title { get; set; }
public int view_count { get; set; }
public DateTime created_at { get; set; }
public string thumbnail_url { get; set; }
}
}
Pagination.cs
namespace Downloader
{
public class Pagination
{
public string cursor { get; set; }
}
}
Root.cs
using System.Collections.Generic;
namespace Downloader
{
public class Root
{
public List<ClipInfo> data { get; set; }
public Pagination pagination { get; set; }
}
}
Data.cs
using System;
using System.IO;
namespace Downloader
{
public class Data
{
private static string directory = Directory.GetCurrentDirectory();
private readonly static string defaultJsonFile = directory + #"\clips.json";
private readonly static string defaultOutputPath = directory + #"\Clips\";
private readonly static string clipsLink = "https://api.twitch.tv/helix/clips?";
public string OutputPath { get; set; }
public string JsonFile { get; set; }
public string ClientID { get; private set; }
public string Authentication { get; private set; }
public string QueryURL { get; private set; }
public Data()
{
OutputPath = defaultOutputPath;
JsonFile = defaultJsonFile;
}
public Data(string clientID, string authentication)
{
ClientID = clientID;
Authentication = authentication;
OutputPath = defaultOutputPath;
JsonFile = defaultJsonFile;
}
public Data(string clientID, string authentication, string outputPath)
{
ClientID = clientID;
Authentication = authentication;
OutputPath = directory + #"\" + outputPath + #"\";
JsonFile = OutputPath + outputPath + ".json";
}
public void GetQuery()
{
Console.Write("Please enter your query: ");
QueryURL = clipsLink + Console.ReadLine();
}
public void GetClientID()
{
Console.WriteLine("Enter your client ID");
ClientID = Console.ReadLine();
}
public void GetAuthentication()
{
Console.WriteLine("Enter your Authentication");
Authentication = Console.ReadLine();
}
public void OutputFolderExists()
{
if (!Directory.Exists(OutputPath))
{
Directory.CreateDirectory(OutputPath);
}
}
}
}
JsonGenerator.cs
using System;
using System.IO;
using System.Net.Http.Headers;
using System.Net.Http;
using System.Threading.Tasks;
using Newtonsoft.Json;
using System.Linq;
namespace Downloader
{
public static class JsonGenerator
{
// This class has no constructor.
// You call the Generate methods, passing in all required data.
// The file will then be generated.
private static Data data;
public static async Task Generate(Data clientData)
{
data = clientData;
string responseContent = null;
// Loop that runs until the api request goes through
bool authError = true;
while (authError)
{
authError = false;
try
{
responseContent = await GetHttpResponse();
}
catch (HttpRequestException)
{
Console.WriteLine("Invalid authentication, please enter client-ID and authentication again!");
data.GetClientID();
data.GetAuthentication();
authError = true;
}
catch (Exception e)
{
Console.WriteLine(e.Message);
authError = true;
}
}
data.OutputFolderExists();
GenerateJson(responseContent);
}
// Returns the contents of the resopnse to the api call as a string
private static async Task<string> GetHttpResponse()
{
// Creating client
HttpClient client = new HttpClient();
if (data.QueryURL == null)
{
data.GetQuery();
}
// Setting up request
HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Get, data.QueryURL);
// Adding Headers to request
requestMessage.Headers.Add("client-id", data.ClientID);
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", data.Authentication);
// Receiving response to the request
HttpResponseMessage responseMessage = await client.SendAsync(requestMessage);
// Gets the content of the response as a string
string responseContent = await responseMessage.Content.ReadAsStringAsync();
return responseContent;
}
// Generates or adds to the .json file that contains data on each clip
private static void GenerateJson(string responseContent)
{
// Parses the data from the response to the api request
Root responseResult = JsonConvert.DeserializeObject<Root>(responseContent);
// If the file doesn't exist, we need to create it and add a '[' at the start
if (!File.Exists(data.JsonFile))
{
FileStream file = File.Create(data.JsonFile);
file.Close();
// The array of json objects needs to be wrapped inside []
File.AppendAllText(data.JsonFile, "[\n");
}
else
{
// For a pre-existing .json file, The last object won't have a comma at the
// end of it so we need to add it now, before we add more objects
string[] jsonLines = File.ReadAllLines(data.JsonFile);
File.WriteAllLines(data.JsonFile, jsonLines.Take(jsonLines.Length - 1).ToArray());
File.AppendAllText(data.JsonFile, ",");
}
// If the file already exists, but there was no [ at the start for whatever reason,
// we need to add it
if (File.ReadAllText(data.JsonFile).Length == 0 || File.ReadAllText(data.JsonFile)[0] != '[')
{
File.WriteAllText(data.JsonFile, "[\n" + File.ReadAllText(data.JsonFile));
}
string json;
// Loops through each ClipInfo object that the api returned
for (int i = 0; i < responseResult.data.Count; i++)
{
// Serializes the ClipInfo object into a json style string
json = JsonConvert.SerializeObject(responseResult.data[i]);
// Adds the serialized contents of ClipInfo to the .json file
File.AppendAllText(data.JsonFile, json);
if (i != responseResult.data.Count - 1)
{
// All objects except the last require a comma at the end of the
// object in order to correctly format the array of json objects
File.AppendAllText(data.JsonFile, ",");
}
// Adds new line after object entry
File.AppendAllText(data.JsonFile, "\n");
}
// Adds the ] at the end of the file to close off the json objects array
File.AppendAllText(data.JsonFile, "]");
}
}
}
FileDownloader.cs
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Threading.Tasks;
namespace Downloader
{
public class FileDownloader
{
private static Data data;
private static List<Task> tasks;
public async static void DownloadAllFiles(Data clientData)
{
data = clientData;
data.OutputFolderExists();
// Deserialize .json file and get ClipInfo list
List<ClipInfo> clips = JsonConvert.DeserializeObject<List<ClipInfo>>(File.ReadAllText(data.JsonFile));
tasks = new List<Task>();
foreach (ClipInfo clip in clips)
{
tasks.Add(DownloadFilesAsync(clip));
}
await Task.WhenAll(tasks);
}
private static void GetData()
{
if (data.ClientID == null)
{
data.GetClientID();
}
if (data.Authentication == null)
{
data.GetAuthentication();
}
if (data.QueryURL == null)
{
data.GetQuery();
}
}
private static string GetClipURL(ClipInfo clip)
{
// Example thumbnail URL:
// https://clips-media-assets2.twitch.tv/AT-cm%7C902106752-preview-480x272.jpg
// You can get the URL of the location of clip.mp4
// by removing the -preview.... from the thumbnail url */
string url = clip.thumbnail_url;
url = url.Substring(0, url.IndexOf("-preview")) + ".mp4";
return url;
}
private async static Task DownloadFilesAsync(ClipInfo clip)
{
WebClient client = new WebClient();
string url = GetClipURL(clip);
string filepath = data.OutputPath + clip.id + ".mp4";
await client.DownloadFileTaskAsync(new Uri(url), filepath);
}
private static void FileDownloadComplete(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
{
tasks.Remove((Task)sender);
}
}
}
Program.cs
using System;
using System.Threading.Tasks;
using Downloader;
namespace ClipDownloader
{
class Program
{
private static string clientID = "{your_client_id}";
private static string authentication = "{your_authentication}";
async static Task Main(string[] args)
{
Console.WriteLine("Enter your output path");
string outputPath = Console.ReadLine();
Data data = new Data(clientID, authentication, outputPath);
Console.WriteLine(data.OutputPath);
//await JsonGenerator.Generate(data);
FileDownloader.DownloadAllFiles(data);
}
}
}
The example query I usually type in is "game_id=510218"
async void is your problem
Change
public static async void DownloadAllFiles(Data clientData)
To
public static async Task DownloadAllFiles(Data clientData)
Then you can await it
await FileDownloader.DownloadAllFiles(data);
The longer story:
async void runs unobserved (fire and forget). You can't wait for them to finish. In essence as soon as your program starts the task, it finishes, and tears down the App Domain and all your sub tasks, leading you to believe nothing is working.
I'm trying to stay on topic here as best as I can, but when using JsonConvert.DeserializeObject{T}, isn't T suppose to be an encapsulating root object type? I have never used it the way you're using it, so I'm just curious if that might be your bug. I could be completely wrong, and spare me if i am, but JSON is key:value based. Deserializing directly to a List doesn't really make sense. Unless there is a special case in the deserializer? List would be a file that's purely an array of ClipInfo values being deserialized into the members of List{T}(private T[] _items, private int _size, etc.) It needs a parent root object.
// current JSON file format implication(which i dont think is valid JSON?(correct me please)
clips:
[
// clip 1
{ "id": "", "url": "" },
// clip N
{ "id": "", "url": "" },
]
// correct(?) JSON file format
{ // { } is the outer encasing object
clips:
[
// clip 1
{ "id": "", "url": "" },
// clip N
{ "id": "", "url": "" },
]
}
class ClipInfoJSONFile
{
public List<ClipInfo> Info { get; set; }
}
var clipInfoList = JsonConverter.DeserializeObject<ClipInfoJSONFile>(...);
I'm trying to compile the following code using the .NET 4.5 Framework:
using System;
using System.Collections.Generic;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.Discussion.Client;
using Microsoft.TeamFoundation.VersionControl.Client;
class CodeReview
{
public List<CodeReviewComment> GetCodeReviewComments(int workItemId)
{
List<CodeReviewComment> comments = new List<CodeReviewComment>();
Uri tfsuri = new Uri(MYURL);
TeamFoundationDiscussionService service = new TeamFoundationDiscussionService();
service.Initialize(new Microsoft.TeamFoundation.Client.TfsTeamProjectCollection(tfsuri));
IDiscussionManager discussionManager = service.CreateDiscussionManager();
IAsyncResult result = discussionManager.BeginQueryByCodeReviewRequest(workItemId, QueryStoreOptions.ServerAndLocal, new AsyncCallback(CallCompletedCallback), null);
var output = discussionManager.EndQueryByCodeReviewRequest(result);
foreach (DiscussionThread thread in output)
{
if (thread.RootComment != null)
{
CodeReviewComment comment = new CodeReviewComment();
comment.Author = thread.RootComment.Author.DisplayName;
comment.Comment = thread.RootComment.Content;
comment.PublishDate = thread.RootComment.PublishedDate.ToShortDateString();
comment.ItemName = thread.ItemPath;
comments.Add(comment);
}
}
return comments;
}
static void CallCompletedCallback(IAsyncResult result)
{
// Handle error conditions here
}
public class CodeReviewComment
{
public string Author { get; set; }
public string Comment { get; set; }
public string PublishDate { get; set; }
public string ItemName { get; set; }
}
}
The Visual Studio compiler is complaining that "the type or namespace 'Uri' could not be found" and when I expand the System namespace in the Object Browser it doesnt list any of the Uri classes. I've tried a few other versions of .NET and still have the same problem although it does appear if I select the .NET 4.0 Client Profile but then none of the TeamFoundation classes are present in the Microsoft namespace.
I solved my own problem but I don't understand why it worked. In desperation I created another new Project using .NET 4.5 and copied and pasted the code over. It compiled and Uri shows up as a Class in the Object Browser. I have no idea what the difference between the 2 projects is.
I have an asp.net core project which needs to be able to support plugins at runtime, and as a consequence, I need to generate database tables based on what has been plugged in. The plugins are each divided in separate projects and they have have their own DbContext class. The plugins to be used are not known during compile-time, only at runtime.
Now in EF Core I thought that there would be a method like "UpdateDatabase" where you can just add tables to the existing database, but I was wrong. Is there a way to accomplish this? I was able to generate a separate database for each of the plugins, but that wasn't quite what I had in mind..I needed all tables in one database.
Here's the code for the "HRContext" plugin:
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.EntityFrameworkCore;
using Plugins.HR.Models.Entities;
namespace Plugins.HR.Contexts
{
public class HrContext : DbContext
{
public HrContext()
{
}
public HrContext(DbContextOptions<HrContext> contextOptions) : base(contextOptions)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.HasDefaultSchema("HR");
base.OnModelCreating(modelBuilder);
}
public DbSet<Address> Address { get; set; }
public DbSet<Attendance> Attendance { get; set; }
public DbSet<Department> Departments { get; set; }
public DbSet<Employee> Employees { get; set; }
public DbSet<JobTitle> JobTitles { get; set; }
}
}
Here's another piece of code for the "CoreContext" plugin:
using System;
using System.Collections.Generic;
using System.Text;
using Core.Data.Models;
using Microsoft.EntityFrameworkCore;
namespace Core.Data.Contexts
{
public class CoreContext : DbContext
{
public CoreContext()
{
}
public CoreContext(DbContextOptions<CoreContext> contextOptions) : base(contextOptions)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.HasDefaultSchema("Core");
base.OnModelCreating(modelBuilder);
}
public DbSet<Test> Tests { get; set; }
}
}
My ConfigureServices method in Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<CoreContext>(options => options.UseSqlServer("Data source = localhost; initial catalog = Company.Core; integrated security = true;"))
.AddDbContext<HrContext>(options => options.UseSqlServer("Data source = localhost; initial catalog = Company.HR; integrated security = true;"));
// Add framework services.
services.AddMvc();
}
If I try to change the connection string to be the same, sooner or later I will get an error that says that the table for one plugin does not exist. I tried "EnsureCreated" but that didn't work too.
I had the same issue. See my solution on GitHub a few days ago, here: EF Core Issue #9238
What you need is something like the following:
// Using an interface, so that we can swap out the implementation to support PG or MySQL, etc if we wish...
public interface IEntityFrameworkHelper
{
void EnsureTables<TContext>(TContext context)
where TContext : DbContext;
}
// Default implementation (SQL Server)
public class SqlEntityFrameworkHelper : IEntityFrameworkHelper
{
public void EnsureTables<TContext>(TContext context)
where TContext : DbContext
{
string script = context.Database.GenerateCreateScript(); // See issue #2943 for this extension method
if (!string.IsNullOrEmpty(script))
{
try
{
var connection = context.Database.GetDbConnection();
bool isConnectionClosed = connection.State == ConnectionState.Closed;
if (isConnectionClosed)
{
connection.Open();
}
var existingTableNames = new List<string>();
using (var command = connection.CreateCommand())
{
command.CommandText = "SELECT table_name from INFORMATION_SCHEMA.TABLES WHERE table_type = 'base table'";
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
existingTableNames.Add(reader.GetString(0).ToLowerInvariant());
}
}
}
var split = script.Split(new[] { "CREATE TABLE " }, StringSplitOptions.RemoveEmptyEntries);
foreach (string sql in split)
{
var tableName = sql.Substring(0, sql.IndexOf("(", StringComparison.OrdinalIgnoreCase));
tableName = tableName.Split('.').Last();
tableName = tableName.Trim().TrimStart('[').TrimEnd(']').ToLowerInvariant();
if (existingTableNames.Contains(tableName))
{
continue;
}
try
{
using (var createCommand = connection.CreateCommand())
{
createCommand.CommandText = "CREATE TABLE " + sql.Substring(0, sql.LastIndexOf(";"));
createCommand.ExecuteNonQuery();
}
}
catch (Exception)
{
// Ignore
}
}
if (isConnectionClosed)
{
connection.Close();
}
}
catch (Exception)
{
// Ignore
}
}
}
}
Then at the end of Startup.Configure(), I resolve an IEntityFrameworkHelper instance and use that with an instance of DbContext to call EnsureTables().
One issue is I need to still account for the parts of the script which are not CREATE TABLE statements. For example, the CREATE INDEX statements.
I requested they give us a clean solution, for example: add a CreateTable<TEntity>() method to IRelationalDatabaseCreator. Not holding my breath for that though...
EDIT
I forgot to post the code for GenerateCreateScript(). See below:
using System.Text;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage;
public static class DatabaseFacadeExtensions
{
public static string GenerateCreateScript(this DatabaseFacade database)
{
var model = database.GetService<IModel>();
var migrationsModelDiffer = database.GetService<IMigrationsModelDiffer>();
var migrationsSqlGenerator = database.GetService<IMigrationsSqlGenerator>();
var sqlGenerationHelper = database.GetService<ISqlGenerationHelper>();
var operations = migrationsModelDiffer.GetDifferences(null, model);
var commands = migrationsSqlGenerator.Generate(operations, model);
var stringBuilder = new StringBuilder();
foreach (var command in commands)
{
stringBuilder
.Append(command.CommandText)
.AppendLine(sqlGenerationHelper.BatchTerminator);
}
return stringBuilder.ToString();
}
}
It's based on the code found here: EF Core Issue #2943
I am trying to change/update the due date of a production order in SAP B1 with the code below:
public static void ChangeDueDateForProductionOrder(SAPB1Credentials credentials, int poAbsEntry, DateTime dueDate)
{
DebugLogger.Log(credentials.CompanyDB, $"Changing due date for production order '{poAbsEntry}' to '{dueDate.ToString(C_DATE_FORMAT_NL)}'.");
using (var sap = new SAPB1Connection(credentials))
{
ProductionOrders productionOrder = sap.Company.GetBusinessObject(BoObjectTypes.oProductionOrders);
if(productionOrder.GetByKey(poAbsEntry))
{
productionOrder.DueDate = dueDate;
if (productionOrder.Update() != 0)
{
var message = $"Error while changing due date for '{poAbsEntry}' to '{dueDate.ToString(C_DATE_FORMAT_NL)}', the following error is given '{sap.Company.GetLastErrorDescription()}'.";
DebugLogger.Log(credentials.CompanyDB, message);
throw new Exception(message);
}
}
else
throw new Exception($"PoId '{poAbsEntry}' does not exists.");
}
}
However, I am getting the following error:
Error while changing due date for '145' to '11-09-2016', the following error is given 'Field cannot be updated (ODBC -1029)'.
And the error SAP gives is:
Field cannot be updated (ODBC -1029)
Additional information:
It is a new production order that has the status Planned.
Creation date is Today.
Due date I am trying to change it to is Today + 5 days.
The Id (AbsEntry) for the production order is 145.
SAP Business One 9.1
Yes, I can alter the due date in the SAP B1 GUI without problems.
Short, Self Contained, Correct (Compilable), Example
The code below gives me the exact same error. Replaced connection settings with ??.
using System;
using SAPbobsCOM; // Reference to SAP B1 DI SDK (32-bit)
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var credentials = new SAPB1Credentials
{
Server = "??",
CompanyDB = "??",
Username = "??",
Password = "??"
};
SAPbobsCOM.Company sap = new SAPbobsCOM.Company();
sap.Server = credentials.Server;
sap.DbServerType = credentials.ServerType;
sap.CompanyDB = credentials.CompanyDB;
sap.UserName = credentials.Username;
sap.Password = credentials.Password;
sap.language = credentials.Language;
sap.UseTrusted = credentials.UseTrusted;
var returnCode = sap.Connect();
if (returnCode != 0)
{
var error = sap.GetLastErrorDescription();
throw new Exception(error);
}
ProductionOrders productionOrder = sap.GetBusinessObject(BoObjectTypes.oProductionOrders);
if (productionOrder.GetByKey(145))
{
productionOrder.DueDate = DateTime.Now.AddDays(5);
if (productionOrder.Update() != 0)
{
var error = sap.GetLastErrorDescription();
throw new Exception(error);
}
}
else
throw new Exception($"PoId does not exists.");
}
public class SAPB1Credentials
{
public string Server { get; set; }
public string CompanyDB { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public BoSuppLangs Language { get; set; } = BoSuppLangs.ln_English;
public BoDataServerTypes ServerType { get; set; } = BoDataServerTypes.dst_MSSQL2008;
public bool UseTrusted { get; set; } = false;
}
}
}
What am I missing, and why do I get this error?
From the linked SAP thread in the comment.
Edy Simon said:
Hi Lars,
Must be a Patch Level bug.
I can use your code to update it on SBO 881PL6.
You might want to test it on another version.
Regards
Edy
Which resolved the issue.
I am writing up a checkout, build and deployment application in C#, and need to know the best way to detect whether my call to msbuild.exe has succeeded or not. I have tried to use the error code from the process, but I am not sure whether this is always accurate.
Is there a way (through the code below) that I can tell whether msbuild.exe completed successfully?
try
{
Process msbProcess = new Process();
msbProcess.StartInfo.FileName = this.MSBuildPath;
msbProcess.StartInfo.Arguments = msbArguments;
msbProcess.Start();
msbProcess.WaitForExit();
if (msbProcess.ExitCode != 0)
{
//
}
else
{
//
}
msbProcess.Close();
}
catch (Exception ex)
{
//
}
As far as I've been able to determine, MSBuild returns an exit code greater then zero when it encounters an error. If it doesn't encounter any errors, it returns exit code 0. I've never seen it exit with code lower than 0.
I use it in a batch file:
msbuild <args>
if errorlevel 1 goto errorDone
In four years of using it this way, I've never had reason to question the correctness of this approach.
Several questions on the MSDN forums ask the same thing.
The standard response is, in effect, "if errorlevel is 0, then there was no error".
Sorry if I'm a little bit too late for the party... but nearly 7 years after the question was posted I wanted to see a complete answer for it. I did some tests using the code below, and here are the conclusions:
Analysis
msbuild.exe returns 1 when at least one build error occurs, and returns 0 when the build is successfully completed. At present, the program does not take warnings into account, which means a successful build with warnings causes msbuild.exe to still return 0.
Other errors like: trying to build a project that does not exist, or providing an incorrect argument (like /myInvalidArgument), will also cause msbuild.exe to return 1.
Source Code
The following C# code is a complete implementation for building your favorite projects by firing msbuild.exe from a command line. Don't forget to setup any necessary environment settings before compiling your projects.
Your BuildControl class:
using System;
namespace Example
{
public sealed class BuildControl
{
// ...
public bool BuildStuff()
{
MsBuilder builder = new MsBuilder(#"C:\...\project.csproj", "Release", "x86")
{
Target = "Rebuild", // for rebuilding instead of just building
};
bool success = builder.Build(out string buildOutput);
Console.WriteLine(buildOutput);
return success;
}
// ...
}
}
MsBuilder class: Builds stuff by calling MsBuild.exe from command line:
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace Example
{
public sealed class MsBuilder
{
public string ProjectPath { get; }
public string LogPath { get; set; }
public string Configuration { get; }
public string Platform { get; }
public int MaxCpuCount { get; set; } = 1;
public string Target { get; set; } = "Build";
public string MsBuildPath { get; set; } =
#"C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\MSBuild\15.0\Bin\MsBuild.exe";
public string BuildOutput { get; private set; }
public MsBuilder(string projectPath, string configuration, string platform)
{
ProjectPath = !string.IsNullOrWhiteSpace(projectPath) ? projectPath : throw new ArgumentNullException(nameof(projectPath));
if (!File.Exists(ProjectPath)) throw new FileNotFoundException(projectPath);
Configuration = !string.IsNullOrWhiteSpace(configuration) ? configuration : throw new ArgumentNullException(nameof(configuration));
Platform = !string.IsNullOrWhiteSpace(platform) ? platform : throw new ArgumentNullException(nameof(platform));
LogPath = Path.Combine(Path.GetDirectoryName(ProjectPath), $"{Path.GetFileName(ProjectPath)}.{Configuration}-{Platform}.msbuild.log");
}
public bool Build(out string buildOutput)
{
List<string> arguments = new List<string>()
{
$"/nologo",
$"\"{ProjectPath}\"",
$"/p:Configuration={Configuration}",
$"/p:Platform={Platform}",
$"/t:{Target}",
$"/maxcpucount:{(MaxCpuCount > 0 ? MaxCpuCount : 1)}",
$"/fileLoggerParameters:LogFile=\"{LogPath}\";Append;Verbosity=diagnostic;Encoding=UTF-8",
};
using (CommandLineProcess cmd = new CommandLineProcess(MsBuildPath, string.Join(" ", arguments)))
{
StringBuilder sb = new StringBuilder();
sb.AppendLine($"Build started: Project: '{ProjectPath}', Configuration: {Configuration}, Platform: {Platform}");
// Call MsBuild:
int exitCode = cmd.Run(out string processOutput, out string processError);
// Check result:
sb.AppendLine(processOutput);
if (exitCode == 0)
{
sb.AppendLine("Build completed successfully!");
buildOutput = sb.ToString();
return true;
}
else
{
if (!string.IsNullOrWhiteSpace(processError))
sb.AppendLine($"MSBUILD PROCESS ERROR: {processError}");
sb.AppendLine("Build failed!");
buildOutput = sb.ToString();
return false;
}
}
}
}
}
CommandLineProcess class - Starts a command line process and waits until it finishes. All standard output/error is captured, and no separate window is started for the process:
using System;
using System.Diagnostics;
using System.IO;
namespace Example
{
public sealed class CommandLineProcess : IDisposable
{
public string Path { get; }
public string Arguments { get; }
public bool IsRunning { get; private set; }
public int? ExitCode { get; private set; }
private Process Process;
private readonly object Locker = new object();
public CommandLineProcess(string path, string arguments)
{
Path = path ?? throw new ArgumentNullException(nameof(path));
if (!File.Exists(path)) throw new ArgumentException($"Executable not found: {path}");
Arguments = arguments;
}
public int Run(out string output, out string err)
{
lock (Locker)
{
if (IsRunning) throw new Exception("The process is already running");
Process = new Process()
{
EnableRaisingEvents = true,
StartInfo = new ProcessStartInfo()
{
FileName = Path,
Arguments = Arguments,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true,
},
};
if (!Process.Start()) throw new Exception("Process could not be started");
output = Process.StandardOutput.ReadToEnd();
err = Process.StandardError.ReadToEnd();
Process.WaitForExit();
try { Process.Refresh(); } catch { }
return (ExitCode = Process.ExitCode).Value;
}
}
public void Kill()
{
lock (Locker)
{
try { Process?.Kill(); }
catch { }
IsRunning = false;
Process = null;
}
}
public void Dispose()
{
try { Process?.Dispose(); }
catch { }
}
}
}
PS: I'm using Visual Studio 2017 / .NET 4.7.2