I want to create a composite Windows Workflow Activity (under .NET 4) that contains a predefined ReceiveAndSendReply Activity. Some of the properties are predefined, but others (particularly ServiceContractName) need to be set in the designer.
I could implement this as an Activity Template (the same way ReceiveAndSendReply is implemented), but would rather not. If I later change the template, I'd have to update all previously created workflows manually. A template would also permit other developers to change properties that should be fixed.
Is there a way to do this from a Xaml Activity? I have not found a way to assign an Argument value to a property of an embedded Activity. If not, what technique would you suggest?
I haven't done this using a composite XAML activity and am getting some errors when I try but doing so through a NativeActivity is no problem. See the example code below.
public class MyReceiveAndSendReply : NativeActivity
{
private Receive _receive;
private SendReply _sendReply;
public string ServiceContractName { get; set; }
public string OperationName { get; set; }
protected override bool CanInduceIdle
{
get { return true; }
}
protected override void CacheMetadata(NativeActivityMetadata metadata)
{
_receive = _receive ?? new Receive();
_sendReply = _sendReply ?? new SendReply();
_receive.CanCreateInstance = true;
metadata.AddImplementationChild(_receive);
metadata.AddImplementationChild(_sendReply);
_receive.ServiceContractName = ServiceContractName;
_receive.OperationName = OperationName;
var args = new ReceiveParametersContent();
args.Parameters["firstName"] = new OutArgument<string>();
_receive.Content = args;
_sendReply.Request = _receive;
var results = new SendParametersContent();
results.Parameters["greeting"] = new InArgument<string>("Hello there");
_sendReply.Content = results;
base.CacheMetadata(metadata);
}
protected override void Execute(NativeActivityContext context)
{
context.ScheduleActivity(_receive, ReceiveCompleted);
}
private void ReceiveCompleted(NativeActivityContext context, ActivityInstance completedInstance)
{
context.ScheduleActivity(_sendReply);
}
}
Related
we have ASP.NET Core 6.0 website and want to write logging data into CosmosdDB.
Therefor i have a static CosmosDB Manager, which has the CosmosDBContext as static property, and the add method uses this context to AddLog() and SaveContext().
I had to put the Context in a lock, as multiple calls to the AddLog() method caused an error.
Is this the right way to have (concurrent) Writes to the CosmosDB?
I had previously separate Context for each request, but this opened to many threads...
Any thoughts on this Implementation?
public static class LogManager {
private static CosmosDBContext Context { get; set; }
public static void AddLog(string data) {
var item = new LogItem {
Data = data
};
if (Context == null) Context = new();
lock (Context) {
Context.Add(item);
Context.SaveChanges();
}
}
}
public class LogItem {
public string Data { get; set; }
}
I am looking at implementing some pretty simple client side validation by implementing the IClientModelValidator interface. Specifically I am creating a NotEqualTo (and later an EqualTo) validation attribute that will compare the value of one input to another.
To provide a nice UX I want to use the display name of both inputs in the error messages: "Password cannot be the same as Email" for example.
This is has obviously been done a million times and there are plenty of example around, but they are either for previous versions of MVC or are not using the display name of the other property.
Below is what I have so far. I have managed to grab the display name via the Display attribute in the server side IsValid(...) method, but I can't work out how to do similar for the client side AddValidation(...) method.
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class NotEqualToAttribute : ValidationAttribute, IClientModelValidator
{
private const string defaultErrorMessage = "{0} cannot be the same as {1}.";
public string OtherProperty { get; private set; }
public NotEqualToAttribute(string otherProperty) : base(defaultErrorMessage)
{
this.OtherProperty = otherProperty;
}
public override string FormatErrorMessage(string name)
{
string.Format(base.ErrorMessageString, name, this.OtherProperty);
}
public void AddValidation(ClientModelValidationContext context)
{
context.Attributes.Add("data-val", "true");
context.Attributes.Add("data-val-notequalto", this.FormatErrorMessage(context.ModelMetadata.GetDisplayName());
context.Attributes.Add("data-val-notequalto-otherproperty", this.otherProperty);
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (value == null)
return ValidationResult.Success;
PropertyInfo otherProperty = validationContext.ObjectInstance.GetType().GetProperty(this.OtherProperty);
object otherValue = otherProperty.GetValue(validationContext.ObjectInstance, null);
if (!value.Equals(otherValue))
return ValidationResult.Success;
DisplayAttribute display = otherProperty.GetCustomAttribute<DisplayAttribute>();
string otherName = display?.GetName() ?? this.OtherProperty;
return new ValidationResult(string.Format(defaultErrorMessage, validationContext.DisplayName, otherName));
}
}
Typically I solved this myself after taking a break, just going to leave this here in case it helps someone else (or there is a better solution):
public void AddValidation(ClientModelValidationContext context)
{
context.Attributes.Add("data-val", "true");
string otherName =
context.ModelMetadata.ContainerMetadata.Properties
.Single(p => p.PropertyName == this.OtherProperty)
.GetDisplayName();
context.Attributes.Add("data-val-notequalto",
string.Format(defaultErrorMessage, context.ModelMetadata.GetDisplayName(), otherName)
);
}
You can get to the meta data for the other properties via ClientModelValidationContext.ModelMetadata.ContainerMetadata.Properties
I need to make a query against a document collection that matches several properties.
(Cross post from the mailing list: https://groups.google.com/forum/?fromgroups=#!topic/ravendb/r5f1zr2jd_o)
Here is the document:
public class SessionToken
{
[JsonProperty("jti")]
public string Id { get; set; }
[JsonProperty("aud")]
public Uri Audience { get; set; }
[JsonProperty("sub")]
public string Subject { get; set; }
[JsonProperty("claims")]
public Dictionary<string, string> Claims { get; set; }
}
And here is the test:
[TestFixture]
public class RavenDbTests
{
private IDocumentStore documentStore;
[SetUp]
public void SetUp()
{
this.documentStore = new EmbeddableDocumentStore() { RunInMemory = true };
this.documentStore.Initialize();
}
[Test]
public async void FirstOrDefault_WhenSessionTokenExists_ShouldReturnSessionToken()
{
var c = new SessionToken()
{
Audience = new Uri("http://localhost"),
Subject = "NUnit",
Claims = new Dictionary<string, string>()
{
{ ClaimTypes.System, "NUnit" }
}
};
using (var session = this.documentStore.OpenAsyncSession())
{
await session.StoreAsync(c);
await session.SaveChangesAsync();
// Check if the token exists in the database without using Where clause
var allTokens = await session.Query<SessionToken>().ToListAsync();
Assert.That(allTokens.Any(x => x.Subject == "NUnit" && x.Audience == new Uri("http://localhost")));
// Try getting token back with Where clause
var token = await session.Query<SessionToken>().Customize(x => x.WaitForNonStaleResults()).Where(x => x.Subject == "NUnit" && x.Audience == new Uri("http://localhost")).ToListAsync();
Assert.IsNotNullOrEmpty(token.First().Id);
}
}
}
The last Assert is the one that is failing.
I must admit Im not sure whether this is a bug or a failure on my part.
As far as I understand, this is supposed to work.
PS. I´ve tried with a standalone document store as well as embedded without running in memory, but with same result.
You are getting stale results. In a unit test, you need to allow time for indexing to occur.
Add .Customize(x=> x.WaitForNonStaleResults()) to your queries and the test should pass.
Also, I think you left the Id property off your question when you cut/paste because it doesn't compile as-is.
UPDATE
Per discussion in comments, the issue was that you were applying the [JsonProperty] attribute to the Id property. Since the Id property represents the document key, and is not serialized as part of the JSON document, you can't apply the [JsonProperty] attribute to it.
I have the same issue as this question on MSDN, but I don't understand the solution because it is still not clear to me if Roman Kiss's solution will correctly replace an endpoint address while a single workflow instance being executed concurrently.
When internal Send activity is scheduled for execution by one thread with certain enpoint address, wouldn't this address be overridden by another thread that schedules same activity with different endpoint address? Correct me if I am mistaken, but I assume it would, because Send.Endpoint is a regular property as oppose to being InArgument<Endpoint> bound to whatever current workflow execution context is.
Can someone shed more light onto this?
UPDATE
I tested the solution provided by Roman Kiss, and it turns out that it is not working as expected in my scenario. I modified Execute method as follows:
protected override void Execute(NativeActivityContext context)
{
Thread.Sleep(Address.Get(context).EndsWith("1") ? 1000 : 0);
Body.Endpoint.Binding = GetBinding(Binding.Get(context));
Body.Endpoint.AddressUri = new Uri(Address.Get(context));
Thread.Sleep(Address.Get(context).EndsWith("1") ? 0 : 3000);
var address = Address.Get(context) + " => " + Body.Endpoint.AddressUri;
Console.WriteLine(address);
Thread.Sleep(10000);
context.ScheduleActivity(Body);
}
Ran this test:
static void Main(string[] args)
{
// Workflow1 is just a SendScope wrapped around by a Sequence with single Address input argument exposed
var workflow = new Workflow1();
Task.WaitAll(
Task.Run(() => WorkflowInvoker.Invoke(workflow, new Dictionary<string, object> { { "Address", #"http://localhost/1" } })),
Task.Run(() => WorkflowInvoker.Invoke(workflow, new Dictionary<string, object> { { "Address", #"http://localhost/2" } })));
Console.ReadLine();
}
The result I am getting is:
http://localhost/1 => http://localhost/1
http://localhost/2 => http://localhost/1
The question remains open: how do I assign endpoint address of my Send activity dynamically at runtime?
This will work as shown because a new Send activity is created by the factory and so when using the CacheMetadata method to setup that Send activity it is setting the binding properly on that instance of the activity.
Including Content Incase Link Dies
[ContentProperty("Body")]
public class SendScope : NativeActivity
{
[DefaultValue((string)null)]
[RequiredArgument]
public InArgument<string> Binding { get; set; }
[DefaultValue((string)null)]
[RequiredArgument]
public InArgument<string> Address { get; set; }
[Browsable(false)]
public Send Body { get; set; }
protected override void CacheMetadata(NativeActivityMetadata metadata)
{
if (this.Body == null || this.Body.EndpointAddress != null)
{
metadata.AddValidationError("Error ...");
return;
}
this.Body.Endpoint = new Endpoint()
{
AddressUri = new Uri("http://localhost/"),
Binding = new BasicHttpBinding(),
ServiceContractName = this.Body.ServiceContractName
};
metadata.AddChild(this.Body);
base.CacheMetadata(metadata);
}
protected override void Execute(NativeActivityContext context)
{
this.Body.Endpoint.Binding = GetBinding(this.Binding.Get(context));
this.Body.Endpoint.AddressUri = new Uri(this.Address.Get(context));
context.ScheduleActivity(Body);
}
private System.ServiceModel.Channels.Binding GetBinding(string binding)
{
if (binding == "basicHttpBinding")
return new BasicHttpBinding();
//else ... others bindings
return null;
}
}
public class SendScopeFactory : IActivityTemplateFactory
{
public Activity Create(DependencyObject target)
{
return new SendScope()
{
DisplayName = "SendScope",
Body = new Send()
{
Action = "*",
OperationName = "ProcessMessage",
ServiceContractName = "IGenericContract",
}
};
}
}
Create a custom native activity for setting Send.Endpoint property during the runtime based on your properties such as Binding, Address, Security, etc.
Create designer for this SendScope activity something simular like CorrelationScope
Create SendScopeFactory - see the above code snippet.
I'm personally committed to .net distributed caching solutions, but I think this question is interesting across all platforms.
Is there a distributed caching solution (or generic strategy) that allows to both store objects in the cache while maintaining the integrity of the references between them?
To exemplify - Suppose I have an object Foo foo that references an object Bar bar and also and object Foo foo2 that references that same Bar bar. If I load foo to the cache, a copy of bar is stored along with it. If I also load foo2 to the cache, a separate copy of bar is stored along with that. If I change foo.bar in the cache, the change does not impact foo2.bar :(
Is there an existing distributed cache solution that will enable me to load foo, foo2 and bar into the cache while maintaining the foo.bar foo2.bar references?
First and foremost
I do not know of any distributed system, and I do not pretend to build one. This post explains how you can simulate this behavior with .NET and C# using the IObjectReference interface with serializable objects.
Now, lets go on with the show
I do not know of such a distributed system, but you can somewhat easily achive this with .NET using the IObjectReference interface. Your implementation of ISerializable.GetObjectData would need to call SerializationInfo.SetType to point out a proxy class that implements IObjectReference, and would be able (with help from data provided by your GetObjectData method) to get a reference to the real object that should be used.
Example code:
[Serializable]
internal sealed class SerializationProxy<TOwner, TKey> : ISerializable, IObjectReference {
private const string KeyName = "Key";
private const string InstantiatorName = "Instantiator";
private static readonly Type thisType = typeof(SerializationProxy<TOwner, TKey>);
private static readonly Type keyType = typeof(TKey);
private static readonly Type instantiatorType = typeof(Func<TKey, TOwner>);
private readonly Func<TKey, TOwner> _instantiator;
private readonly TKey _key;
private SerializationProxy() {
}
private SerializationProxy(SerializationInfo info, StreamingContext context) {
if (info == null) throw new ArgumentNullException("info");
_key = (TKey)info.GetValue(KeyName, keyType);
_instantiator = (Func<TKey, TOwner>)info.GetValue(InstantiatorName, instantiatorType);
}
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) {
throw new NotSupportedException("This type should never be serialized.");
}
object IObjectReference.GetRealObject(StreamingContext context) {
return _instantiator(_key);
}
internal static void PrepareSerialization(SerializationInfo info, TKey key, Func<TKey, TOwner> instantiator) {
if (info == null) throw new ArgumentNullException("info");
if (instantiator == null) throw new ArgumentNullException("instantiator");
info.SetType(thisType);
info.AddValue(KeyName, key, keyType);
info.AddValue(InstantiatorName, instantiator, instantiatorType);
}
}
This code would be called with SerializationProxy.PrepareSerialization(info, myKey, myKey => LoadedInstances.GetById(myKey)) from your GetObjectData method, and your LoadedInstances.GetById should return the instance from a Dictionary<TKey, WeakReference> or load it from cache/database if it isnt already loaded.
EDIT:
I've wrote some example code to show what I mean.
public static class Program {
public static void Main() {
// Create an item and serialize it.
// Pretend that the bytes are stored in some magical
// domain where everyone lives happily ever after.
var item = new Item { Name = "Bleh" };
var bytes = Serialize(item);
{
// Deserialize those bytes back into the cruel world.
var loadedItem1 = Deserialize<Item>(bytes);
var loadedItem2 = Deserialize<Item>(bytes);
// This should work since we've deserialized identical
// data twice.
Debug.Assert(loadedItem1.Id == loadedItem2.Id);
Debug.Assert(loadedItem1.Name == loadedItem2.Name);
// Notice that both variables refer to the same object.
Debug.Assert(ReferenceEquals(loadedItem1, loadedItem2));
loadedItem1.Name = "Bluh";
Debug.Assert(loadedItem1.Name == loadedItem2.Name);
}
{
// Deserialize those bytes back into the cruel world. (Once again.)
var loadedItem1 = Deserialize<Item>(bytes);
// Notice that we got the same item that we messed
// around with earlier.
Debug.Assert(loadedItem1.Name == "Bluh");
// Once again, force the peaceful object to hide its
// identity, and take on a fake name.
loadedItem1.Name = "Blargh";
var loadedItem2 = Deserialize<Item>(bytes);
Debug.Assert(loadedItem1.Name == loadedItem2.Name);
}
}
#region Serialization helpers
private static readonly IFormatter _formatter
= new BinaryFormatter();
public static byte[] Serialize(ISerializable item) {
using (var stream = new MemoryStream()) {
_formatter.Serialize(stream, item);
return stream.ToArray();
}
}
public static T Deserialize<T>(Byte[] bytes) {
using (var stream = new MemoryStream(bytes)) {
return (T)_formatter.Deserialize(stream);
}
}
#endregion
}
// Supercalifragilisticexpialidocious interface.
public interface IDomainObject {
Guid Id { get; }
}
// Holds all loaded instances using weak references, allowing
// the almighty garbage collector to grab our stuff at any time.
// I have no real data to lend on here, but I _presume_ that this
// wont be to overly evil since we use weak references.
public static class LoadedInstances<T>
where T : class, IDomainObject {
private static readonly Dictionary<Guid, WeakReference> _items
= new Dictionary<Guid, WeakReference>();
public static void Set(T item) {
var itemId = item.Id;
if (_items.ContainsKey(itemId))
_items.Remove(itemId);
_items.Add(itemId, new WeakReference(item));
}
public static T Get(Guid id) {
if (_items.ContainsKey(id)) {
var itemRef = _items[id];
return (T)itemRef.Target;
}
return null;
}
}
[DebuggerDisplay("{Id} {Name}")]
[Serializable]
public class Item : IDomainObject, ISerializable {
public Guid Id { get; private set; }
public String Name { get; set; }
// This constructor can be avoided if you have a
// static Create method that creates and saves new items.
public Item() {
Id = Guid.NewGuid();
LoadedInstances<Item>.Set(this);
}
#region ISerializable Members
public void GetObjectData(SerializationInfo info, StreamingContext context) {
// We're calling SerializationProxy to call GetById(this.Id)
// when we should be deserialized. Notice that we have no
// deserialization constructor. Fxcop will hate us for that.
SerializationProxy<Item, Guid>.PrepareSerialization(info, Id, GetById);
}
#endregion
public static Item GetById(Guid id) {
var alreadyLoaded = LoadedInstances<Item>.Get(id);
if (alreadyLoaded != null)
return alreadyLoaded;
// TODO: Load from storage container (database, cache).
// TODO: The item we load should be passed to LoadedInstances<Item>.Set
return null;
}
}