Unity leaderboard on mac os x device - Pubnub, Playfab or Firebase? - firebase

I am currently making a a leaderboard feature for my Mac OS X Unity game. I first tried Playfab but I kept getting an error saying "PlayFabException: Must be logged in to call this method PlayFab". I can't find a way to resolve this.
I have 2 scripts that does this, this is the code for the PlayFabManger script:
`using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using PlayFab;
using PlayFab.ClientModels;
using System;
public class PlayFabManager : MonoBehaviour
//public static PlayFabManager instance;
// Start is called before the first frame update
void Start()
{
//instance = this;
if (string.IsNullOrEmpty(PlayFabSettings.staticSettings.TitleId))
{
/*
Please change the titleId below to your own titleId from PlayFab Game Manager.
If you have already set the value in the Editor Extensions, this can be skipped.
*/
PlayFabSettings.staticSettings.TitleId = "F9F3D";
}
Login();
}
// Update is called once per frame
void Update()
{
}
void Login()
{
var request = new LoginWithCustomIDRequest
{
CustomId = SystemInfo.deviceUniqueIdentifier,
CreateAccount = true
};
PlayFabClientAPI.LoginWithCustomID(request, OnSuccess, OnError);
}
//private void OnLoginSuccess(LoginResult result)
//{
// //>> Call Client API here <<
// var getStoreItemsRequest = new GetStoreItemsRequest { StoreId = "[YourStoreId]" };// Please change this value to your own storeId from PlayFab Game Manager
// PlayFabClientAPI.GetStoreItems(getStoreItemsRequest, OnGetSuccess, OnError);
//}
void OnSuccess(LoginResult result)
{
print("Successful login create");
}
public void SendLeaderBoard(int score)
{
var request = new UpdatePlayerStatisticsRequest
{
Statistics = new List<StatisticUpdate>
{
new StatisticUpdate
{
StatisticName = "PlatformScore",
Value = score
}
}
};
PlayFabClientAPI.UpdatePlayerStatistics(request, OnLeaderboardUpdate, OnError);
}
void OnLeaderboardUpdate(UpdatePlayerStatisticsResult result)
{
print("Successful leaderboard sent");
}
void OnError(PlayFabError error)
{
print("Error while logging in/creating account!");
print(error.GenerateErrorReport());
}
public void GetLeaderBoard()
{
var request = new GetLeaderboardRequest
{
StatisticName = "PlatformScore",
StartPosition = 0,
MaxResultsCount = 10
};
PlayFabClientAPI.GetLeaderboard(request, OnLeaderboardGet, OnError);
}
private void OnLeaderboardGet(GetLeaderboardResult result)
{
foreach (var item in result.Leaderboard)
{
print(item.Position + " " + item.PlayFabId + " " + item.StatValue);
}
}
}
`
I also have a line of code in another script that is called at the start method and refers to the above script, where I pass in a Playerprefs.GetInt variable:
playFabManager.SendLeaderBoard(PlayerPrefs.GetInt("TtlPoints"));
Does anyone have any idea on resolving this error? Is there an easier way to achieve this leaderboard feature on Mac OS X using other extensions such as firebase or pubnub?
Sorry for my English, looking forward to hearing from you.

PubNub awesome, Beamable uses them for fabs leaderboards for Unity! Check them out.

I'll take the Firebase answer.
If you go the Firebase route, there is not an out of the box Leaderboard solution. There is an example open source repository that you can use to implement leaderboards on Realtime Database that should be relatively simple to implement.
Your second problem is that although Realtime Database does work on desktop (and the leaderboard in particular hasn't given me any issues), it is currently a beta feature only intended for use during development. If you file a related bug, the team will work to fix it, but it will probably be prioritized behind any mobile-features. You could implement it all using the REST API directly, but at this point the official Unity samples and documentation won't apply.

Related

Azure Function Integration of Serilog with Application Insights, logs visible in Search but are not appearing in Failures events timeline

I am trying to use Serilog with Application Insights sink for logging purposes. I can see the logs in Search bar in Azure Portal (Application Insights) but same logs are not visible if we view the timeline of events in Failures or Performance Tab. Thanks
Below is the code am using for registering Logger in FunctionStartup, which then gets injected in Function for logging:
var logger = new LoggerConfiguration()
.Enrich.FromLogContext()
.Enrich.WithProperty("ApplicationName", "testApp")
.Enrich.WithProperty("Environment", "Dev")
.WriteTo.ApplicationInsights(GetTelemetryClient("Instrumentationkey"), TelemetryConverter.Traces)
.CreateLogger();
builder.Services.AddSingleton<ILogger>(logger);
Telementory Client is getting fetched from a helper method:
public static TelemetryClient GetTelemetryClient(string key)
{
var teleConfig = new TelemetryConfiguration { InstrumentationKey = key };
var teleClient = new TelemetryClient(teleConfig);
return teleClient;
}
host.json
{
"version": "2.0",
"logging": {
"applicationInsights": {
"samplingExcludedTypes": "Request",
"samplingSettings": {
"isEnabled": true
}
}
}
}
I got your mean, and pls allow me to sum up my testing result here.
First, the failure blade is not designed for providing a timeline which used to trace the details(what happened before the exception take place), but to show all the exceptions, how often the error happened, how many users be affected, etc, it's more likely stand in a high place to see the whole program.
And to achieve your goal, I think you can use this kql query in the Logs blade or watching it in transaction blade.
union traces, requests,exceptions
| where operation_Id == "178845c426975d4eb96ba5f7b5f376e1"
Basically, we may add many logs in the executing chain, e.g. in the controller, log the input parameter, then log the result of data combining or formatting, log the exception information in catch, so here's my testing code. I can't see any other information in failure blade as you, but in the transaction blade, I can see the timeline.
public class HelloController : Controller
{
public string greet(string name)
{
Log.Verbose("come to greet function");
Log.Debug("serilog_debug_info");
Log.Information("greet name input " + name);
int count = int.Parse(name);
Log.Warning("enter greet name is : {0}", count);
return "hello " + name;
}
}
And we can easily find that, the whole chain shares the same operationId, and via all these logs, we can pinpoint the wrong line code. By the way, if I surround the code with try/catch, exception won't be captured in the failure blade.
==================================
Using Serilog integrate app insights, we need to send serilog to application insights, and we will see lots of Traces in transaction search, so it's better to made the MinimumLevel to be information and higher. The sreenshot below is my log details, and we can also use kql query by operationId to see the whole chain.
You can easily solve this by following the solution provided by Azure Application Insights on their GitHub repo, as per this Github Issue, you can either use the DI to configure TelemetryConfiguration, i.e
services.Configure<TelemetryConfiguration>(
(o) => {
o.InstrumentationKey = "123";
o.TelemetryInitializers.Add(new OperationCorrelationTelemetryInitializer());
});
or you can configure it manually like this:
var config = TelemetryConfiguration.CreateDefault();
var client = new TelemetryClient(config);
So in your code, you have to change your GetTelemetryClient from
public static TelemetryClient GetTelemetryClient(string key)
{
var teleConfig = new TelemetryConfiguration { InstrumentationKey = key };
var teleClient = new TelemetryClient(teleConfig);
return teleClient;
}
to this
public static TelemetryClient GetTelemetryClient(string key)
{
var teleConfig = TelemetryConfiguration.CreateDefault();
var teleClient = new TelemetryClient(teleConfig);
return teleClient;
}
In order to use logging using Telemetry Configuration as mentioned in the answer above for Azure Functions, we just need to update the function as in below snippet and on deployment it should fetch Instrumentation key itself
public static TelemetryClient GetTelemetryClient()
{
var teleConfig = TelemetryConfiguration.CreateDefault();
var teleClient = new TelemetryClient(teleConfig);
return teleClient;
}
But to run both locally and after deployment on Azure. We need to add something like this in function Startup and get rid of the Function above.
builder.Services.Configure<TelemetryConfiguration>((o) =>
{
o.InstrumentationKey = "KEY";
o.TelemetryInitializers.Add(new OperationCorrelationTelemetryInitializer());
});
builder.Services.AddSingleton<ILogger>(sp =>
{
var logger = new LoggerConfiguration()
.Enrich.FromLogContext()
.Enrich.WithProperty("ApplicationName", "TEST")
.Enrich.WithProperty("Environment", "DEV")
.WriteTo.ApplicationInsights(
sp.GetRequiredService<TelemetryConfiguration>(), TelemetryConverter.Traces).CreateLogger();
return logger;
});
After wards we just need to use typical DI in our classes/azure function to use ILogger
public class Test{
public ILogger _log;
public void Test(ILogger log){
_log=log;
}
}

PushSharp 4.0.10.0: HTTP/2-based Apple Push Notification service (APNs)

We use PushSharp 4.0.10 to send iOS Push Notifications:
https://github.com/Redth/PushSharp
Recently we recieved this email from Apple Developer:
"If you still send push notifications with the legacy binary protocol, it's time to update to the HTTP/2-based Apple Push Notification service (APNs) provider API. You'll be able to take advantage of great features, such as authentication with a JSON Web Token, improved error messaging, and per-notification feedback.
To give you additional time to prepare, the deadline to upgrade to the APNs provider API has been extended to March 31, 2021. We recommend upgrading as soon as possible, as APNs will no longer support the legacy binary protocol after this date."
My question is: Will PushSharp 4.0.10 still work after March 31, 2021?
There is a discussion about this but the thread was closed. But there are still some suggestions on this thread that you might want to try.
The Apple Push Notification service (APNs) will no longer support the legacy binary protocol as of November 2020
https://github.com/Redth/PushSharp/issues/923
**
EDIT - 25th March 2021
The deadline is close and #Ashita Shah asked some code snippet so I hope the following can save your time.
Add the following class dotAPNSService to your project. You can customise this structure according to your needs. Also I didn't focus the best of best coding C# standards when implementing my own push notification service. You can implement LINQ, Tasks async etc. I tested this dotAPNS library and it works perfectly fine. For Android you can still use PushSharp.
Before you implement the dotAPNSService helper class, get the following from your Apple developer account. The ApnsJwtOptions values should be:
BundleId - your app’s bundle ID. Should not include specific topics (i.e. com.myapp but not com.myapp.voip).
CertFilePath - path to the .p8 certificate you have downloaded from the Developer Center.
KeyId - The 10-character Key ID you obtained from your developer account
TeamId - The 10-character Team ID you use for developing your company’s apps. Obtain this value from your developer account.
public class dotAPNSService : IDisposable
{
public event EventHandler OnTokenExpiredHandler;
private ApnsJwtOptions options = null;
public dotAPNSService()
{
options = new ApnsJwtOptions()
{
BundleId = "com.xx.xxxx",
CertFilePath = "../../certificate.p8",
KeyId = "The_Key_Id",
TeamId = "The_Team_Id"
};
}
public void SendNotifications(String[] deviceTokens, String title, String body)
{
if (deviceTokens == null || deviceTokens.Length <= 0)
{
return;
}
if (String.IsNullOrEmpty(title))
{
return;
}
if (String.IsNullOrEmpty(body))
{
return;
}
// once you've gathered all the information needed and created an options instance, it's time to call
var apns = ApnsClient.CreateUsingJwt(new HttpClient(), options);
// start the process
foreach (String deviceToken in deviceTokens)
{
var push = new ApplePush(ApplePushType.Alert)
.AddAlert(title, body)
.AddToken(deviceToken);
Send(apns, push, deviceToken);
}
}
public void SendSilentNotifications(String[] deviceTokens)
{
try
{
if (deviceTokens == null || deviceTokens.Length <= 0)
{
return;
}
// once you've gathered all the information needed and created an options instance, it's time to call
var apns = ApnsClient.CreateUsingJwt(new HttpClient(), options);
// start the process
foreach (String deviceToken in deviceTokens)
{
var push = new ApplePush(ApplePushType.Background)
.AddContentAvailable()
.AddToken(deviceToken);
Send(apns, push, deviceToken);
}
}
finally
{
}
}
private void Send(ApnsClient apns, ApplePush push, String deviceToken)
{
try
{
var response = apns.SendAsync(push);
if (response.Result.Reason == ApnsResponseReason.Success)
{
// the notification has been sent!
}
else
{
Boolean removeToken = false;
switch (response.Result.Reason)
{
case ApnsResponseReason.BadDeviceToken:
removeToken = true;
break;
case ApnsResponseReason.TooManyRequests:
break;
}
// remove the token from database?
if (removeToken)
OnTokenExpired(new ExpiredTokenEventArgs(deviceToken));
}
}
catch (TaskCanceledException)
{
// ERROR - HTTP request timed out, you can use the deviceToken to log the error
}
catch (HttpRequestException ex)
{
// ERROR - HTTP request failed, you can use the deviceToken to log the error
}
}
protected virtual void OnTokenExpired(ExpiredTokenEventArgs args)
{
try
{
EventHandler handler = OnTokenExpiredHandler;
if (handler != null)
{
ISynchronizeInvoke target = handler.Target as ISynchronizeInvoke;
if (target != null && target.InvokeRequired)
target.Invoke(handler, new object[] { this, args });
else
handler(this, args);
}
}
catch (Exception ex)
{
}
}
}
These are the namespaces of the dotAPNSService helper class:
using System;
using System.ComponentModel;
using System.Net.Http;
using System.Threading.Tasks;
using dotAPNS;
In order to use the dotAPNSService helper on your project just pull the tokens from the database and then pass them to it. For instance, to send silent notifications:
public void SendScheduledSilentNotifications()
{
try
{
IList<User> users = _accountService.GetUsers(true);
if (users != null && users.Count > 0)
{
List<String> deviceTokens = new List<String>();
foreach (User user in users)
{
if (!String.IsNullOrEmpty(user.DeviceToken))
deviceTokens.Add(user.DeviceToken);
}
if (deviceTokens.Count > 0)
{
using (dotAPNSService service = new dotAPNSService())
{
service.OnTokenExpiredHandler += new EventHandler(OnTokenExpired);
service.SendSilentNotifications(deviceTokens.ToArray());
}
}
}
}
finally
{
}
}
To remove the expired tokens from the database you can use the following:
private void OnTokenExpired(object sender, EventArgs e)
{
if (e == null)
return;
if (e.GetType() == typeof(ExpiredTokenEventArgs))
{
var args = (ExpiredTokenEventArgs)e;
User user = _accountService.GetUserByDeviceToken(args.Token);
if (user != null)
{
user.DeviceToken = String.Empty;
Boolean success = !(_accountService.SaveUser(user) == null);
if (success)
// INFO - expired device token has been removed from database
else
// INFO - something went wrong
}
}
}
You can download the source code from here:
https://github.com/alexalok/dotAPNS
The API is now sending thousands of silent notifications at one time and there are no delays, crashes etc. Hope this code snippet helps and saves your time!

Test and use chat bot without emulator

It may seems very basic question but any guidance is appreciated .Where can i start to learn?.
I have used bot framework and made some code that gives total number of cities in particular country .I integrated and used LUIS .All went good but now to test,i use bot emulator of microsoft.
In real time ,i want to use this chat application on my asp.net application .How can i use this code in Asp.net and test without bot emulator.
Working Code(in case it may help someone) -
using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.Http.Description;
using Microsoft.Bot.Connector;
using Newtonsoft.Json;
namespace YahooBot
{
[BotAuthentication]
public class MessagesController : ApiController
{
/// <summary>
/// POST: api/Messages
/// Receive a message from a user and reply to it
/// </summary>
public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
{
if (activity.Type == ActivityTypes.Message)
{
ConnectorClient connector = new ConnectorClient(new Uri(activity.ServiceUrl));
string StockRateString;
LUIS StLUIS = await GetEntityFromLUIS(activity.Text);
if (StLUIS.intents.Count() > 0)
{
switch (StLUIS.intents[0].intent)
{
case "StockPrice":
StockRateString = await GetStock(StLUIS.entities[0].entity);
break;
case "StockPrice2":
StockRateString = await GetStock(StLUIS.entities[0].entity);
break;
case "Getcity":
StockRateString = await GetCityDetails(StLUIS.entities[0].entity);
break;
default:
StockRateString = "Sorry, I am not getting you...";
break;
}
}
else
{
StockRateString = "Sorry, I am not getting you...";
}
Activity reply = activity.CreateReply(StockRateString);
await connector.Conversations.ReplyToActivityAsync(reply);
}
else
{
HandleSystemMessage(activity);
}
var response = Request.CreateResponse(HttpStatusCode.OK);
return response;
}
private async Task<string> GetStock(string StockSymbol)
{
double? dblStockValue = await YahooBot.GetStockRateAsync(StockSymbol);
if(dblStockValue==null)
{
return string.Format("This \"{0}\" is not an valid stock symbol", StockSymbol);
}
else
{
return string.Format("Stock Price of {0} is {1}", StockSymbol, dblStockValue);
}
}
private async Task<string> GetCityDetails(string citychar)
{
string dblcityValue = await YahooBot.GetCityAsync(citychar);
if (dblcityValue == "")
{
return string.Format("This \"{0}\" is not an valid city ", citychar);
}
else
{
return string.Format("number of cities beginning with {0} is {1}", citychar, dblcityValue);
}
}
private static async Task<LUIS> GetEntityFromLUIS(string Query)
{
Query = Uri.EscapeDataString(Query);
LUIS Data = new LUIS();
using (HttpClient client = new HttpClient())
{
string RequestURI = "https://westus.api.cognitive.microsoft.com/luis/v2.0/apps/f2a85791-c9fe-44f8-b23c-2580581dc383?subscription-key=8b4566ea897c4c87960995755aa8881d&verbose=true&timezoneOffset=0&q=" + Query;
HttpResponseMessage msg = await client.GetAsync(RequestURI);
if (msg.IsSuccessStatusCode)
{
var JsonDataResponse = await msg.Content.ReadAsStringAsync();
Data = JsonConvert.DeserializeObject<LUIS>(JsonDataResponse);
}
}
return Data;
}
private Activity HandleSystemMessage(Activity message)
{
if (message.Type == ActivityTypes.DeleteUserData)
{
// Implement user deletion here
// If we handle user deletion, return a real message
}
else if (message.Type == ActivityTypes.ConversationUpdate)
{
// Handle conversation state changes, like members being added and removed
// Use Activity.MembersAdded and Activity.MembersRemoved and Activity.Action for info
// Not available in all channels
}
else if (message.Type == ActivityTypes.ContactRelationUpdate)
{
// Handle add/remove from contact lists
// Activity.From + Activity.Action represent what happened
}
else if (message.Type == ActivityTypes.Typing)
{
// Handle knowing tha the user is typing
}
else if (message.Type == ActivityTypes.Ping)
{
}
return null;
}
}
}
A few steps must be followed:
Publish your bot on Azure (can be done from Visual Studio directly), see documentation
Register your bot on dev.botframework.com (see documentation)
Integrate the chat in your Asp.net site, for example using iFrame: see the tutorial on the documentation . There are also other possibilities described here. An important note is that you should use a token instead of secret in the iframe src, see details in the documentation provided before (option 1 vs option 2)
In a nutshell: everything you need is on the official documentation, in the How-to guides

Realm + Retrofit + RxJava: Concat and SubscribeOn

I am having an issue while using the RxJava concat operator. I have two observables, the first emits results from a server database and the other one emits results from the local database, and then I concat the :
// Uses a Realm in the UI thread
Observable<MyResult> remoteObservable = mRemoteDataSource.find(tId);
// Uses Retrofit
Observable<MyResult> localObservable = mLocalDataSource.find(tId);
Observable.concat(localObservable, remoteObservable)
.doOnNext(result -> /* Do my stuff */)
.observeOn(AndroidSchedulers.mainThread())
.doOnError(throwable -> throwable.printStackTrace())
.subscribe()
So this causes me problem, since I am not using subscribeOn() the concatenated observable is running on AndroidScheduler.MainThread() and this does not run the remote and it launches a NetworkOnMainThreadException.
If I implement a subscribeOn(Schedulers.computation()) I get Realm access from incorrect thread. Realm objects can only be accessed on the thread they were created since of course the Observable is not running on the thread the Realm instance does exist.
I have searched in other questions and I have not gotten anything useful, I have checked the example made by realm: https://github.com/realm/realm-java/blob/master/examples/rxJavaExample/src/main/java/io/realm/examples/rxjava/retrofit/RetrofitExample.java but strangely I see that the retrofit observable is subscribed on nothing and it works.
Why does it work on the sample and in my code I cannot do the same? Any suggestion?
I believe you should use subscribeOn() in the right places.
// Uses a Realm in the UI thread
Observable<MyResult> realmObservable = mRealmDataSource.find(tId).subscribeOn(AndroidSchedulers.mainThread());
// Uses Retrofit
Observable<MyResult> retrofitObservable = mRetrofitDataSource.find(tId).subscribeOn(Subscribers.io());
Observable.concat(realmObservable, retrofitObservable)
.doOnNext(result -> /* Do my stuff */)
.subscribeOn(AndroidSchedulers.mainThread())
.observeOn(AndroidSchedulers.mainThread())
.doOnError(throwable -> throwable.printStackTrace())
.subscribe()
See if it fixes your issue.
You can concat your local and remote observables like below:
// Uses a Realm in the UI thread
Observable<MyResult> remoteObservable = mRemoteDataSource.find(tId);
// Uses Retrofit
Observable<MyResult> localObservable = mLocalDataSource.find(tId);
Observable.concat(localObservable, remoteObservable).first()
.map(new Func1<MyResult, MyResult>() {
#Override
public myResult call(MyResult result) {
if (result == null) {
throw new IllegalArgumentException();
}
return result;
}
});
And subscribe like below:
CompositeSubscription mCompositeSubscription = new CompositeSubscription();
final Subscription subscription = mRepo.find(tId
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<MyResult>() {
#Override
public void onCompleted() {
// Completed
}
#Override
public void onError(Throwable e) {
// onError
}
#Override
public void onNext(MyResult result) {
//onSuccess
}
});
mCompositeSubscription.add(subscription);
You can check this repo for RxJava + Retrofit + Realm
https://github.com/savepopulation/wikilight
Good luck!
Instead of using subscribeOn at mRealmDataSource.find(tId).subscribeOn(AndroidSchedulers.mainThread())
like said : https://stackoverflow.com/a/39304891/2425851
You can use Observable.defer
for example:
class RealmDataSource{
fun find(id: String): Observable<MyResult> {
// Default pattern for loading data on a background thread
return Observable.defer{
val realm = Realm.getInstance()
val query = realm
.where(MyResult::class.java)
val flowable =
if (realm.isAutoRefresh) {
query
.findAllAsync()
.asFlowable()
.filter(RealmResults::isLoaded)
} else {
Flowable.just(query.findAll())
}
return#defer flowable
.toObservable()
}
}
Then usage will be without subscribeOn
// Uses a Realm
Observable<MyResult> realmObservable = mRealmDataSource.find(tId);
// Uses Retrofit
Observable<MyResult> remoteObservable = mRemoteDataSource.find(tId);
For more info see https://realm.io/blog/realm-java-0-87-0/

Is there a quick way to delete large Workflow Process history stack?

Is there any quick way/trick to delete around 85K entries for the workflow process history? Trying from the GUI gives a storage issue and to resolve this issue need to bounce the box.
Also trying the PowerTool crashes after a long time. Thought to ask the wider community. appreciate for your thoughts.
Thanks
Vin
Which version of Tridion? 2011?
You could probably get away with a CoreService client app that does this regularly for you. By "PowerTool" I assume you mean the Purge tool?
Also - I would likely contact Customer Support about the errors you see, doesn't seem like using the GUI or the Purge Tool should fail.
If you're on 2011 SP1 you could use the following code:
using System;
using System.ServiceModel;
using System.Xml;
using Tridion.ContentManager.CoreService.Client;
namespace DeleteWorkflowHistory
{
class Program
{
private const string NetTcpEndpoint =
"net.tcp://localhost:2660/CoreService/2011/netTcp";
private static readonly EndpointAddress EndpointAddress =
new EndpointAddress(NetTcpEndpoint);
static void Main(string[] args)
{
var binding = new NetTcpBinding
{
MaxReceivedMessageSize = 2147483647
};
var quota = new XmlDictionaryReaderQuotas
{
MaxStringContentLength = 2147483647,
MaxArrayLength = 2147483647
};
binding.ReaderQuotas = quota;
var client = new SessionAwareCoreServiceClient(binding, EndpointAddress);
Log("Connected to Tridion Content Manager version: " + client.GetApiVersion());
ProcessesFilterData filter = new ProcessesFilterData
{
BaseColumns = ListBaseColumns.IdAndTitle,
ProcessType = ProcessType.Historical
};
foreach (IdentifiableObjectData data in client.GetSystemWideList(filter))
{
var processHistory = data as ProcessHistoryData;
if (processHistory != null)
{
Log("Deleting history: " + processHistory.Id + " / " + processHistory.Title);
client.Delete(processHistory.Id);
}
}
client.Close();
}
private static void Log(string message)
{
Console.WriteLine(string.Format("[{0}] {1}", DateTime.Now.ToString("HH:mm:ss.fff"), message));
}
}
}
N
If you can't use the Core Service, have a look at this blog entry, which describes using the Powershell to force workflow processes to complete. With some very minor modifications, the same technique would work for deleting workflow processes.

Resources