Is there an easy way of setting parameters in code when setting up a Deployment via ResourceManagementClient in code, without having to go via JObject?
Thanks for the pointer. I've generalised it a bit so the following converts a dictionary into the structure that the deployment request needs:
public static JObject ConvertProperties(this Dictionary<String, Object> properties)
{
if (properties == null || properties.Count == 0)
{
return null;
}
JObject Output = new JObject();
foreach (KeyValuePair<String, Object> TargetProperty in properties)
{
JObject Child = new JObject();
Child["value"] = JToken.FromObject(TargetProperty.Value);
Output[TargetProperty.Key] = Child;
}
return Output;
}
Related
The subject runtime error's location is inside the following code. Please advise how to fix this issue.
using (var ctx = new MyDbContext())
{
foreach (var entityInfo in ctx.GetEntitiesInfo())
System.Console.WriteLine($"{entityInfo.Index}. EntityName = {entityInfo.Name}, Records count = {entityInfo.RecordCount}");
}
public static class Extensions
{
public readonly record struct EntityInfo (int Index, string? Name, int RecordCount);
public static IEnumerable<EntityInfo> GetEntitiesInfo(this MyDbContext context)
{
var dbSetProperties = context.GetDbSetProperties();
var dbSets = dbSetProperties.Select(x => x.GetValue(context, null)).ToList();
var index = 0;
foreach (IQueryable? dbSet in dbSets)
{
++index;
// Runtime Error happens on next code line on second iteration
// when index = 2:
//
// System.InvalidOperationException:
// 'There is already an open DataReader associated with
// this Connection which must be closed first
//-
dbSet.Load();
var items = new List<object>();
var enumerator = dbSet?.GetEnumerator();
while (enumerator?.MoveNext() == true)
{
var item = enumerator.Current;
items.Add(item);
}
yield return new EntityInfo(index, dbSet?.ElementType?.ToString(), items.Count);
}
}
public static IEnumerable<PropertyInfo> GetDbSetProperties(this MyDbContext context)
{
foreach (var property in context.GetType().GetProperties())
if (property?.PropertyType?.FullName?
.Contains("Microsoft.EntityFrameworkCore.DbSet`") == true)
yield return property;
}
}
[Update]
Actually I wanted to develop a generic code to get all DbSet(s) of a given DbContext. Here it's based on Ivan Stoev's answer to this topic:
public static IEnumerable<IQueryable<object>> GetDbSets(this MyDbContext context)
{
var dbSetProperties = context.GetDbSetTypeProperties();
return dbSetProperties.Select(x => x.GetValue(context, null))
.Cast<IQueryable<object>>();
}
public static IEnumerable<PropertyInfo> GetDbSetTypeProperties(this MyDbContext context)
{
foreach (var property in context.GetType().GetProperties())
if (property?.PropertyType?.FullName?
.Contains("Microsoft.EntityFrameworkCore.DbSet`") == true)
yield return property;
}
Please advise if the above code can be even more simplified/streamlined.
[Update 2]
GetDbSets() method can be simplified this way:
public static IEnumerable<IQueryable<object>> GetDbSets(this MyDbContext context)
{
return context
.GetType()
.GetRuntimeProperties()
.Where(x => x.PropertyType?.FullName?.Contains("Microsoft.EntityFrameworkCore.DbSet`") == true)
.Select(x => x.GetValue(context, null))
.Cast<IQueryable<object>>();
}
but is there any other ways to enumerate all DbSet(s) of a given DbContext?
There are a lot of flaws in that code snippet, but the concrete problem in question is caused by the lack of disposal of the IEnumerator returned by the IEnumerable.GetEnumerator() call.
I know the non generic IEnumerator does not implement IDisposable, but that was a miss, and the generic IEnumerator<T> added later normally implements both IEnumerator and IDisposable.
So you have to either account for that and modify the code similar to this:
var items = new List<object>();
var enumerator = dbSet?.GetEnumerator();
try
{
while (enumerator?.MoveNext() == true)
{
var item = enumerator.Current;
items.Add(item);
}
}
finally
{
(enumerator as IDisposable)?.Dispose();
}
or better let the C# foreach do that for you (writing behind the scenes a code similar to the above):
var items = new List<object>();
foreach (var item in dbSet)
items.Add(item);
Note that DbSet<T> type properties are the equivalent of Set<T>() method and are initialized by EF Core at the DbContext instance creation, so neither they nor their content is null, hence all the ?. operators are not needed.
Also calling the Load method is not needed since all it does is to enumerate the db set without storing items in a list (basically foreach with empty body).
Anyway, both previous code snippets will fix the original issue. But there is even better ways. Since all entities are required to be classes (reference types), each db set can be cast to IQueryable<object>
foreach (IQueryable<object> dbSet in dbSets)
which offers some additional benefits.
First, the original code could be fixed just by adding single using statement:
var items = new List<object>();
using var enumerator = dbSet?.GetEnumerator();
while (enumerator?.MoveNext() == true)
{
var item = enumerator.Current;
items.Add(item);
}
It can be replaced with foreach as before
var items = new List<object>();
foreach (var item in dbSet)
items.Add(item);
but now you have access to many Enumerable (and Queryable and EF Core specific) extension methods, so you can replace all the iteration code with simple
var items = dbSet.ToList();
And in case you just need the record count, retrieve it with server side query without loading all the data in memory
var itemsCount = dbSet.Count();
yield return new EntityInfo(index, dbSet.ElementType.ToString(), itemsCount);
Using a map in the proto file of grpc for .net core to send a dictionary as a request parameter makes it private field(read-only) in the auto-generated code. So I am unable to assign the dictionary to map and pass it in the API request. How do I make it read-write.?
Sample proto request:
service xyz{
rpc testTrans(TestRequest) returns (TestResponse);
}
message TestRequest {
map<string,string> props = 1;
}
so the auto-generated code looks like this :
public const int PropsFieldNumber = 1;
private static readonly pbc::MapField<string, string>.Codec _map_Props_codec
= new pbc::MapField<string, string>.Codec(pb::FieldCodec.ForString(10), pb::FieldCodec.ForString(18), 10);
private readonly pbc::MapField<string, string> Props_ = new pbc::MapField<string, string>();
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public pbc::MapField<string, string> Props {
get { return Props_; }
}
So now when i try to assign property in request as below, it throws error :
Property or Indexer TestRequest.Props could not be assigned to -- it is read only.
public static void testTrans(Dictionary<string, string> test)
{
var res = client.InitTrans(new TestRequest
{
Props = test
});
}
It seems like there is being prevented when you want to directly declare and initialize the value with:
var res = client.InitTrans(new TestRequest
{
//Property could not be assigned to -- it is read only...error
Props = new Map<string,string>.Add("somekey", "somevalue");
// Alternatively the same problem will also occur when you do
// Props = new Map<string,string>.Add(SomeDict);
}
Instead there should be work around by initializing your variable and then add the value(s) to the dictionary later (after the initialization of the entire message object).
var res = new TestRequest{};
//test is some dictionary
res.TestRequest.Props.Add(test);
//alternatively you can also add with (key, value)
res.TestRequest.Props.Add("someKey", "someValue);
I need to dynamically build a object that I can then serialize to a JSON string. Essentially I'm working off of two Dictionaries that I need to use to create the new object.
var myValues= new Dictionary<string, object>
{
{ "Value1", "Foo" },
{ "Value2", "Bar" }
};
var mappedValues = new Dictionary<string, string>
{
{ "Value1", "Some:Path" },
{ "Value2", "Some:OtherPath }
};
As I loop through I need to be able to build out the Json Object so that I end up with something like:
{
"Some": {
"Path": "Foo",
"OtherPath": "Bar"
}
}
From what I've seen there is no way built into Newtonsoft.Json to do this specifically, but I'm hoping that someone may have an idea of how I might be able to most efficiently accomplish the goal.
It doesn't seem that there is any sort of way to create an element built into Newtonsoft.Json at this time... however I eventually came up with the following solution which works
private JObject DoFoo()
{
var jObject = new JObject();
foreach(var mapping in mappedValues)
{
var queue = new Queue<string>(mapping.Value.Split(':');
var value = myValues[mapping.Key];
SetValueWithPath(jObject, queue, value);
}
return jObject;
}
private void SetValueWithPath(JObject parent, Queue<string> path, object content)
{
var currentNode = path?.FirstOrDefault();
if (string.IsNullOrEmpty(currentNode)) return;
if (path.Count == 1)
{
parent[currentNode] = JToken.FromObject(content);
return;
}
else if (!parent.ContainsKey(currentNode))
{
parent[currentNode] = new JObject();
}
path.Dequeue();
SetValueWithPath(parent[currentNode] as JObject, path, content);
}
I'm using a Linq query to retrieve entities from an SQL server using the Entity Framework. When I update an entitiy, the EF is caching the result. I suspect this is because the ObjectContext is in a static variable (below). The only way to refresh the data using my code below is to call a method and set _db to null when there might be stale data displayed (Eg: in a GridView). Is there a way to just prevent it from caching, or to add some sort of end request handler to call this method on my data layer instead of needing to detect when there may be stale data displayed?
private static ServiceEntities _db;
protected static ServiceEntitiesDb
{
get
{
if (_db == null)
{
_db = new ServiceEntities();
_db.Contacts.MergeOption = MergeOption.OverwriteChanges; // failed
}
return _db;
}
}
public static IEnumerable<Contact> GetContactsByName(string name) {
var items = Db.Contacts;
var filteredName = items.Where(i => (i.Name??string.Empty).IndexOf(name) >=0);
return filteredName;
}
The slightly verbose solution (which I wanted to avoid) is to wrap it in a using block. Ie:
public static IEnumerable<Contact> GetContactsByName(string name) {
var items = Db.Contacts;
var filteredName = items.Where(i => (i.Name??string.Empty).IndexOf(name) >=0);
return filteredName;
}
Becomes
public static IEnumerable<Contact> GetContactsByName(string name) {
using (var db = new SomeContext()) {
var items = db.Contacts;
var filteredName = items.Where(i => (i.Name??string.Empty).IndexOf(name) >=0);
return filteredName;
}
}
Consider following code:
My problem is:
1) I can't seem to cast the errors to HttpContent
2) I can't use the CreateContent extension method as this doesn't exist on the context.Response.Content.CreateContent
The example here only seems to provide StringContent and I'd like to be able to pass the content as a JsobObject:
http://www.asp.net/web-api/overview/web-api-routing-and-actions/exception-handling
public class ServiceLayerExceptionFilter : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
if (context.Response == null)
{
var exception = context.Exception as ModelValidationException;
if ( exception != null )
{
var modelState = new ModelStateDictionary();
modelState.AddModelError(exception.Key, exception.Description);
var errors = modelState.SelectMany(x => x.Value.Errors).Select(x => x.ErrorMessage);
// Cannot cast errors to HttpContent??
// var resp = new HttpResponseMessage(HttpStatusCode.BadRequest) {Content = errors};
// throw new HttpResponseException(resp);
// Cannot create response from extension method??
//context.Response.Content.CreateContent
}
else
{
context.Response = new HttpResponseMessage(context.Exception.ConvertToHttpStatus());
}
}
base.OnException(context);
}
}
context.Response = new HttpResponseMessage(context.Exception.ConvertToHttpStatus());
context.Response.Content = new StringContent("Hello World");
you also have the possibility to use the CreateResponse (added in RC to replace the generic HttpResponseMessage<T> class that no longer exists) method if you want to pass complex objects:
context.Response = context.Request.CreateResponse(
context.Exception.ConvertToHttpStatus(),
new MyViewModel { Foo = "bar" }
);