Creating a List of Database Objects - c#

I want to create a list based off the query that gets passed into my Method. My issue is determining how to add those items to a list that I return as a result list. The following is the code that includes my list and what will hopefully be the way I populate that list...
public void QueryInto<T>(ref List<T> returnType, string queryString, string[] parameters = null)
{
try
{
// Setup the query.
SetupCommand(queryString);
// Setup the parameters.
AddParameters(parameters);
// Execute the reader.
OpenReader();
// Make sure the statement returns rows...
if (reader.HasRows)
{
// For each row, do this...
while (reader.Read())
{
// TODO: use datamapping to map to model and add items to list...
}
}
}
Perhaps there is a better way of doing this, but so far this is the direction Google Has directed me!

you can use Dapper for this.
example useage;
public class Dog
{
public int? Age { get; set; }
public Guid Id { get; set; }
public string Name { get; set; }
public float? Weight { get; set; }
}
var guid = Guid.NewGuid();
var dog = connection.Query<Dog>("select Age = #Age, Id = #Id", new { Age = (int?)null, Id = guid });

Related

Mongodb collection as dynamic

I have an application that has two similar but different objects and I want to store those objects in the same collection. What is the best way to do this? And how can I query this collection?
Today my collections is represented by:
public IMongoCollection<Post> Posts
{
get
{
return _database.GetCollection<Post>("posts");
}
}
And I have this class:
public class Post
{
public string Id { get; set; }
public string Message { get; set; }
}
public class NewTypePost
{
public string Id { get; set; }
public string Image { get; set; }
}
So, today I just can save and query using Post class. Now I want to store and retrive the both classes, Post and NewTypePost.
I tried to change the class type from Post to dynamic. But when I did this, I could not query the collections.
MongoDB .NET driver offers few possibilites in such cases:
Polymorphism
You can build a hierarchy of classes and MongoDB driver will be able to determine a type of an object it gets retrieved from the database:
[BsonKnownTypes(typeof(Post), typeof(NewTypePost))]
public abstract class PostBase
{
[BsonId]
public string Id { get; set; }
}
public class Post: PostBase
{
public string Message { get; set; }
}
public class NewTypePost: PostBase
{
public string Image { get; set; }
}
MongoDB driver will create additional field _t in every document which will represent corresponding class.
Single Class
You can still have Post class and use BsonIgnoreIfNull attribute to avoid serialization exception. MongoDB .NET driver will set those properties to null if they don't exist in your database.
public class Post
{
[BsonId]
public string Id { get; set; }
[BsonIgnoreIfNull]
public string Message { get; set; }
[BsonIgnoreIfNull]
public string Image { get; set; }
}
BsonDocument
You can also drop strongly-typed approach and use BsonDocument class which is dynamic dictionary-like structure that represents your Mongo documents
var collection = db.GetCollection<BsonDocument>("posts");
More details here
dynamic
Specifying dynamic as generic parameter of ICollection you should get a list of ExpandoObject that will hold all the values you have in your database.
var collection = db.GetCollection<dynamic>("posts");
var data = collection.Find(Builders<dynamic>.Filter.Empty).ToList();
var firstMessage = data[0].Message; // dynamically typed code
Suppose I have the next conn to a test database:
var mongoClient = new MongoClient(new MongoClientSettings
{
Server = new MongoServerAddress("localhost"),
});
var database = mongoClient.GetDatabase("TestDb");
Then I can do something like:
var col = database.GetCollection<Post>("posts");
var col2 = database.GetCollection<NewTypePost>("posts");
To get two different instances of IMongoCollection but pointing to the same collection in the database. Further I am able to save to each collection in the usual way:
col.InsertOne(new Post { Message = "m1" });
col2.InsertOne(new NewTypePost { Image = "im1" });
Then, I'm also able to query from those collection base on the specific fields:
var p1= col.Find(Builders<Post>.Filter.Eq(x=>x.Message, "m1")).FirstOrDefault();
var p2 =col2.Find(Builders<NewTypePost>.Filter.Eq(x=>x.Image, "im1")).FirstOrDefault();
Console.WriteLine(p1?.Message); // m1
Console.WriteLine(p2?.Image); // im1
I don't know if that's what you want but it uses the same collection. BTW, change the Id properties to be decorated with [BsonId, BsonRepresentation(BsonType.ObjectId)]. Hope it helps.
Use the BsonDocument data type. It can do all of that. BsonDocument and dynamic back and forth is very convenient.
public class CustomObject{
public long Id{get;set;}
public string Name{get;set;}
public List<(string,object)> CollectionDynamic{get;set;}
}
// inserted in mongo
//public class CustomObject_in_Db{
// public long Id {get;set;}
// public string Name {get;set;}
// public string field2 {get;set;}
// public string field3 {get;set;}
// public string field4 {get;set;}
// public string field5 {get;set;}
// }
// something code... mapper(config)
Automapper.Mapper.CreateMap<BsonDocument,CustomObject>()
.ForMember(dest=>dest.Id, a=>a.MapFrom(s=>s.Id.GetValue(nameof(CustomObject.Id)).AsInt64)
.ForMember(dest=>dest.Name, a=>a.MapFrom(s=>s.Id.GetValue(nameof(CustomObject.Name)).AsString)
.ForMember(dest=>dest.CollectionDynamic, a=>a.MapFrom(s=>_getList(s));
// .......
private List<(string, object)> _getList(BsonDocument source){
return source.Elements.Where(e=>!typeof(CustomObject).GetProperties().Select(s=>s.Name).Any(a=>a ==e.Name)).Select(e=>e.Name, BsonTryMapper.MapToDotNetValue(e.Value)));
}

Convert a Deedle Dataframe into a C# List of Custom Class

I receive data from an external API as a Deedle dataframe. I need to take this data and convert it to a List of a custom class to be inserted into a database via Entity Framework.
Would anyone be able to point me in the right direction? I've not used Deedle before and am having trouble finding the best way to extract data.
The custom object I need to populate looks like the below:
public class FrameData
{
public string SecurityId { get; set; }
public string FieldName { get; set; }
public DateTime Date { set; get; }
public decimal value { get; set; }
}
Thanks,
Nick
There are many ways to get data from a Deedle frame. Does Entity Framework allow you to use interfaces? If so, then there is a nice function GetRowsAs which lets you do this:
// Given a simple Person interface
public interface Person {
int Age { get; }
string Name{ get; }
}
// And a sample data frame with some data
Frame<int, string> df = Frame.FromValues(new[] {
Tuple.Create(1, "Name", (object) "One"),
Tuple.Create(2, "Name", (object) "Two"),
Tuple.Create(1, "Age", (object) 42),
Tuple.Create(2, "Age", (object) 21)
});
// You can get an array of rows using
var rows = df.GetRowsAs<Person>();
If Entity Framework cannot handle interfaces, then this method sadly won't work. In that case, you'll need something like:
var rows =
df.Rows.Select(row =>
new Person { Name = row.Value.GetAs<string>("Name"),
Age = row.Value.GetAs<int>("Age"))).Observations;
What #Tomas Petricek said is a good example, but having to many diffrent classes, this may become a problem.
So here i made an example how to dynamicly convert it to a specific class, and use attribute to mapp between the property and the index of column.
// Mapp a key to an index
public class IndexAttribute: Attribute(){
public int Index { get; private set; }
public IndexAttribute(int index){
Index = index;
}
}
public class FrameData
{
[IndexAttribute(0)]
public string SecurityId { get; set; }
[IndexAttribute(3)]
public string FieldName { get; set; }
[IndexAttribute(2)]
public DateTime Date { set; get; }
[IndexAttribute(1)]
public decimal value { get; set; }
}
// create a class the convert the frame to class
public static List<T> Convert<T>(this DataTable data) where T: class
{
var list = new List<T>();
foreach(var row in data.Rows){
object item = Activator.CreateInstance(typeof(T));
var props = typeof(T).GetProperties();
foreach(var p in props){
var index = p.GetCustomAttribute<IndexAttribute>()?.Index ?? -1;
if (index< 0)
continue;
if (table.Columns.length > index) // if the index exist
{
var value = row[index]; // get the value.
if (value == null)
continue; // ignore null value
if (prop.PropertyType == typeof(string))
prop.SetValue(item, value.ToString());
if (prop.PropertyType == typeof(DateTime))
prop.SetValue(item, DateTime.Parse(value.ToString()));
// .. now check for decimal, int etc
}
}
list.Add(item);
}
return list;
}
And now all you need is to call the function
List<Person> persons = df.Convert<Person>(); // that all

Dapper - passing child objects as query parameter

Is there a way to pass child or multiple objects to Dapper as query parameter, when they have properties with the same name?
For example, if I have these classes:
class Person
{
public int Id { get; set; }
public string Name { get; set; }
public City City { get; set; }
}
class City
{
public int Id { get; set; }
public string Name { get; set; }
}
and I want to execute this query:
connect.QueryFirst<Result>("select :Id Id, :Name Name, :City_Id City_Id, :City_Name City_Name from dual", personParams);
The only way I managed to do it without reflection was passing the first object and then adding the other properties one by one:
var personParams = new DynamicParameters(person);
personParams.AddDynamicParams(new { City_Id = person.City.Id, City_name = person.City.Name });
But on the database there are hundreds of tables, some of them with more than a hundred columns, and I need to split them into multiple classes. So passing the parameters one by one would be improductive.
I tried adding the parameters to a temporary bag and then adding a prefix to each one, something like this:
static DynamicParameters GetParameters(object mainEntity, object otherEntities)
{
var allParams = new DynamicParameters(mainEntity);
foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(otherEntities))
{
object obj = descriptor.GetValue(otherEntities);
var parameters = new DynamicParameters(obj);
foreach (var paramName in parameters.ParameterNames)
{
var value = parameters.Get<object>(paramName);
allParams.Add($"{descriptor.Name}{paramName}", value);
}
}
return allParams;
}
But it doesn't work because Dapper only populates the parameters when the command is executing, not when the DynamicParamters is created.
I wanted to avoid reflection because Dapper is very good at it, and my code would probably perform much worse. Is there a way to do it or is reflection my only option?

AutoMapper: mapping many properties into one

The scenario is the following: I receive a message containing a lot of variables, several hundreds. I need to write this to Azure Table storage where the partition key is the name of the individual variables and the value gets mapped to e.g. Value.
Let’s say the payload looks like the following:
public class Payload
{
public long DeviceId { get; set; }
public string Name { get; set; }
public double Foo { get; set; }
public double Rpm { get; set; }
public double Temp { get; set; }
public string Status { get; set; }
public DateTime Timestamp { get; set; }
}
And my TableEntry like this:
public class Table : TableEntity
{
public Table(string partitionKey, string rowKey)
{
this.PartitionKey = partitionKey;
this.RowKey = rowKey;
}
public Table() {}
public long DeviceId { get; set; }
public string Name { get; set; }
public double Value { get; set; }
public string Signal { get; set; }
public string Status { get; set; }
}
In order to write that to Table storage, I need to
var table = new Table(primaryKey, payload.Timestamp.ToString(TimestampFormat))
{
DeviceId = payload.DeviceId,
Name = payload.Name,
Status = payload.Status,
Value = value (payload.Foo or payload.Rpm or payload.Temp),
Signal = primarykey/Name of variable ("foo" or "rmp" or "temp"),
Timestamp = payload.Timestamp
};
var insertOperation = TableOperation.Insert(table);
await this.cloudTable.ExecuteAsync(insertOperation);
I don’t want to copy this 900 times (or how many variables there happen to be in the payload message; this is a fixed number).
I could make a method to create the table, but I will still have to call this 900 times.
I thought maybe AutoMapper could help out.
Are they always the same variables? A different approach could be to use DynamicTableEntity in which you basically have a TableEntity where you can fill out all additional fields after the RowKey/PartitionKey Duo:
var tableEntity = new DynamicTableEntity();
tableEntity.PartitionKey = "partitionkey";
tableEntity.RowKey = "rowkey";
dynamic json = JsonConvert.DeserializeObject("{bunch:'of',stuff:'here'}");
foreach(var item in json)
{
tableEntity.Properties.Add(item.displayName, item.value);
}
// Save etc
The problem is to map these properties, it is right?
Value = value (payload.Foo or payload.Rpm or payload.Temp),
Signal = primarykey/Name of variable ("foo" or "rmp" or "temp"),
This conditional mapping can be done via Reflection:
object payload = new A { Id = 1 };
object value = TryGetPropertyValue(payload, "Id", "Name"); //returns 1
payload = new B { Name = "foo" };
value = TryGetPropertyValue(payload, "Id", "Name"); //returns "foo"
.
public object TryGetPropertyValue(object obj, params string[] propertyNames)
{
foreach (var name in propertyNames)
{
PropertyInfo propertyInfo = obj.GetType().GetProperty(name);
if (propertyInfo != null) return propertyInfo.GetValue(obj);
}
throw new ArgumentException();
}
You may map rest of properties (which have equal names in source and destination) with AutoMapper.Mapper.DynamicMap call instead of AutoMapper.Mapper.Map to avoid creation of hundreds configuration maps. Or just cast your payload to dynamic and map it manually.
You can create a DynamicTableEntity from your Payload objects with 1-2 lines of code using TableEntity.Flatten method in the SDK or use the ObjectFlattenerRecomposer Nuget package if you are also worried about ICollection type properties. Assign it PK/RK and write the flattened Payload object into the table as a DynamicTableEntity. When you read it back, read it as DynamicTableEntity and you can use TableEntity.ConvertBack method to recreate the original object. Dont even need that intermediate Table class.

Add data from list to list in another class conditionally

I have a class called WebsiteForm which contains a property List<Survey>.
I also have a stored procedure that returns a record set of WebsiteForm data (minus surveys) and a record set of Survey data. These are temporarily stored in lists called formList and surveyList.
I want to be able to put the relevant surveys from surveyList into the survey list in each formList object, if that makes sense. The relevant ones are where the ID property of WebsiteForm matches the LinkID property of Survey.
I imagine this could be done with LINQ but I'm struggling due to the structure of the data.
Thanks.
public class WebsiteForm
{
public int ID { get; set; }
...
public List<Survey> Surveys { get; set; }
}
public class Survey
{
public int LinkID { get; set; }
...
}
public class CRMService
{
...
public List<WebsiteForm> GetData(string storedProcedure)
{
List<WebsiteForm> formList;
List<Survey> surveyList;
using (IDbConnection sqlConnection = new SqlConnection(ConnectionString))
{
sqlConnection.Open();
try
{
using (var data = sqlConnection.QueryMultiple(storedProcedure, commandType: CommandType.StoredProcedure))
{
formList = data.Read<WebsiteForm>().ToList();
surveyList = data.Read<Survey>().ToList();
// Add surveys from surveyList to each formList item where the LinkID of surveyList matches the ID of formList
}
}
finally
{
sqlConnection.Close();
}
}
return formList;
}
}
I'm using Dapper to map the SQL data to the classes btw.
you can try somthing like this
....
formList = data.Read<WebsiteForm>().ToList();
surveyList = data.Read<Survey>().ToList();
formList.ForEach(fl=>fl.Surveys = surveyList.Where(sl=>sl.LinkID == fl.ID).ToList());
....

Categories