ServiceStack Ormlite usage in ServiceInterface - c#

I have created a solution using the empty asp.net template.
I have addred Ormlite and MySql Servicestatck libraries and configured them in the Apphost.cs
ConnectionStringSettings connstring = ConfigUtils.GetConnectionStringSetting("Nice2NoConnection");
container.Register<IDbConnectionFactory>(new OrmLiteConnectionFactory(connstring.ToString(), MySqlDialectProvider.Instance));
// Below we refer to the connection factory that we just registered
// with the container and use it to create our table(s).
using (var db = container.Resolve<IDbConnectionFactory>().Open())
{
// We’re just creating a single table, but you could add
// as many as you need. Also note the “overwrite: false” parameter,
// this will only create the table if it doesn’t already exist.
db.CreateTable<Channel>(overwrite: false);
What I am having trouble figuring out is how to access the OrmliteWriteExtensions which include Save() in the ServiceInterface project. I tried adding Ormlite and MySql there but don't know how to access the reference of the connectionfactory in this project.
I think my issue relates to a lack of deeper understanding of IoC
I am probably over complicating something here.
Suggestions?
TIA

Thank you for the feedback. I wish to thank you for such a fantastic library. My issues turns out was my lack of understanding of IoC and I had to do code like: var conn = HostContext.Container.Resolve<IDbConnection>();
conn.Open();
conn.Save<Channel>(channel);

The OrmLite extension methods are under the ServiceStack.OrmLite namespace so to access them you just need to import:
using ServiceStack.OrmLite;

Related

C# AWS Parameter Store - Configuration not loading SystemManagerConfiguration in .Net 6

Application is not able to talk to AWS Parameter Store in .NET 6.
It is always talking to appsettings.json.
I tried debugging locally, still same behavior. Not able to find the SystemManagerConfiguration under list of configuration .
var builder = WebApplication.CreateBuilder();
var connectionString = builder.Configuration.GetConnectionString("OrderTrackerDatabase");
Packages Used
Library Source Code : https://github.com/aws/aws-dotnet-extensions-configuration
image
I got the same issue and finally resolved it.
The samples code in https://github.com/aws/aws-dotnet-extensions-configuration missed one line as below after called "AddSystemsManager" method in .Net 6.
builder.Services.Configure<Settings>(builder.Configuration.GetSection($"common:settings"));
After added above line, then I'm able to get the correct values from AWS Parameter Store when using the settings.
I've also created an issue of this in GitHub as below -
https://github.com/aws/aws-dotnet-extensions-configuration/issues/114
I believe the problem might be the trailing slash after "/OrderTracking/", try "/OrderTracking" instead.
WebApplication.CreateBuilder() will create new instance and doesn't carry over the SystemManager configuration.
Instead, use IConfiguration instance through constructor DI.
var connectionString = _configuration.GetConnectionString("OrderTrackerDatabase");
In my case this extensions method was returning null at my lambda:
private static IConfiguration InitializeConfiguration() => new ConfigurationBuilder()
.AddSystemsManager($"/my-data", true, TimeSpan.FromMinutes(5))
.Build();
Because the role of lambda didn't have permission for read SSM for that resource.
User: is not authorized to perform: ssm:GetParametersByPath on resource
So, just add necessary permission (ssm:GetParametersByPath) for the role of lambda to your resource at System Manager.
In my case, I am using lambda serverless, so the IConfig is always null when it is passed to the controller.
I resolved it by changing the IOptions<Settings> settings in the Controller constructor to IConfiguration settings and then access the parameters by name like _settings.GetValue<string>("ParameterName")
A little less "Object Oriented", but it seemed much easier than this complex solution

is it possible to create base EF DbContext and use it in many services?

I try to implement Microservice Architecture in my project. I was wondering can i create 1 method for Depedency Injection there is to implement EntityFrameworkCore in many services so i didnt DRY.
So what iam thinking is i still create my own DbContext, and than when i want to register this i just put it in my json file and add something like services.UseSql() in my startup.cs and inside UseSql is using Configuration from .json file.
what i try to say is something like generic DbContext.
I have some example to this but is using MongoDb :
Example
public static void AddMongo(this ContainerBuilder builder)
{
builder.Register(context =>
{
var configuration = context.Resolve<IConfiguration>();
var options = configuration.GetOptions<MongoDbOptions>("mongo");
return options;
}).SingleInstance();
builder.Register(context =>
{
var options = context.Resolve<MongoDbOptions>();
return new MongoClient(options.ConnectionString);
}).SingleInstance();
builder.Register(context =>
{
var options = context.Resolve<MongoDbOptions>();
var client = context.Resolve<MongoClient>();
return client.GetDatabase(options.Database);
}).InstancePerLifetimeScope();
builder.RegisterType<MongoDbInitializer>()
.As<IMongoDbInitializer>()
.InstancePerLifetimeScope();
builder.RegisterType<MongoDbSeeder>()
.As<IMongoDbSeeder>()
.InstancePerLifetimeScope();
}
If I understand your problem statement correctly, you want multiple microservices to connect to the same database. As comments above suggest, you really shouldn't do that as you'd essentially be coupling your microsrvices through your data layer - which can be messy if your microservices grow into incompatible data layers. This would eliminate a lot of benefits of the architecture and leave you to deal with pains. So if that is indeed the case you might want to consider revisiting your arсhitecture and trying to figure out whether you need a new microservice that fronts this common data store and provides it to others.
In reality however you sometimes must share code between projects and there's a official way to do that: extract the common code into a nuget package, host it somewhere in your private enterprise repository (if you care) and reference it from your other projects.

Mysterious authentication error connecting from Dapper but works from EF 6 - same ConnectionString

We have services and repositories loaded by IoC.
/* registration in the service class for repository */
container.Register<IContainerRepository, ContainerRepository>();
/* registration in GUI app from service class and Form class for constructor injection usage */
container.Register<IContainerService, ContainerService>();
container.Register<rfrmContainerList, rfrmContainerList>();
So we have the ContainerRepository for data access and ContainerService for business logic.
ContainerRepository uses EF6 and Dapper (basically EF6 for simple queries and insert/update/delete methods while we use Dapper for running optimized queries against complex joined data). Every repository has the EF6 context passed by IoC in the class constructor. Dapper always get the ConnectionString from the context:
var testItem = Context.Container.FirstOrDefault();
using (SqlConnection connection = new SqlConnection(Context.Database.Connection.ConnectionString))
{
var builder = new SqlBuilder();
...
var items = connection.Query<ContainerDto>(sql.RawSql, sql.Parameters);
}
The ContainerService injected by constructor injection:
private readonly IContainerService _containerService;
public rfrmContainerList(IContainerService containerService)
{
_containerService = containerService ?? throw new ArgumentNullException(nameof(containerService));
}
Now everything is set up, so I expect that if Dapper using the same ConnectionString as EF6 it will use the same authentication, right?
Nope. While var testItem = Context.Container.FirstOrDefault(); successfully returns the expected result Dapper fails at connection.Query<> throwing an exception says Login failed for user 'username' (username replaced to actual login name for my test user).
So after many tests and try I finally localized the main problem, it's something in the IoC.
/* This query will return successfully */
var localContainerService = IoC.Resolve<IContainerService>();
var container = localContainerService.ListContainers(new Model.QueryParams.DesktopApp.Operation.ContainerListQueryParams() { StatusDateFrom = new DateTime(2019, 8, 1), StatusDateTo = DateTime.Now.Date });
/* This query will fail with authentication */
var container2 = _containerService.ListContainers(new Model.QueryParams.DesktopApp.Operation.ContainerListQueryParams() { StatusDateFrom = new DateTime(2019, 8, 1), StatusDateTo = DateTime.Now.Date });
So the first query with locally resolved service object runs without any problem, takes the ConnectionString from the Context and connection.Query<ContainerDto>(sql.RawSql, sql.Parameters) returns an expected result.
The second query using the constructor injected service fails at the same line says "Login failed for user 'username'".
Strange but EF6 query var testItem = Context.Container.FirstOrDefault(); works for both cases.
Can anyone experience a similar problem? I've checked all my IoC registrations and can't figure out why this strange behavior happens.
There are multiple scenarios where that won't work. The good news is that you shouldn't be using two seperate SqlConnections in the first place. Just use the DbContext's SqlConnection for Dapper/ADO.NET. The DbContext will clean up the SqlConnection and you don't even need an additional using block.
So just something like:
class MyDb : DbContext
{
public SqlConnection GetConnection()
{
var con = (SqlConnection)Database.Connection;
if (con.State != System.Data.ConnectionState.Open)
{
con.Open();
}
return con;
}
// . . .
}
If you are using SQL Authentication (UId / Pwd) then EF does not reveal the password when you use context.Database.Connection.ConnectionString. For instance, output that to the Console and you get something like:
"Data Source=QH10373138\DEV2008R2;Initial Catalog=Spikes;uid=sqladmin;MultipleActiveResultSets=True"
where my initial connection string included a ";pwd=HeyYouShouldntSeeThi$"
It should work if you use Windows Authentication.
Though +1 to David's answer, use the EF's DbConnection if you need to do something tricky.
I'd also look at whether the issues can be solved with better EF querying such as leveraging projection with Select to get relevant details vs. EagerLoading, or looking for pitfalls with Lazy Loading. Personally, after working on a large number of projects with NHibernate when EF(4) was pretty useless for anything but the most trivial scenarios, to now using EF 6 pretty much exclusively, I haven't found a scenario where I've needed something like Dapper to address a scenario that at worst introduced an optimized entity model via a bounded context. And that is rare, when it is used, it's something like for supporting bulk operations. The risk of work-arounds is them becoming a crutch to hide design issues and add to the complexity of maintenance. The default approach becomes if EF seems slow, write a Dapper alternative instead of resolving what can be simple inefficiencies in how the EF code was written.
Thanks for these answers, I'm adding another one: ConnectionString must include Persist Security Info=true and it works again.
However I'm preferring the solution by #David.

Bound function call returns InvalidOperationException

I'm following this OData V4 tutorial and now have a problem with the bound function MostExpensive.
This function is bound to the Productscollection and is registered in WebApiConfig.Register() like suggested in the tutorial:
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.Namespace = "ProductService";
builder.EntityType<Product>().Collection.Function("MostExpensive").Returns<decimal>();
There is only described a rudimentary client for this service in part 2 of this tutorial. So I want to extend it so that I also can call the functions described in the later parts of the tutorial.
I have created the client using OData Client Code Generator as suggested in the tutorial. I then add some products and suppliers and then want to get the most expensive product:
static void Main(string[] args)
{
const string serviceUri = "http://localhost:52542";
Container container = new Container(new Uri(serviceUri));
AddProducts(container, GenerateSomeProducts());
AddSuppliers(container, GenerateSomeSuppliers());
Console.WriteLine("Most expensive product is: {0}", container.Products.MostExpensive().GetValue());
...
}
When calling GetValue() I am getting an InvalidOperationException stating that http://localhost:52542/$metadata refers to a Edm.Decimal type but a Collection(Edm-Decimal) type is expected.
When calling http://localhost:52542/Products/ProductService.MostExpensive() directly in the browser I'm getting
{
"#odata.context":"http://localhost:52542/$metadata#Edm.Decimal","value":40000.95
}
Which seems to be correct.
Do I do anything wrong? I have no idea how to fix this. So any suggestions about that?
I guess you are using T4 2.2.0, right?
There is a bug in T4 2.2.0 which causes this issue. You can use the content in following link to replace your ttinclude file and regenerate your proxy to work around the issue.
https://raw.githubusercontent.com/LaylaLiu/odata.net/T4TempFix/src/CodeGen/ODataT4CodeGenerator.ttinclude

a proxy type with the name account has been defined by another assembly

We have 2 orgs running in our on-premise crm 2011 system.
We have generated early bound classes for both orgs.
One of our plugins is throwing the "a proxy type with the name account has been defined by another assembly" error when deactivating an account.
That plugin only references one of the early bound dll's.
How do I get the CRM system to respect the namespace of these references.
I've tried the few items that show up from Google and none are working.
Since you can reproduce this with 2 vanilla orgs I would imaging there is something OUTSIDE the code layer we can do without having to go back and refactor a bunch of code for the 2 orgs.
Thanks,
Jon
The problem is actually with WCF attempting to deserialize the server response and not being able to identify the correct type. The best method to sort this issue is to pass in the current assembly using Assembly.GetExecutingAssembly() to the ProxyTypesBehavior() while creating the proxy like so.
using (serviceProxy = new OrganizationServiceProxy(config.OrganizationUri,
config.HomeRealmUri,
config.Credentials,
config.DeviceCredentials))
{
// This statement is required to enable early-bound type support.
serviceProxy.ServiceConfiguration.CurrentServiceEndpoint.Behaviors.Add(new ProxyTypesBehavior(Assembly.GetExecutingAssembly()));
}
You may run into this issue when referencing different assemblies containing proxy-classes, i.e. one assembly wrapping the server SDK (Microsoft.Xrm.Sdk) and another assembly wrapping the client SDK (Microsoft.Xrm.Sdk.Client).
In such a scenario it seems to be required to tell the OrganizationServiceProxy which assembly should be used to resolve the proxy classes.
This should help:
var credentials = new ClientCredentials();
credentials.Windows.ClientCredential = new System.Net.NetworkCredential(userName, password, domain);
var proxy = new OrganizationServiceProxy(new Uri(discoveryUrl), null, credentials, null);
proxy.EnableProxyTypes(typeof(CrmServiceContext).Assembly);
var context = CrmServiceContext(proxy);
The important thing is to call EnableProxyTypes by passing the correct assembly. I saw another solution using CrmConnection but CrmConnection is only available in the client SDK, which means that you can't instantiate a "server-OrganizationServiceProxy" this way. EnableProxyTypes(Assembly assembly) works for both sides.
Hope this helps.
Regards,
MH
It maybe years since this question has been raised. However, I faced this problem recently and have been extremely worried about thousands of lines of code to be changed. However, I was lucky to find the following simple change to get myself out of hell:
Suppose there are two context objects you deal with:
an OrganizationServiceContext object: context1
a CrmSvcUtil Context object: context2
and a single OrganizationServiceProxy object: service
if in a single method, you make multiple CRUD operations using the same service object but with either of context objects as exemplified above, it is highly probable that this error be raised. However, by doing the following, you can prevent it to happen.
Every time you want to work with context1, you precede the context object with the service object as following:
service.EnableProxyTypes(typeof(OrganizationServiceContext).Assembly);
using (var context1 = new OrganizationServiceContext(_service)){
// your classic code here
}
Also, every time you want to work with context2, you follow the same structure:
service.EnableProxyTypes(typeof(HiwebContext).Assembly);
using (var context = new XYZContext(this._service)){
// your CrmSvcUtil none-classic code here
}
this normally means that there is one or more assemblies with the same method name or property to fix this use the fully qualified name of the assembly.. for example in the using System.IO for example if you had a method named the same way in your Class code that conflicts with System.IO.... you would write your fix like
thisObject.System.IO.Path( ---- ) = somthing for example.. does this make sense..?
I found that adding the Assembly.GetExecutingAssembly() solved the problem.
adding the Assembly.GetExecutingAssembly() solve my problem, you also need to add using System.Reflection;
thanks

Categories