ASP.NET Identity DbContext confusion - asp.net

A default MVC 5 App comes with this piece of code in IdentityModels.cs - this piece of code is for all the ASP.NET Identity operations for the default templates:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection")
{
}
}
If I scaffold a new controller using views with Entity Framework and create a "New data context..." in the dialog, I get this generated for me:
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Web;
namespace WebApplication1.Models
{
public class AllTheOtherStuffDbContext : DbContext
{
// You can add custom code to this file. Changes will not be overwritten.
//
// If you want Entity Framework to drop and regenerate your database
// automatically whenever you change your model schema, please use data migrations.
// For more information refer to the documentation:
// http://msdn.microsoft.com/en-us/data/jj591621.aspx
public AllTheOtherStuffDbContext() : base("name=AllTheOtherStuffDbContext")
{
}
public System.Data.Entity.DbSet<WebApplication1.Models.Movie> Movies { get; set; }
}
}
If I scaffold another controller + view using EF, say for instance for an Animal model, this new line would get autogenerated right under public System.Data.Entity.DbSet<WebApplication1.Models.Movie> Movies { get; set; } - like this:
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Web;
namespace WebApplication1.Models
{
public class AllTheOtherStuffDbContext : DbContext
{
// You can add custom code to this file. Changes will not be overwritten.
//
// If you want Entity Framework to drop and regenerate your database
// automatically whenever you change your model schema, please use data migrations.
// For more information refer to the documentation:
// http://msdn.microsoft.com/en-us/data/jj591621.aspx
public AllTheOtherStuffDbContext() : base("name=AllTheOtherStuffDbContext")
{
}
public System.Data.Entity.DbSet<WebApplication1.Models.Movie> Movies { get; set; }
public System.Data.Entity.DbSet<WebApplication1.Models.Animal> Animals { get; set; }
}
}
ApplicationDbContext (for all the ASP.NET Identity stuff) inherits from IdentityDbContext which in turn inherits from DbContext.
AllOtherStuffDbContext (for my own stuff) inherits from DbContext.
So my question is:
Which of these two (ApplicationDbContext and AllOtherStuffDbContext) should I use for all my other own models? Or should I just use the default autogenerated ApplicationDbContext since it shouldn't be a problem using it since it derives from the base class DbContext, or will there be some overhead? You should use only one DbContext object in your app for all your models (I've read this somewhere) so I should not even consider using both ApplicationDbContext and AllOtherStuffDbContext in a single app? Or what is best practice in MVC 5 with ASP.NET Identity?

I would use a single Context class inheriting from IdentityDbContext.
This way you can have the context be aware of any relations between your classes and the IdentityUser and Roles of the IdentityDbContext.
There is very little overhead in the IdentityDbContext, it is basically a regular DbContext with two DbSets. One for the users and one for the roles.

There is a lot of confusion about IdentityDbContext, a quick search in Stackoverflow and you'll find these questions:
"
Why is Asp.Net Identity IdentityDbContext a Black-Box?
How can I change the table names when using Visual Studio 2013 AspNet Identity?
Merge MyDbContext with IdentityDbContext"
To answer to all of these questions we need to understand that IdentityDbContext is just a class inherited from DbContext.
Let's take a look at IdentityDbContext source:
/// <summary>
/// Base class for the Entity Framework database context used for identity.
/// </summary>
/// <typeparam name="TUser">The type of user objects.</typeparam>
/// <typeparam name="TRole">The type of role objects.</typeparam>
/// <typeparam name="TKey">The type of the primary key for users and roles.</typeparam>
/// <typeparam name="TUserClaim">The type of the user claim object.</typeparam>
/// <typeparam name="TUserRole">The type of the user role object.</typeparam>
/// <typeparam name="TUserLogin">The type of the user login object.</typeparam>
/// <typeparam name="TRoleClaim">The type of the role claim object.</typeparam>
/// <typeparam name="TUserToken">The type of the user token object.</typeparam>
public abstract class IdentityDbContext<TUser, TRole, TKey, TUserClaim, TUserRole, TUserLogin, TRoleClaim, TUserToken> : DbContext
where TUser : IdentityUser<TKey, TUserClaim, TUserRole, TUserLogin>
where TRole : IdentityRole<TKey, TUserRole, TRoleClaim>
where TKey : IEquatable<TKey>
where TUserClaim : IdentityUserClaim<TKey>
where TUserRole : IdentityUserRole<TKey>
where TUserLogin : IdentityUserLogin<TKey>
where TRoleClaim : IdentityRoleClaim<TKey>
where TUserToken : IdentityUserToken<TKey>
{
/// <summary>
/// Initializes a new instance of <see cref="IdentityDbContext"/>.
/// </summary>
/// <param name="options">The options to be used by a <see cref="DbContext"/>.</param>
public IdentityDbContext(DbContextOptions options) : base(options)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="IdentityDbContext" /> class.
/// </summary>
protected IdentityDbContext()
{ }
/// <summary>
/// Gets or sets the <see cref="DbSet{TEntity}"/> of Users.
/// </summary>
public DbSet<TUser> Users { get; set; }
/// <summary>
/// Gets or sets the <see cref="DbSet{TEntity}"/> of User claims.
/// </summary>
public DbSet<TUserClaim> UserClaims { get; set; }
/// <summary>
/// Gets or sets the <see cref="DbSet{TEntity}"/> of User logins.
/// </summary>
public DbSet<TUserLogin> UserLogins { get; set; }
/// <summary>
/// Gets or sets the <see cref="DbSet{TEntity}"/> of User roles.
/// </summary>
public DbSet<TUserRole> UserRoles { get; set; }
/// <summary>
/// Gets or sets the <see cref="DbSet{TEntity}"/> of User tokens.
/// </summary>
public DbSet<TUserToken> UserTokens { get; set; }
/// <summary>
/// Gets or sets the <see cref="DbSet{TEntity}"/> of roles.
/// </summary>
public DbSet<TRole> Roles { get; set; }
/// <summary>
/// Gets or sets the <see cref="DbSet{TEntity}"/> of role claims.
/// </summary>
public DbSet<TRoleClaim> RoleClaims { get; set; }
/// <summary>
/// Configures the schema needed for the identity framework.
/// </summary>
/// <param name="builder">
/// The builder being used to construct the model for this context.
/// </param>
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<TUser>(b =>
{
b.HasKey(u => u.Id);
b.HasIndex(u => u.NormalizedUserName).HasName("UserNameIndex").IsUnique();
b.HasIndex(u => u.NormalizedEmail).HasName("EmailIndex");
b.ToTable("AspNetUsers");
b.Property(u => u.ConcurrencyStamp).IsConcurrencyToken();
b.Property(u => u.UserName).HasMaxLength(256);
b.Property(u => u.NormalizedUserName).HasMaxLength(256);
b.Property(u => u.Email).HasMaxLength(256);
b.Property(u => u.NormalizedEmail).HasMaxLength(256);
b.HasMany(u => u.Claims).WithOne().HasForeignKey(uc => uc.UserId).IsRequired();
b.HasMany(u => u.Logins).WithOne().HasForeignKey(ul => ul.UserId).IsRequired();
b.HasMany(u => u.Roles).WithOne().HasForeignKey(ur => ur.UserId).IsRequired();
});
builder.Entity<TRole>(b =>
{
b.HasKey(r => r.Id);
b.HasIndex(r => r.NormalizedName).HasName("RoleNameIndex");
b.ToTable("AspNetRoles");
b.Property(r => r.ConcurrencyStamp).IsConcurrencyToken();
b.Property(u => u.Name).HasMaxLength(256);
b.Property(u => u.NormalizedName).HasMaxLength(256);
b.HasMany(r => r.Users).WithOne().HasForeignKey(ur => ur.RoleId).IsRequired();
b.HasMany(r => r.Claims).WithOne().HasForeignKey(rc => rc.RoleId).IsRequired();
});
builder.Entity<TUserClaim>(b =>
{
b.HasKey(uc => uc.Id);
b.ToTable("AspNetUserClaims");
});
builder.Entity<TRoleClaim>(b =>
{
b.HasKey(rc => rc.Id);
b.ToTable("AspNetRoleClaims");
});
builder.Entity<TUserRole>(b =>
{
b.HasKey(r => new { r.UserId, r.RoleId });
b.ToTable("AspNetUserRoles");
});
builder.Entity<TUserLogin>(b =>
{
b.HasKey(l => new { l.LoginProvider, l.ProviderKey });
b.ToTable("AspNetUserLogins");
});
builder.Entity<TUserToken>(b =>
{
b.HasKey(l => new { l.UserId, l.LoginProvider, l.Name });
b.ToTable("AspNetUserTokens");
});
}
}
Based on the source code if we want to merge IdentityDbContext with our DbContext we have two options:
**First Option:**
Create a DbContext which inherits from IdentityDbContext and have access to the classes.
public class ApplicationDbContext
: IdentityDbContext
{
public ApplicationDbContext()
: base("DefaultConnection")
{
}
static ApplicationDbContext()
{
Database.SetInitializer<ApplicationDbContext>(new ApplicationDbInitializer());
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
// Add additional items here as needed
}
Extra Notes:
We can also change asp.net Identity default table names with the following solution:
public class ApplicationDbContext : IdentityDbContext
{
public ApplicationDbContext(): base("DefaultConnection")
{
}
protected override void OnModelCreating(System.Data.Entity.DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<IdentityUser>().ToTable("user");
modelBuilder.Entity<ApplicationUser>().ToTable("user");
modelBuilder.Entity<IdentityRole>().ToTable("role");
modelBuilder.Entity<IdentityUserRole>().ToTable("userrole");
modelBuilder.Entity<IdentityUserClaim>().ToTable("userclaim");
modelBuilder.Entity<IdentityUserLogin>().ToTable("userlogin");
}
}
Furthermore we can extend each class and add any property to classes like 'IdentityUser', 'IdentityRole', ...
public class ApplicationRole : IdentityRole<string, ApplicationUserRole>
{
public ApplicationRole()
{
this.Id = Guid.NewGuid().ToString();
}
public ApplicationRole(string name)
: this()
{
this.Name = name;
}
// Add any custom Role properties/code here
}
// Must be expressed in terms of our custom types:
public class ApplicationDbContext
: IdentityDbContext<ApplicationUser, ApplicationRole,
string, ApplicationUserLogin, ApplicationUserRole, ApplicationUserClaim>
{
public ApplicationDbContext()
: base("DefaultConnection")
{
}
static ApplicationDbContext()
{
Database.SetInitializer<ApplicationDbContext>(new ApplicationDbInitializer());
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
// Add additional items here as needed
}
To save time we can use AspNet Identity 2.0 Extensible Project Template to extend all the classes.
Second Option:(Not recommended)
We actually don't have to inherit from IdentityDbContext if we write all the code ourselves.
So basically we can just inherit from DbContext and implement our customized version of "OnModelCreating(ModelBuilder builder)" from the IdentityDbContext source code

This is a late entry for folks, but below is my implementation. You will also notice I stubbed-out the ability to change the the KEYs default type: the details about which can be found in the following articles:
Extending Identity Models and Using Integer Keys Instead of Strings
Change Primary Key for Users in ASP.NET Identity
NOTES:
It should be noted that you cannot use Guid's for your keys. This is because under the hood they are a Struct, and as such, have no unboxing which would allow their conversion from a generic <TKey> parameter.
THE CLASSES LOOK LIKE:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser, CustomRole, string, CustomUserLogin, CustomUserRole, CustomUserClaim>
{
#region <Constructors>
public ApplicationDbContext() : base(Settings.ConnectionString.Database.AdministrativeAccess)
{
}
#endregion
#region <Properties>
//public DbSet<Case> Case { get; set; }
#endregion
#region <Methods>
#region
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
//modelBuilder.Configurations.Add(new ResourceConfiguration());
//modelBuilder.Configurations.Add(new OperationsToRolesConfiguration());
}
#endregion
#region
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
#endregion
#endregion
}
public class ApplicationUser : IdentityUser<string, CustomUserLogin, CustomUserRole, CustomUserClaim>
{
#region <Constructors>
public ApplicationUser()
{
Init();
}
#endregion
#region <Properties>
[Required]
[StringLength(250)]
public string FirstName { get; set; }
[Required]
[StringLength(250)]
public string LastName { get; set; }
#endregion
#region <Methods>
#region private
private void Init()
{
Id = Guid.Empty.ToString();
}
#endregion
#region public
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser, string> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
#endregion
#endregion
}
public class CustomUserStore : UserStore<ApplicationUser, CustomRole, string, CustomUserLogin, CustomUserRole, CustomUserClaim>
{
#region <Constructors>
public CustomUserStore(ApplicationDbContext context) : base(context)
{
}
#endregion
}
public class CustomUserRole : IdentityUserRole<string>
{
}
public class CustomUserLogin : IdentityUserLogin<string>
{
}
public class CustomUserClaim : IdentityUserClaim<string>
{
}
public class CustomRoleStore : RoleStore<CustomRole, string, CustomUserRole>
{
#region <Constructors>
public CustomRoleStore(ApplicationDbContext context) : base(context)
{
}
#endregion
}
public class CustomRole : IdentityRole<string, CustomUserRole>
{
#region <Constructors>
public CustomRole() { }
public CustomRole(string name)
{
Name = name;
}
#endregion
}

If you drill down through the abstractions of the IdentityDbContext you'll find that it looks just like your derived DbContext. The easiest route is Olav's answer, but if you want more control over what's getting created and a little less dependency on the Identity packages have a look at my question and answer here. There's a code example if you follow the link, but in summary you just add the required DbSets to your own DbContext subclass.

Related

Moq dbContext if there are entities with jsonb (postgresql)

I try to moq my DbContext like in memory db. I use PostgreSql in my app, so I have entities with jsonb properties. For example:
[Table("examples")]
public class Example
{
/// <summary>
/// id (autogenerated by DB)
/// </summary>
[Column("id", TypeName = "bigserial")]
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id { get; set; }
/// <inheritdoc/>
[Column("layout_config", TypeName = "jsonb")]
[Required]
public LayoutConfigDto LayoutConfig { get; set; }
}
[Keyless]
public class LayoutConfigDto
{
/// <summary>
/// Координата X расположения виджета
/// </summary>
public byte X { get; set; }
/// <summary>
/// Координата Y расположения виджета
/// </summary>
public byte Y { get; set; }
}
so LayoutConfigDto just a model for JSON, that doesn't need a table. And doesn't need any relation or configuration for table.
Then I create Test class:
[TestFixture]
public class ExampleServiceTests
{
private IExampleService _exampleService;
[SetUp]
public void Setup()
{
DbContextOptions<ExampleDbContext> options = new DbContextOptionsBuilder<ExampleDbContext>()
.UseInMemoryDatabase(databaseName: "InMemoryExampleDatabase")
.Options;
ExampleDbContext dbContext = new(options);
new FakeDatabaseDataGenerator().Generate(dbContext);
Mock<ILogger<ExampleService>> mock = new();
_exampleService = new ExampleService(dbContext, mock.Object);
}
[Test]
[TestCase(0)]
[TestCase(3)]
public async Task GetExampleTest(long id)
{
ExampleModel example = await _exampleService.GetExample(id);
if (id <= 0)
{
Assert.AreEqual(example, null);
return;
}
Assert.AreNotEqual(null, example);
}
}
When I run GetExampleTest it fails on Exceptions like:
System.InvalidOperationException : Unable to determine the relationship represented by navigation 'UserWidgetModel.LayoutConfig' of type 'LayoutConfigDto'. Either manually configure the relationship, or ignore this property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
I can't use [NotMapped] attribute on LayoutConfig field, because I need to get it from DB and with pgsql driver all works and serializes. But with in memory db it fails. How can I change my Test to make it works? Is there any other options to mock db context?
The error it returns is pretty clear; it tells you what you need to do. If you can't use the [NotMapped] attribute, you need to configure a relationship for the fields it needs

Display user parameter value in swagger against jSon for HttpPost methods in asp.net core 3 web application [duplicate]

Is there a way to specify example requests for swagger? Maybe even multiple ones?
The Try it out button shows only generic values like:
{
"firstName": "string",
"lastName": "string"
}
for
public class User
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
It becomes very difficult to use with large objects when you have to edit all the values first. I know I could use Postman, and I do too, but being able to create multiple good looking and useful examples with swagger would be very nice.
In .Net5 you can add a SchemaFilter to Swagger in the Startup.cs
public override void ConfigureServices(IServiceCollection services)
{
services.AddSwaggerGen(c =>
{
c.SchemaFilter<ExampleSchemaFilter>();
});
}
In the ExampleSchemaFilter.cs you simply define an OpenApiObject for your specific class:
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
public class ExampleSchemaFilter : ISchemaFilter
{
public void Apply(OpenApiSchema schema, SchemaFilterContext context)
{
if (context.Type == typeof(User))
{
schema.Example = new OpenApiObject()
{
["firstName"] = new OpenApiString("John"),
["lastName"] = new OpenApiString("Doe"),
};
}
}
}
With ASP.NET Core 3.1, Swagger OAS 3 and Swashbuckle.AspNetCore 5.4.1, the following model class + XML comments works for me:-
/// <summary>
/// A user.
/// </summary>
public class User
{
/// <summary>
/// The user's first name.
/// </summary>
/// <example>Jane</example>
public string FirstName { get; set; }
/// <summary>
/// The user's last name.
/// </summary>
/// <example>Austin</example>
public string LastName { get; set; }
}
Now when I click "Try it Out" (for a POST operation that takes a User model in the message body), I get the defaults:-
{
"firstName": "Jane",
"lastName": "Austin"
}

When I query my REST service with Breeze JS, the parameter is not passed

I am working on a SPA with AngularJS and BreezeJS. The backend is implemented on ASP.NET Core MVC 6 and EF 6.1.3.
The REST service is running, but when I query the service with Breeze JS, the parameter is not passed.
E.g.
[HttpGet]
[EnableQuery]
public IQueryable<Event> Events()
{
return this._contextProvider.Context.Events;
}
I tried to call the service over the browser .../Context/Events?$filter=(Id%20eq%202). The result is still the same the filter is not apply.
What have I missed? How can I add OData to MVC6?
My Controller:
namespace FleetManagement.WebApi.Controllers
{
/// <summary>Main REST / Breeze controller for the Fleet Management module.</summary>
/// <seealso cref="Microsoft.AspNetCore.Mvc.Controller"/>
[BreezeController]
[EnableCors("AllowAll")]
[EnableQuery]
[EnableBreezeQuery]
public class ContextController : Controller
{
#region Fields.
private readonly D4GEFContextProvider<FleetManagementContext> _contextProvider;
#endregion
#region Constructors / Desctructor.
/// <summary>Initializes a new instance of the <see cref="ContextController"/> class.</summary>
public ContextController()
{
var connectionString = "Data Source=lomoofsv14\\d4g;Initial Catalog=Test_Westfalen_D4G_Custom_RandomData;Integrated Security=True;MultipleActiveResultSets=True;Application Name=FMREST";
this._contextProvider = new D4GEFContextProvider<FleetManagementContext>(connectionString);
}
#endregion
#region Public Methods.
/// <summary>Get a list of assets to a given asset filter and data profile.</summary>
/// <param name="filters">The filters for the asset types is a string of type names e.g. 'Tractor, Trailer'.</param>
/// <param name="profileUId">The profile identifier for the data filter.</param>
/// <returns>IEnumerable<Asset>.</returns>
[HttpGet]
public IQueryable<Asset> Assets(string filters, string profileUId)
{
return this._contextProvider.Context.GetAssets(filters, profileUId);
}
/// <summary>Assets the types.</summary>
/// <returns>IQueryable<System.String>.</returns>
[HttpGet]
public IQueryable<string> AssetTypes()
{
return Enum.GetNames(typeof(AssetTypes))
.AsQueryable();
}
/// <summary>Data collection "Events".</summary>
/// <returns>IQueryable<Event>.</returns>
[HttpGet]
public IQueryable<Event> Events()
{
return this._contextProvider.Context.Events;
}
/// <summary>Metadata of the EF DBContext necessary for Breeze to build the JSON objects.</summary>
/// <returns>System.String.</returns>
[HttpGet]
public string Metadata()
{
return this._contextProvider.Metadata();
}
/// <summary>Saves all changes to the database.</summary>
/// <param name="saveBundle">The save bundle.</param>
/// <returns>SaveResult.</returns>
[HttpPost]
public SaveResult SaveChanges(JObject saveBundle)
{
this._contextProvider.BeforeSaveEntityDelegate = this.BeforeSaveEntityDelegate;
this._contextProvider.AfterSaveEntitiesDelegate = this.AfterSaveEntitiesDelegate;
return this._contextProvider.SaveChanges(saveBundle);
}
[HttpGet]
public IQueryable<Tractor> Tractors()
{
return this._contextProvider.Context.Tractors;
}
[HttpGet]
public IQueryable<Trailer> Trailers()
{
return this._contextProvider.Context.Trailers;
}
#endregion
#region Private Methods.
private void AfterSaveEntitiesDelegate(Dictionary<Type, List<EntityInfo>> saveMap, List<KeyMapping> keyMappings)
{
// TractorToEvent Assign Event to tractor
}
}
}

ASP.NET Inject Identity

I am trying to inject UserManager and UserManager with Ninject. But it gives an error.
What is wrong with the binding?
UserStore
public class UserStoreModule : NinjectModule
{
/// <summary>
/// The load.
/// </summary>
public override void Load()
{
Bind<IUserStore<Account>>()
.To<UserStore<Account>>()
.InRequestScope().WithConstructorArgument("context", context => Kernel.Get<RovinMediaContext>());
}
}
UserManager
public class UserManagerModule : NinjectModule
{
/// <summary>
/// The load.
/// </summary>
public override void Load()
{
Kernel.Bind<UserManager<Account>>().ToSelf();
}
}
Result to the following error
Error activating string No matching bindings are available, and the type is not self-bindable.
Activation path:
2) Injection of dependency string into parameter connectionString of constructor of type RovinMediaContext
1) Request for MyDbContext

dynamic load recive activity wf

I'm trying to load and invoke activityes from custom activity as follows:
Imagine I have a xamlx like this:
--Sequence
|----- LoadActiviy
|--Initialize dictionary with input data of activitity
|----- Invoke
This works when activity NOT CONTAINS receive/send messages. But when i try with activity wich contains receive/send messages the result is a exception
WorkflowApplicationUnhandledExceptionEventArgs: Only registered bookmark scopes can be used for creating scoped bookmarks.
The code:
1-Load xaml: (Load activity)
public sealed class LoadActivity : CodeActivity<Activity>
{
#region Properties
/// <summary>
/// Gets or sets Path.
/// </summary>
[RequiredArgument]
public InArgument<string> Path { get; set; }
#endregion
#region Methods
/// <summary>
/// The execute method.
/// </summary>
/// <param name="context">
/// The context.
/// </param>
/// <returns>
/// An activity loaded from a file
/// </returns>
protected override Activity Execute(CodeActivityContext context)
{
return ActivityXamlServices.Load(this.Path.Get(context));
}
#endregion
}
2- Run activity:
public class SynchronousSynchronizationContext : SynchronizationContext
{
public override void Post(SendOrPostCallback d, object state)
{
d(state);
}
}
public sealed class Invoke : CodeActivity
{
#region Properties
/// <summary>
/// Gets or sets Activity.
/// </summary>
/// <remarks>
/// The activity that will be invoked. Can be loaded from XAML.
/// </remarks>
[RequiredArgument]
public InArgument<Activity> Activity { get; set; }
public OutArgument<IDictionary<string, object>> Output { get; set; }
/// <summary>
/// Gets or sets Input.
/// </summary>
/// <remarks>
/// The input arguments you want to pass to the other workflow
/// </remarks>
public InArgument<IDictionary<string, object>> Input { get; set; }
#endregion
// If your activity returns a value, derive from CodeActivity<TResult>
// and return the value from the Execute method.
protected override void Execute(CodeActivityContext context)
{
try
{
IDictionary<string,object> _input= this.Input.Get(context);
foreach (KeyValuePair<string,object> item in _input )
{
Debug.WriteLine(string.Format("{0} {1}", item.Key, item.Value));
}
// AutoResetEvent idleEvent = new AutoResetEvent(false);
WorkflowApplication app = new WorkflowApplication(this.Activity.Get(context),this.Input.Get(context));
app.SynchronizationContext = new SynchronousSynchronizationContext();
app.Completed = delegate(WorkflowApplicationCompletedEventArgs e)
{
// idleEvent.Set();
};
app.OnUnhandledException = delegate(WorkflowApplicationUnhandledExceptionEventArgs e)
{
// Display the unhandled exception.
Console.WriteLine("OnUnhandledException in Workflow {0}\n{1}",
e.InstanceId, e.UnhandledException.Message);
Console.WriteLine("ExceptionSource: {0} - {1}",
e.ExceptionSource.DisplayName, e.ExceptionSourceInstanceId);
// Instruct the runtime to terminate the workflow.
// Other choices are Abort and Cancel. Terminate
// is the default if no OnUnhandledException handler
// is present.
return UnhandledExceptionAction.Terminate;
};
app.Idle = e => Console.WriteLine("WorkflowApplication.Idle called");
Console.WriteLine("Before WorkflowApplication.Run()");
app.Run();
}
catch
{
throw;
}
}
}
Any ideas?
You can only use a Receive activity in a workflow hosted in a WorkflowServiceHost. Even if your main workflow is hosted in a WorkflowServiceHost the child workflow is hosted in a WorkflowApplication and can't contain a Receive activity because it isn't running as part of the WCF infrastructure.

Resources