I'm trying to use the ABP's identity module and have a seed for my first (admin) user.
In the identity module seed contributor's source code I see this:
public Task SeedAsync(DataSeedContext context)
{
return _identityDataSeeder.SeedAsync(
context["AdminEmail"] as string ?? "admin#abp.io",
context["AdminPassword"] as string ?? "1q2w3E*",
context.TenantId
);
}
So in my migrator module I added this:
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
using (var scope = context.ServiceProvider.CreateScope())
{
var dataSeeder = scope.ServiceProvider.GetRequiredService<IDataSeeder>();
var dsCtx = new DataSeedContext
{
["AdminEmail"] = "my#admin-email",
["AdminPassword"] = "my-admin-password"
};
AsyncHelper.RunSync(() => dataSeeder.SeedAsync(dsCtx));
}
base.OnApplicationInitialization(context);
}
This works... however there's probably another module creating a data seeder (more likely the one that actually gets executed on the migrator, but I can't really find it), so all my contributors (and probably the module contributors) get executed twice (that's to be expected, I guess).
Is there any way I can change the seeding context without actually running an IDataSeeder? and if this can't be done... is there a way I can "unregister" all IDataSeeders previous to mine so only mine gets executed?
The solution to this specific question (although I was hoping to find a more "general" solution), was to change the actual contributor. On your domain module (or wherever you see fit: your migrator or whatever), just do:
// Remove the contributor for the module
context.Services.RemoveAll(t => t.ImplementationType == typeof(IdentityDataSeedContributor));
// Add my custom constributor
context.Services.AddTransient<IDataSeedContributor, MyCustomConstributor>();
Where the implementation of the contributor is simply a copy of the default:
public class MyCustomConstributor : IDataSeedContributor
{
private readonly IIdentityDataSeeder _identityDataSeeder;
public IdentityDataSeedContributor(IIdentityDataSeeder identityDataSeeder)
{
_identityDataSeeder = identityDataSeeder;
}
public Task SeedAsync(DataSeedContext context)
{
return _identityDataSeeder.SeedAsync(
context["AdminEmail"] as string ?? "my#admin-email",
context["AdminPassword"] as string ?? "my-admin-password",
context.TenantId
);
}
}
Notice that you still get the username admin here... if you want to change it, you can just replace also the IIdentityDataSeeder implementation (using the same method, or the easier Services.Replace, which you can use since there should only be one implementation of IIdentityDataSeeder) and copy your own from the default one, changing the searched username.
For now, replacing the services on your module seems the way to go. Maybe the possibility to directly intercept the initialization stages of other modules might be there on future versions, but I haven't seen how for now.
Related
I am checking for the possibility to build custom roslyn analyzer for case specifics to our system.
The solution is in .net Framework 4.8. I started with the tutorial How to write csharp analyzer code fix and am making my way from there.
The first case I want to check is that when the programmer use the value of a specific service they must not assume that the result is not null.
Take this service definition :
public interface IConfigurationService
{
Task<IConfiguration> GetConfiguration(string clientId);
}
And a code sample to analyze :
public async Task DoSomeTask(string clientId)
{
var configuration = await _configurationService.GetConfiguration(clientId);
// This line should raise a warning because this specific client may not be configurated
var serviceUri = configuration.ServiceUri;
DoSomeSubTask(serviceUri);
}
So far I got this
public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();
// The goal is to target the variable declaration (var configuration = ...)
context.RegisterSyntaxNodeAction(
AnalyzeDecalaration,
SyntaxKind.LocalDeclarationStatement
);
}
private static void AnalyzeDecalaration(SyntaxNodeAnalysisContext context)
{
// Check for the type of the variable and exit if it is not 'IConfiguration'
var symbolInfo = context.SemanticModel.GetSymbolInfo(localDeclaration.Declaration.Type);
var typeSymbol = symbolInfo.Symbol;
if (typeSymbol.Name != "IConfiguration")
{
return;
}
// Stuck here. I'm pretty sure dataFlowAnalysis is the key, but I can't figure how to use it
var dataFlowAnalysis = context.SemanticModel.AnalyzeDataFlow(localDeclaration);
var variable = localDeclaration.Declaration.Variables.Single();
ISymbol variableSymbol = context.SemanticModel.GetDeclaredSymbol(
variable,
context.CancellationToken
);
}
So that's where I am. I have targeted variable declaration for the target type. Not very much.
Since it is a specific case for a specific type, the analysis does not have to be very fancy. For exemple, I don't need to check for instanaces of IConfiguration inside an array, that's not a thing in our code base. Basically juste property access without null check.
I have a requirement where we need a plugin to retrieve a session id from an external system and cache it for a certain time. I use a field on the entity to test if the session is actually being cached. When I refresh the CRM form a couple of times, from the output, it appears there are four versions (at any time consistently) of the same key. I have tried clearing the cache and testing again, but still the same results.
Any help appreciated, thanks in advance.
Output on each refresh of the page:
20170511_125342:1:55a4f7e6-a1d7-e611-8100-c4346bc582c0
20170511_125358:1:55a4f7e6-a1d7-e611-8100-c4346bc582c0
20170511_125410:1:55a4f7e6-a1d7-e611-8100-c4346bc582c0
20170511_125342:1:55a4f7e6-a1d7-e611-8100-c4346bc582c0
20170511_125437:1:55a4f7e6-a1d7-e611-8100-c4346bc582c0
20170511_125358:1:55a4f7e6-a1d7-e611-8100-c4346bc582c0
20170511_125358:1:55a4f7e6-a1d7-e611-8100-c4346bc582c0
20170511_125437:1:55a4f7e6-a1d7-e611-8100-c4346bc582c0
To accomplish this, I have implemented the following code:
public class SessionPlugin : IPlugin
{
public static readonly ObjectCache Cache = MemoryCache.Default;
private static readonly string _sessionField = "new_sessionid";
#endregion
public void Execute(IServiceProvider serviceProvider)
{
var context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
try
{
if (context.MessageName.ToLower() != "retrieve" && context.Stage != 40)
return;
var userId = context.InitiatingUserId.ToString();
// Use the userid as key for the cache
var sessionId = CacheSessionId(userId, GetSessionId(userId));
sessionId = $"{sessionId}:{Cache.Select(kvp => kvp.Key == userId).ToList().Count}:{userId}";
// Assign session id to entity
var entity = (Entity)context.OutputParameters["BusinessEntity"];
if (entity.Contains(_sessionField))
entity[_sessionField] = sessionId;
else
entity.Attributes.Add(new KeyValuePair<string, object>(_sessionField, sessionId));
}
catch (Exception e)
{
throw new InvalidPluginExecutionException(e.Message);
}
}
private string CacheSessionId(string key, string sessionId)
{
// If value is in cache, return it
if (Cache.Contains(key))
return Cache.Get(key).ToString();
var cacheItemPolicy = new CacheItemPolicy()
{
AbsoluteExpiration = ObjectCache.InfiniteAbsoluteExpiration,
Priority = CacheItemPriority.Default
};
Cache.Add(key, sessionId, cacheItemPolicy);
return sessionId;
}
private string GetSessionId(string user)
{
// this will be replaced with the actual call to the external service for the session id
return DateTime.Now.ToString("yyyyMMdd_hhmmss");
}
}
This has been greatly explained by Daryl here: https://stackoverflow.com/a/35643860/7708157
Basically you are not having one MemoryCache instance per whole CRM system, your code simply proves that there are multiple app domains for every plugin, so even static variables stored in such plugin can have multiple values, which you cannot rely on. There is no documentation on MSDN that would explain how the sanboxing works (especially app domains in this case), but certainly using static variables is not a good idea.Of course if you are dealing with online, you cannot be sure if there is only single front-end server or many of them (which will also result in such behaviour)
Class level variables should be limited to configuration information. Using a class level variable as you are doing is not supported. In CRM Online, because of multiple web front ends, a specific request may be executed on a different server by a different instance of the plugin class than another request. Overall, assume CRM is stateless and that unless persisted and retrieved nothing should be assumed to be continuous between plugin executions.
Per the SDK:
The plug-in's Execute method should be written to be stateless because
the constructor is not called for every invocation of the plug-in.
Also, multiple system threads could execute the plug-in at the same
time. All per invocation state information is stored in the context,
so you should not use global variables or attempt to store any data in
member variables for use during the next plug-in invocation unless
that data was obtained from the configuration parameter provided to
the constructor.
Reference: https://msdn.microsoft.com/en-us/library/gg328263.aspx
I am trying to figure out how to remove a command from the discord bot after it has been created. Here is how I create the command:
_commandService.CreateCommand("create").Parameter("message", ParameterType.Multiple).Do(async e =>
{
var message = e.Args.Skip(1).Aggregate("", (current, t) => current + (t + " "));;
_commandService.CreateCommand("hello").Do(async cc =>
{
await e.User.SendMessage(customCommand.Message);
});
});
The _commandService object is of type Discord.Commands.CommandService
Now, I want to be able to run:
_commandService.CreateCommand("delete").Parameter("message", ParameterType.Multiple).Do(async e =>
{
_commandService.DeleteCommand("hello");
});
However, no such method exists, nor am I able to access the commands inside _commandService object as everything is read only get;
Does anyone know how I can delete the command without having to restart the bot?
It's possible, but as of discord.net 1.0 you need to use the Modules system to do it. Unfortunately, it greatly complicates things. Hopefully they'll add a proper DeleteCommand(string commandName) in a future update.
Why you need to do this (this section not needed if you don't care about the discord.net source): The
class CommandMap (it stores the commands, unsurprisingly) exposes a method RemoveCommand that does what you're looking to do. The only reference to an object of this class in the source is in the private method RemoveModuleInternal in CommandService. This is exposed in one of two public methods: RemoveModuleAsync(ModuleInfo module) or RemoveModuleAsync<T>(). There is no other way to affect commands as of the 1.0 release.
How to do it: Get the ModuleInfo object for your module first. Preferably, the module you create will only contain the command you want to delete for reasons that should be obvious pretty soon. When you use CreateModuleAsync(string primaryAlias, Action<ModuleBuilder> buildFunc) (or one of the other methods used to add modules) you'll get the ModuleInfo object back. This does mean you need to use a ModuleBuilder instead of the simple commandService.CreateCommand method you use. Read up on how to do that here... if the process still confuses you, it's an excellent topic for another question.
You need to keep track of the ModuleInfo object that CreateModuleAsync returns in some manner (the method I would use is below) and then your second command becomes:
// private ModuleInfo createInfo
_commandService.CreateCommand("delete").Parameter("message", ParameterType.Multiple).Do(async e =>
{
if (createInfo != null)
{
await _commandService.DeleteModuleAsync(createInfo);
}
});
Do note that the entire module instance is getting deleted... that's why your "create" command should be the only thing in it.
An alternate solution (although significantly less elegant) if this whole Module business seems too complicated would be to store a boolean and simply toggle it to simulate the deletion of the command. That is:
// bool createNotDeleted = true;
_commandService.CreateCommand("create").Parameter("message", ParameterType.Multiple).Do(async e =>
{
if (createNotDeleted)
{
var message = e.Args.Skip(1).Aggregate("", (current, t) => current + (t + " "));;
_commandService.CreateCommand("hello").Do(async cc =>
{
await e.User.SendMessage(customCommand.Message);
});
}
else
{
// error handling
}
});
and
_commandService.CreateCommand("delete").Parameter("message", ParameterType.Multiple).Do(async e =>
{
if (createNotDeleted)
{
createNotDeleted = false
// return something indicating command is deleted
}
else
{
// error handling
}
});
I'm fiddling with Azure Functions, combining it with CQRS and event sourcing. I'm using Azure Table Storage as an Event Store. The code below is a simplified version to not distract from the problem.
I'm not interested in any code tips, since this is not a final version of the code.
public static async Task Run(BrokeredMessage commandBrokeredMessage, IQueryable<DomainEvent> eventsQueryable, IAsyncCollector<IDomainEvent> eventsCollector, TraceWriter log)
{
var command = commandBrokeredMessage.GetBody<FooCommand>();
var committedEvents = eventsQueryable.Where(e => e.PartitionKey = command.AggregateRootId);
var expectedVersion = committedEvents .Max(e => e.Version);
// some domain logic that will result in domain events
var uncommittedEvents = HandleFooCommand(command, committedEvents);
// using(Some way to lock partition)
// {
var currentVersion = eventsQueryable.Where(e => e.PartitionKey = command.AggregateRootId).Max(e => e.Version);
if(expectedVersion != currentVersion)
{
throw new ConcurrencyException("expected version is not the same as current version");
}
var i = currentVersion;
foreach (var domainEvent in uncommittedEvents.OrderBy(e => e.Timestamp))
{
i++;
domainEvent.Version = i;
await eventsCollector.AddAsync(domainEvent);
}
// }
}
public class DomainEvent : TableEntity
{
private string eventType;
public virtual string EventType
{
get { return eventType ?? (eventType = GetType().UnderlyingSystemType.Name); }
set { eventType = value; }
}
public long Version { get; set; }
}
My efforts
To be fair, I could not try anything, because I don't know where to start and if this is even possible. Id did some research which did not solve my problem, but could help you solve this problem.
Do Azure Tables support locking?
yes, they do: Managing Concurrency in Microsoft Azure Storage. It's called leasing, but I do not know how to implement this in an Azure Function.
Other sources
Azure Functions triggers and bindings developer reference
Azure Functions C# developer reference
Tips, suggestions, alternatives
I'm always open to any suggestions on how to solve problems, but I cannot accept these as an answer to my question. Unless the answer to my question is "no", I can not mark an alternative as an answer. I'm not seeking for the best way to solve my problem, I want it to work the way I engineered it. I know this is stubborn, but this is practice/fiddling.
Blob leases would indeed work pretty well for what you're trying to accomplish (the Functions runtime actually makes extensive use of that internally).
If, before working on a partition, you acquire a lease on a blob (by convention, a blob named after the partition, or something like that) you'd be able to ensure only a given function is working on that partition.
The article you've linked to does show an example of lease acquisition and release, you can find more information in the documentation.
One thing you want to ensure is that you flush your collector before you leave the lock scope (by calling FlushAsync on it)
I hope this helps!
Maybe this is dreaming, but is it possible to create an attribute that caches the output of a function (say, in HttpRuntime.Cache) and returns the value from the cache instead of actually executing the function when the parameters to the function are the same?
When I say function, I'm talking about any function, whether it fetches data from a DB, whether it adds two integers, or whether it spits out the content of a file. Any function.
Your best bet is Postsharp. I have no idea if they have what you need, but that's certainly worth checking. By the way, make sure to publish the answer here if you find one.
EDIT: also, googling "postsharp caching" gives some links, like this one: Caching with C#, AOP and PostSharp
UPDATE: I recently stumbled upon this article: Introducing Attribute Based Caching. It describes a postsharp-based library on http://cache.codeplex.com/ if you are still looking for a solution.
I have just the same problem - I have multiply expensive methods in my app and it is necessary for me to cache those results. Some time ago I just copy-pasted similar code but then I decided to factor this logic out of my domain.
This is how I did it before:
static List<News> _topNews = null;
static DateTime _topNewsLastUpdateTime = DateTime.MinValue;
const int CacheTime = 5; // In minutes
public IList<News> GetTopNews()
{
if (_topNewsLastUpdateTime.AddMinutes(CacheTime) < DateTime.Now)
{
_topNews = GetList(TopNewsCount);
}
return _topNews;
}
And that is how I can write it now:
public IList<News> GetTopNews()
{
return Cacher.GetFromCache(() => GetList(TopNewsCount));
}
Cacher - is a simple helper class, here it is:
public static class Cacher
{
const int CacheTime = 5; // In minutes
static Dictionary<long, CacheItem> _cachedResults = new Dictionary<long, CacheItem>();
public static T GetFromCache<T>(Func<T> action)
{
long code = action.GetHashCode();
if (!_cachedResults.ContainsKey(code))
{
lock (_cachedResults)
{
if (!_cachedResults.ContainsKey(code))
{
_cachedResults.Add(code, new CacheItem { LastUpdateTime = DateTime.MinValue });
}
}
}
CacheItem item = _cachedResults[code];
if (item.LastUpdateTime.AddMinutes(CacheTime) >= DateTime.Now)
{
return (T)item.Result;
}
T result = action();
_cachedResults[code] = new CacheItem
{
LastUpdateTime = DateTime.Now,
Result = result
};
return result;
}
}
class CacheItem
{
public DateTime LastUpdateTime { get; set; }
public object Result { get; set; }
}
A few words about Cacher. You might notice that I don't use Monitor.Enter() ( lock(...) ) while computing results. It's because copying CacheItem pointer ( return (T)_cachedResults[code].Result; line) is thread safe operation - it is performed by only one stroke. Also it is ok if more than one thread will change this pointer at the same time - they all will be valid.
You could add a dictionary to your class using a comma separated string including the function name as the key, and the result as the value. Then when your functions can check the dictionary for the existence of that value. Save the dictionary in the cache so that it exists for all users.
PostSharp is your one stop shop for this if you want to create a [Cache] attribute (or similar) that you can stick on any method anywhere. Previously when I used PostSharp I could never get past how slow it made my builds (this was back in 2007ish, so this might not be relevant anymore).
An alternate solution is to look into using Render.Partial with ASP.NET MVC in combination with OutputCaching. This is a great solution for serving html for widgets / page regions.
Another solution that would be with MVC would be to implement your [Cache] attribute as an ActionFilterAttribute. This would allow you to take a controller method and tag it to be cached. It would only work for controller methods since the AOP magic only can occur with the ActionFilterAttributes during the MVC pipeline.
Implementing AOP through ActionFilterAttribute has evolved to be the goto solution for my shop.
AFAIK, frankly, no.
But this would be quite an undertaking to implement within the framework in order for it to work generically for everybody in all circumstances, anyway - you could, however, tailor something quite sufficient to needs by simply (where simplicity is relative to needs, obviously) using abstraction, inheritance and the existing ASP.NET Cache.
If you don't need attribute configuration but accept code configuration, maybe MbCache is what you're looking for?