Json Serializing EF object without relationships - c#

I am trying to store a Json serialized EF object in another database.
One of the items I'm strying to store is the cart, which has some relateded tables.
How can I store the cart, without dragging its relations along (preferably without resorting to .Select() to handpick the distinct columns)
[AllowAnonymous]
public ActionResult Test() {
using (var db = new DALEntities())
{
var q = db.tblCarts.SingleOrDefault(x => x.CartItemID == 4275);
q.tblContactsExtra = null;
q.TBLINVENTORY = null;
var settings = new JsonSerializerSettings {PreserveReferencesHandling = PreserveReferencesHandling .Objects} ;
var str = JsonConvert.SerializeObject(q, settings);
return Content(str);
}

Unfortunately, you'll have to set your foreign keys to allow nulls. Otherwise you won't be able to achieve what you're looking to achieve. To do that, just make the ID fields nullable, something like:
public int? ContactsExtraId { get; set; }
public virtual tblContactsExtra tblContactsExtra { get; set; }
public int? InventoryId { get; set; }
public virtual TblInventory TBLINVENTORY { get; set; }

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)));
}

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.

Querying for object based on property of inside object

I have some objects stored in a LiteDB database. I'm trying to get a result of all CostBasisTradeSessionObjects that include Marked objects with a particular name, MarkedNameString. I find the Marked object easily enough, but I dont now how to query for object in object.
public string Marked
{
public ObjectId MarkedId { get; set; }
public String Name { get; set; }
}
public class CostBasisTradeSessionObject
{
public ObjectId CostBasisTradeSessionId { get; set; }
public bool IsActive { get; set; }
public DateTime SessionStarted { get; set; }
public DateTime SessionClosed { get; set; }
public Marked Marked { get; set; }
}
using (var db = new LiteDatabase(#"CostBasesTradeSessionsDatabase.db"))
{
var costBasisTradeSessionObjects = db.GetCollection("costBasisTradeSessionObjects");
Marked marked = db.GetCollection<Marked>("markeds").Find(Query.EQ("Name", "<MarkedNameString>")).Single();
}
So I try to get an result with CostBasisTradeSessionObject objects that includes the marked object returned in var marked.
So I tried a couple of things
var cb = costBasisTradeSessionObjects.Include(x => x.Marked).Equals(marked);
and justing jusing the MarkedNameString directory
var results = costBasisTradeSessionObjects.(Query.("Marked.name", "MarkedNameString"));
or
var results = costBasisTradeSessionObjects.Find(x => x.Marked.Name.Equals("MarkedNameString"));
but all the things I tried return an empty result or dont work.
Regards
I believe you're looking for the Where() method. You can filter your search by your Name property, and return an IEnumerable of CostBasisTradeSessionObject.
var results = costBasisTradeSessionObjects
.Where(x => x.Marked.Name == "MarkedNameString");

Dynamic properties in LINQ query

I'm trying to convert a complex domain model into a CSV format. However the dynamic structure of the model is pushing my LINQ wizardry to its limit :)
The desired result should look something like the following. A header row, then data rows.
Note that some fields are empty and others have multiple.
Output
PropA;RolenameA;RolenameB;RolenameC
123;username1,username2;username1;username2
321;;;username1
I wish to create a LINQ query which dynamically can add properties for each Role and then fill that property with the correct username(s).
I recognize that the header and the data must be in 2 different statements.
Pseudo code
var result = new { "PropA", Roles.ForEach(x => x.Rolename) }
result += query.Select(x => new { x.PropA, x.UserRoles.ForEach(...) });
Is what I'm trying to do not possible with LINQ and I should do it "manually" with a dynamic object, or is there some black magic I need to learn :)?
Model
public class A
{
public int PropA { get; set; }
public IEnumerable<UserRole> UserRoles { get; set; }
}
public class UserRole
{
public Role Role { get; set; }
public User User { get; set; }
}
public class Role
{
public string Rolename { get; set; }
}
public class User
{
public string Username { get; set; }
}
I ended up doing it "manually" with an ExpandoObject.
foreach (var item in items)
{
var obj = new ExpandoObject() as IDictionary<string, Object>;
obj.Add("PropA", item.PropA);
foreach (var role in roles)
{
obj.Add(role.Name,
String.Join(",", item.UserRole.Where(x => x.Role == role).Select(x => x.User.Name)));
}
// omitted...
}

MVC3 EF4 duplicates foreign key object on save

I'm using MVC3 with EF4 code-first. I have the following model:
public class Order {
public int ID { get; set; }
[Required]
public float Price { get; set; }
[Required]
public int PayMethodId { get; set; }
public PayMethod PayMethod { get; set; }
public int? SpecificEventId { get; set; }
public SpecificEvent SpecificEvent { get; set; }
public int? SeasonalTicketId { get; set; }
public SeasonalTicket SeasonalTicket { get; set; }
}
When I try to save an Order object with specificEventId = 2 and specificEvent = X, a new SpecificEvent object is created in the DB, even though there's already a specific event X with ID 2 in the DB. When i try with specificEventId = 2 and specificEvent = null I get a data validation error.
What am I doing wrong? I want SpecificEvent and SeasonalTicket to be nullable, and I don't want EF4 to create a new instance of these objects in the DB whenever I save 'Order'.
Update
This is my code for saving Order in the DB:
public void SaveOrder(Order order)
{
Order fromDb = null;
// If editing an existing object.
if ((fromDb = GetOrder(order.ID)) != null)
{
db = new TicketsDbContext();
db.Entry(order).State = System.Data.EntityState.Modified;
db.SaveChanges();
}
// If adding a new object.
else
{
db.orders.Add(order);
db.SaveChanges();
}
}
When I save, I do reach the else clause.
The real question is, where did you get the instance of X from? It appears as though EF has no knowledge of this instance. You either need to fetch the already existing SpecificEvent through EF and use the proxy it returns to set your navigation property, or else tell EF to "attach" X, so that it knows what your intent is. As far as EF knows, it appears, you are trying to send it a new instance with a conflicting Id, so it is properly issuing the error.

Categories