How do you set the Customvalidation in Metadata file, If the Metadata is in different Model project - wcf

My silverlight solution has 3 project files
Silverlight part(Client)
Web part(Server)
Entity model(I maintained the edmx along with Metadata in a seperate project)
Metadata file is a partial class with relavent dataannotation validations.
[MetadataTypeAttribute(typeof(User.UserMetadata))]
public partial class User
{
[CustomValidation(typeof(UsernameValidator), "IsUsernameAvailable")]
public string UserName { get; set; }
}
Now my question is where I need to keep this class UsernameValidator
If my Metadata class and edmx are on Server side(Web) then I know I need to create a .shared.cs class in my web project, then add the proper static method.
My IsUserAvailable method intern will call a domainservice method as part of asyc validation.
[Invoke]
public bool IsUsernameAvailable(string username)
{
return !Membership.FindUsersByName(username).Cast<MembershipUser>().Any();
}
If my metadata class is in the same project as my domain service is in then I can call domain service method from my UsernameValidator.Shared.cs class.
But here my entity models and Metadata are in seperate library.
Any idea will be appreciated
Jeff wonderfully explained the asyc validation here
http://jeffhandley.com/archive/2010/05/26/asyncvalidation-again.aspx
but that will work only when your model, metadata and Shared class, all are on server side.

There is a kind of hack to do this. It is not a clean way to do it it, but this is how it would probably work.
Because the .shared takes care of the code generation it doesn't complain about certain compile errors in the #if brackets of the code. So what you can do is create a Validator.Shared.cs in any project and just make sure it generates to the silverlight side.
Add the following code. and dont forget the namespaces.
#if SILVERLIGHT
using WebProject.Web.Services;
using System.ServiceModel.DomainServices.Client;
#endif
#if SILVERLIGHT
UserContext context = new UserContext();
InvokeOperation<bool> availability = context.DoesUserExist(username);
//code ommited. use what logic you want, maybe Jeffs post.
#endif
The compiler will ignore this code part because it does not meet the condition of the if statement. Meanwhile on the silverlight client side it tries to recompile the shared validator where it DOES meet the condition of the if-statement.
Like I said. This is NOT a clean way to do this. And you might have trouble with missing namespaces. You need to resolve them in the non-generated Validator.shared.cs to finally let it work in silverlight. If you do this right you can have the validation in silverlight with invoke operations. But not in your project with models and metadata like you would have with Jeff's post.
Edit: I found a cleaner and better way
you can create a partial class on the silverlight client side and doing the following
public partial class User
{
partial void OnUserNameChanging(string value)
{
//must be new to check for this validation rule
if(EntityState == EntityState.New)
{
var ctx = new UserContext();
ctx.IsValidUserName(value).Completed += (s, args) =>
{
InvokeOperation invop = (InvokeOperation) s;
bool isValid = (bool) invop.Value;
if(!isValid)
{
ValidationResult error = new ValidationResult(
"Username already exists",
new string[] {"UserName"});
ValidationErrors.Add(error;
}
};
}
}
}
This is a method generated by WCF RIA Services and can be easily partialled and you can add out-of-band validation like this. This is a much cleaner way to do this, but still this validation now only exists in the silverlight client side.
Hope this helps

Related

Access registered service from Program.cs in ASP.NET Core 6

In my Program.cs file I'm registering two repositories on builder.Services that will be used via dependency injection. However, I now have a case where I need to call one to configure the other. So essentially I need to do this:
builder.Services.AddFirstService();
var tempProvider = builder.Services.BuildServiceProvider();
var injected = tempProvider.GetRequiredService<IFirstService>();
var password = await injected.GetPasswordAsync();
builder.Services.AddSecondService(password);
That of course gives a warning about creating duplicate services. Is there a way to properly inject the first service so that I can then use it in another call?
The second service isn't something that can be modified to know about the first service.
The password is runtime data. This means you should design that component as such that it doesn't require runtime data during creation but instead allow to fetch the password lazily after it was created.
In case the component can't be changed—for instance because it comes from a third-party library—it's best to hide its use behind an abstraction. This also makes sure you adhere to the Dependency Inversion Principle, which states that:
high level modules should own the abstractions they depend on.
For instance, inside your application layer, you can create an abstraction such as the following:
public interface IApplicationTailoredAbstraction
{
Task DoSomething();
}
Application code that originally depended on the third-party component can now start to take a dependency on IApplicationTailoredAbstraction instead.
Inside your application's Composition Root, you can now create an Apapter that adapts from IApplicationTailoredAbstraction to the third-party component:
public class AppTailoredToThirdPartyComponentAdapter
: IApplicationTailoredAbstraction
{
public AppTailoredToThirdPartyComponentAdapter(IFirstService firstService) ...
// Optionally cache the password (depending on your needs)
private string password;
public async Task DoSomething()
{
if (password is null)
{
password = await firstService.GetPasswordAsync();
}
// Create the third-party library component
var service = new SecondService(password);
// Invoke its method(s)
await service.Run();
}
}
The registration of your components can now be done as follows:
builder.Services.AddFirstService();
builder.AddSingleton<IApplicationTailoredAbstraction,
AppTailoredToThirdPartyComponentAdapter>();

IdentityServer4 Authorization context as transient service

We have a running IS4 instance for corporate customers (built on top of QuickUI), now the mgmt wants to extend it with another client and they want to have a special layout and page adjustments when the authorization process comes from this client (in essence we need to collect additional info in login form and it has to be branded slightly differently).
Now, adjusting the form and visual appearance is not a problem, but deciding when to show it is proving a challenge. We have our templating and branding in _Layout which is used by all the pages, and I somehow need to know inside _layout, if the page load is part of the authentication context and if so, which one.
The way QuickUI did it in AccountController, is using IS interaction, which generates AuthenticationRequest which contains the client:
var context = await interaction.GetAuthorizationContextAsync(returnUrl);
var clientName = context.Client?.ClientName;
This fits nicely with AccountController logic and works just fine for authentication purposes, but does little for visual. I do not have the returnUrl inside _Layout (and layout is used by other pages, for registration, etc, so I cannot really cram specific viewmodels into it) and apparently no clean way to determine the context.
a) I could start digging through HttpRequest and parsing request URLs, fishing for returnUrls, but I would first like to see if there is a more streamlined, or even existing solution.
b) Another option is to have multiple layout files, and expose Client to the ViewModel and switch layouts based on it.
Ideally however, I would like something "clean" and maintainable, a service I could #inject into _Layout.cshtml and just do #if (contextService.Client == ...)
Has someone seen or done something like it?
Until (and if) someone posts a "cleaner" solution (especially the one that does not revolve around returnUrl magic string), this is the workaround I implemented. It's dirty, I admit, it pokes on HttpContext from a service, which is not really asp.net core way, but at this point, I see no other way to obtain the information I need to construct authorization context:
using System.Threading.Tasks;
using IdentityServer4.Models;
using IdentityServer4.Services;
using Microsoft.AspNetCore.Http;
namespace MyServices
{
public interface IContextService
{
Task<AuthorizationRequest> GetContextAsync();
}
public class ContextService : IContextService
{
private readonly IIdentityServerInteractionService interaction;
private readonly IHttpContextAccessor contextAccessor;
private AuthorizationRequest context;
public ContextService(IIdentityServerInteractionService interaction, IHttpContextAccessor contextAccessor)
{
this.interaction = interaction;
this.contextAccessor = contextAccessor;
}
public async Task<AuthorizationRequest> GetContextAsync() => context ??= await InternalObtainContextAsync();
private async Task<AuthorizationRequest> InternalObtainContextAsync()
{
var query = contextAccessor.HttpContext?.Request.Query;
if (query == null || query.Count == 0 || !query.ContainsKey("returnUrl")) return new AuthorizationRequest(); // empty context
return await interaction.GetAuthorizationContextAsync(query["returnUrl"]);
}
}
}
Register in startup with services.AddTransient<IContextService, ContextService>(); and then you can inject it into any page and/or layout:
#inject IContextService contextService;
#{
// render only when invoked from authorization context
var context = await contextService.GetContextAsync();
if (context.Client != null)
{
<div>Hi, I am inside authorization context for client #context.Client.ClientId</div>
}
}

CRM 2011 - ITracingService getting access to the traceInfo at runtime

I have some custom logging in my plugin and want to include the contents of my tracingService in my custom logging (which is called within a catch block, before the plugin finishes).
I cant seem to access the content of tracingService. I wonder if it is accessible at all?
I tried tracingService.ToString() just incase the devs had provided a useful overload, alas as expected I get name of the class "Microsoft.Crm.Sandbox.SandboxTracingService".
Obviously Dynamics CRM makes use of the tracingService content towards the end of the pipeline if it needs to.
Anybody have any ideas on this?
Kind Regards,
Gary
The tracing service does not provide access to the trace text during execution but that can be overcome by creating your own implementation of ITracingService. Note, you cannot get any text that was written to the trace log prior to the Execute method of your plugin being called - meaning if you have multiple plugins firing you won't get their trace output in the plugin that throws the exception.
public class CrmTracing : ITracingService
{
ITracingService _tracingService;
StringBuilder _internalTrace;
public CrmTracing(ITracingService tracingService)
{
_tracingService = tracingService;
_internalTrace = new StringBuilder();
}
public void Trace(string format, params object[] args)
{
if (_tracingService != null) _tracingService.Trace(format, args);
_internalTrace.AppendFormat(format, args).AppendLine();
}
public string GetTraceBuffer()
{
return _internalTrace.ToString();
}
}
Just instantiate it in your plugin passing in the CRM provided ITracingService. Since it is the same interface it works the same if you pass it to other classes and methods.
public class MyPlugin : IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
var tracingService = new CrmTracing((ITracingService)serviceProvider.GetService(typeof(ITracingService)));
tracingService.Trace("Works same as always.");
var trace = tracingService.GetTraceBuffer();
}
}
To get the traceInfo string from traceService at runtime I used debugger to interrogate the tracingService contents.
So the trace string is accessible from these expressions...
for Plugins
((Microsoft.Crm.Extensibility.PipelineTracingService)(tracingService)).TraceInfo
for CWA
((Microsoft.Crm.Workflow.WorkflowTracingService)(tracingService)).TraceInfo
You can drill into the tracing service by debugging and extract the expression.
However, at design time neither of these expressions seem to be accessible from any of the standard CRM 2011 SDK dlls... so not sure if its possible as yet.

Can a DomainService return a single custom type?

I would like a method on my in my domain service similar to:
public SystemState GetSystemStatus()
{
return new SystemStatus
{
InterestingStatusValue1 = 1223,
OtherInterstingStatusValue = "abc",
}
}
That doesn't work. Nothing is auto-generated for the Silverlight client app. Howerver if I make this an IQueryable method, then I get something generated on the client. I'll get a SystemStates property and a Query method on the context object.
Is there no way to make this a simple WCF call? I suppose I could a WCF Silverlight Enabled service to my RIA Web site, and then setting a Service Reference (that can't be right?) (and why can't I see the Services Reference in the Silverlight app?)
On first blush it seems that RIA services forces a very data-centric/easy CRUD which is great for table editors, but not so much for LOB applications that go behind drag on a datagrid and you're done.
You can return just one entity using an attribute (assuming that SystemState is your entity):
Ex:
[Query(IsComposable = false)]
public SystemState GetSystemStatus()
{
return new SystemStatus
{
InterestingStatusValue1 = 1223,
OtherInterstingStatusValue = "abc",
}
}
Remember that this is still a query and Ria Services will generate a method in your DomainContext like:
EntityQuery<SystemState> GetSystemStatusQuery()
Use it like a normal EntityQuery, but keep in mind that you can't perform query operations (sorting or filtering) on the returned object.
If you want to execute an operation on server, try using the [Invoke] attribute. Ex:
[Invoke]
public SystemState GetSystemStatus()
{
return new SystemStatus
{
InterestingStatusValue1 = 1223,
OtherInterstingStatusValue = "abc",
}
}
I don't know how complex your return type can be, but I guess if it can be serialized, it will work (not sure).

Passing IList<T> vs. IEnumerable<T> with protobuf-net

I noticed in the protobuf-net changelog that IList<> was supported but I'm getting the "Cannot create an instance of an interface" exception. If I change to IEnumerable<> then life is good. Does this sound correct?
// Client call
public override IList<IApplicationWorkflow> Execute(IRoleManagement service)
{
IList<ApplicationWorkflowMessagePart> list = service.RetrieveWorkflows(roleNames);
IList<IApplicationWorkflow> workflows = new List<IApplicationWorkflow>(list.Count);
foreach (ApplicationWorkflowMessagePart a in list)
{
workflows.Add(new ApplicationWorkflowImpl(a));
}
return workflows;
}
// Service contract
[OperationContract, ProtoBehavior]
[ServiceKnownType(typeof (ServiceFault))]
[FaultContract(typeof (ServiceFault))]
IList<ApplicationWorkflowMessagePart> RetrieveWorkflows(string[] roleNames);
// Service implementation
public IList<ApplicationWorkflowMessagePart> RetrieveWorkflows(string[] roleNames)
{
IList<IApplicationWorkflow> workflows = manager.RetrieveApplicationWorkflows(roleNames);
IList<ApplicationWorkflowMessagePart> workflowParts = new List<ApplicationWorkflowMessagePart>();
if (workflows != null)
{
foreach (IApplicationWorkflow workflow in workflows)
{
workflowParts.Add(
ModelMediator.GetMessagePart<ApplicationWorkflowMessagePart, IApplicationWorkflow>(workflow));
}
}
return workflowParts;
}
Thanks,
Mike
Also, is there document site that has this and other answers? I hate to be asking newb questions. :)
Currently it will support IList<T> as a property, as long as it doesn't have to create it - i.e. allowing things like (attributes not shown for brevity):
class Order {
private IList<OrderLine> lines = new List<OrderLine>();
public IList<OrderLine> Lines {get {return lines;}}
}
I would have to check, but for similar reasons, I expect it would work with Merge, but not Deserialize (which is what the WCF hooks use). However, I can't think of a reason it couldn't default to List<T>... it just doesn't at the moment.
The simplest option is probably to stick with List<T> / T[] - but I can have a look if you want... but I'm in a "crunch" on a (work) project at the moment, so I can't lift the bonnet today.
Re "this and other answers"... there is a google group, but that is not just protobuf-net (protobuf-net is simply one of many "protocol buffers" implementations).
You are also free to log an issue on the project site. I do mean to collate the FAQs and add them to the wiki on the site - but time is not always my friend...
But hey! I'm here... ;-p