We're using MongoDb as a datasource for our application, which is built using cqrs and event sourcing. The problem that we faced today is what is the best way to implement mapping (denormalization) of events to read database. For example, we have a user MongoDb collection which contains all info about user.We have event like this:
[Serializable]
public class PasswordChangedEvent : DomainEvent
{
private string _hashedPassword;
private string _salt;
public PasswordChangedEvent()
{
}
public PasswordChangedEvent(string hashedPassword, string salt, DateTime createdDate)
:base(createdDate)
{
HashedPassword = hashedPassword;
Salt = salt;
}
public string HashedPassword
{
private set { _hashedPassword = value; }
get { return _hashedPassword; }
}
public string Salt
{
private set { _salt = value; }
get { return _salt; }
}
}
And read DTO like
public class User : BaseReportDataObject
{
public string Name { get; set; }
public string Email { get; set; }
public string Gender { get; set; }
public DateTime? BirthDate { get; set; }
public string HashedPassword { get; set; }
public string Salt { get; set; }
public string RestoreHash { get; set; }
public string OpenIdIdentifyer { get; set; }
}
Our current solution for updating documents with events goes like this: we have some mapping code for our events (BsonClassMap.RegisterClassMap etc.) and code for update:
MongoCollection.Update(Query<PasswordChangedEvent>.EQ(ev => ev.AggregateId, evnt.AggregateId),
Update<PasswordChangedEvent>
.Set(ev => ev.HashedPassword, evnt.HashedPassword)
.Set(ev => ev.Salt, evnt.Salt));
The code looks little ugly and redundant to me: with all that lambda stuff we still need to provide property values explicitly. Another way is to replace PasswordChangedEvent with User dto, so we do not need event mapping anymore:
MongoCollection.Update(Query<ReadDto.User>.EQ(u => u.Id, evnt.AggregateId),
Update<ReadDto.User>.Set(u => u.HashedPassword, evnt.HashedPassword));
So the question again: is there any better way to do such a type of mapping? Two types of objects (Events and DTO) mapped to the same mongo db collection.
It seems like this is actually a question about mapping data from one object to another?
If so, you may want to consider using something like Ditto or AutoMapper. I am the developer of ditto and have used it for a number of CQRS systems effectively...I wrote it to handle mixing in alot of different objects' data into the same View Model.
These are known as OO mappers and typically have some form of bootstrapping configuration code, often using sensible conventions to avoid all the redundancy.
Related
B"H
Is there a way to return field names when using Tuples as return types for actions?
What I would like to do is skip creating DTOs for every single function in every single controller. When I have a complex system with many controllers each with many actions (functions). I often find that there are a handful of central DTOs. Then there are hundreds of slight variations of them. One for each function. I would like to stick with the handful of central classes and skip the extra classes. Replacing them with Tuples.
For example. I have a Customer class
public class Customer
{
public string Name{ get; set; }
public string Email{ get; set; }
}
with a many to many relation to store locations
public class StoreLocation
{
public string Name{ get; set; }
public string City{ get; set; }
public string State{ get; set; }
public string Focus{ get; set; }
}
I then have a function in a controller
[HttpGet("TopCustomersByState")]
public IEnumerable<(Customer customer, string state )> TopCustomersByState()
{
}
and a function
[HttpGet("TopCustomersByFocus")]
public IEnumerable<(Customer customer, string focus)> TopCustomersByState()
{
}
and a function
[HttpGet("CustomersAndTotalMoneySpent")]
public IEnumerable<(Customer customer, float moneySpent)> CustomersAndTotalMoneySpent()
{
}
These function are all accessed from Javascript in the browser expecting JSON.
Until now, I'd make a separate class for each return type. This quickly gets out of hand.The solution that I present above in my examples if it were to work would be perfect.The issue is that the JSON being returned is
{
"item1": {
},
"item2": null
}
instead of the property names customer, moneySpent, etc. as you'd expect.
Thank you
You don't need to put any return type of data at all if you want to make it generic. In any case it is converted to a json string. You can use an anonymous class for example
[HttpGet("CustomersAndTotalMoneySpent")]
public IActionResult CustomersAndTotalMoneySpent()
{
... your code
return Ok (new { Focus = focus, Customer = customer } );
//or
return Ok (new List<object> { { Focus = focus, Customer = customer} });
//or List<dynamic>
}
Goal: to save ViewModel object by Entity Framework. I have UserViewModel object which has list of UnitViewModel. Then, I have a UserAdapter class which converts UserViewModel into Entity Framework User object (see Convert()below how).
Now, my question is how do I convert this list of UnitViewModel to its corresponding Entity Framework Unit list? - Do I have to get each object from DB Context by calling something like context.Units.Where(u=>myListofUnitIDs.Contains(u.UnitID))?
public class UserViewModel
{
public Guid? UserID { get; set; }
public string UserName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Password { get; set; }
public DateTime? CreateTime { get; set; }
public List<UnitViewModel> UserUnits { get; set; }
}
public class UnitViewModel
{
public Guid UnitID { get; set; }
public string Name { get; set; }
public int? SortIndex { get; set; }
public DateTime CreateTime { get; set; }
public bool Assigned { get; set; }
}
public class UserAdapter
{
public static User Convert(UserViewModel userView)
{
User user;
if (userView.UserID.HasValue)
{
using (var provider = new CoinsDB.UsersProvider())
{
user = provider.GetUser(userView.UserID.Value);
}
}
else
{
user = new User();
}
user.FirstName = userView.FirstName;
user.LastName = user.LastName;
user.Password = StringHelper.GetSHA1(userView.Password);
user.UserName = user.UserName;
user.CreateTime = DateTime.Now;
// Problem here :)
// user.Units = userView.UserUnits;
return user;
}
}
UPDATE: The main concern here is that I have to retrieve each Unit from database to match (or map) it with ViewModel.Unit objects, right? Can I avoid it?
For your information, this operation is called as Mapping mainly. So, you want to map your view model object to the entity object.
For this, you can either use already existed 3rd party library as AutoMapper. It will map properties by reflection which have same name. Also you can add your custom logic with After method. But, this approach has some advantages and disadvantages. Being aware of these disadvantages could help you to decide whether you must use this API or not. So, I suggest you to read some articles about advantages and disadvantages of AutoMapper especially for converting entities to other models. One of such disadvantages is that it can be problem to change the name of one property in the view model in the future, and AutoMapper will not handle this anymore and you won't get any warning about this.
foreach(var item in userView.UserUnits)
{
// get the mapped instance of UnitViewModel as Unit
var userUnit = Mapper.Map<UnitViewModel, UserUnit>(item);
user.Units.Add(userUnit);
}
So, I recommend to write your custom mappers.
For example, I have created a custom library for this and it maps objects lik this:
user.Units = userView.UserUnits
.Select(userUnitViewModel => userUnitViewModel.MapTo<UserUnit>())
.ToList();
And I am implementing these mapping functions as:
public class UserUnitMapper:
IMapToNew<UnitViewModel, UserUnit>
{
public UnitViewModel Map(UserUnit source)
{
return new UnitViewModel
{
Name = source.Name,
...
};
}
}
And then in runtime, I am detecting the types of the objects which will be used during mapping, and then call the Map method. In this way, your mappers will be seperated from your action methods. But, if you want it urgently, of course you can use this:
foreach(var item in userView.UserUnits)
{
// get the mapped instance of UnitViewModel as Unit
var userUnit= new UserUnit()
{
Name = item.Name,
...
};
user.Units.Add(userUnit);
}
I am new to learning how to use MongoDB and am stuck pretty early on, so hoping someone can help me out with a simple example.
I can successfully connect to a Mongo server and create a collection and create objects to put in it. I am doing all of this through c# and the c# driver.
I have the following custom objects defined in my code.
public class Feature
{
public string FeatureName { get; set; }
public ObjectId Id { get; set; }
public List<Scenario> Scenarios { get; set; }
}
public class Scenario
{
private string _notes;
public string ScenarioName { get; set; }
public List<string> Givens { get; set; }
public List<string> Whens { get; set; }
public List<string> Thens { get; set; }
public string Explanation { get; set; }
public string Notes { get; set; }
}
As you can see, the Feature object contains a property which is a list of scenarios.
In my Mongo collection I have a Feature object that contains 3 scenarios. What I want to do in C# is write a method that can remove a specific scenario from a feature:
User provides a feature and scenario name to a method
Method checks the feature, looking through the list within it, for a scenario where the scenario.scenarioname matches the scenario name passed in to the method
If it exists, then remove the scenario from the feature and update Mongo and return a bool of true. If it doesn't exist return false
I am sure that this is probably going to be obvious when I see an example, but I have tried and get stuck trying to work through the List property looking for a property on a sub object.
I hope that all makes sense??!?
Thanks in advance.
P
Resolved it myself...
public bool DeleteScenario(string featureName, string scenarioName)
{
var collection = GetCollection<Feature>();
var query = Query.EQ("FeatureName", featureName);
var resultingFeature = collection.Find(query).SetLimit(1).FirstOrDefault();
if (resultingFeature == null)
{
return false;
}
// we have found our feature and it exists.
foreach (var scenario in resultingFeature.Scenarios)
{
if (scenario.ScenarioName == scenarioName)
{
resultingFeature.Scenarios.Remove(scenario);
collection.Save(resultingFeature);
return true;
}
}
return true;
}
I have the following entity:
public class SampleClass
{
public int Id { get; set; }
public object Args {get; set; }
}
Because Args can be of different types and doesnt need to be queryable, I want to store it in the Database as a json string.
I know the following workaround would solve my problem:
public class SampleClass
{
public int Id { get; set; }
public object Args { get { return Json.Deserialize(ArgsJson); } set { ArgsJson = Json.Serialize(value); } }
public string ArgsJson {get; set; }
}
But this is pretty ugly as it exposes information not related to the model and it contains logic again not related to the model.
What I would like to do, is something like that:
public class SampleClassMapper : EntityTypeConfiguration<SampleClass>
{
public SampleClassMapper()
{
this.Property(e => e.Args).MapAs<string>(arg => Json.Serialize(arg), str => Json.Deserialize(str));
}
}
Is there any cool way of doing so?
(I'm using .Net 4.0 with EntityFramework 5 and Sql Server 2008 if it helps)
The way that you do is the only one available for now in EF. Currently EF Code First don't have any easy way to change the object serialization but this can be done modifying the EDMX file at runtime.
I have created the following class which I believe gives me some good auditing capabilities for data rows in certain tables that require it. Here is the class I am using:
public class AuditableTableServiceEntity : TableServiceEntity
{
protected AuditableTableServiceEntity()
: base()
{
}
protected AuditableTableServiceEntity(string pk, string rk)
: base(pk, rk)
{
}
#region CreatedBy and ModifiedBy
private string _CreatedBy;
[DisplayName("Created By")]
public string CreatedBy
{
get { return _CreatedBy; }
set { _CreatedBy = value; Created = DateTime.Now; }
}
[DisplayName("Created")]
public DateTime? Created { get; set; }
private string _ModifiedBy;
[DisplayName("Modified By")]
public string ModifiedBy
{
get { return _ModifiedBy; }
set { _ModifiedBy = value; Modified = DateTime.Now; }
}
[DisplayName("Modified")]
public DateTime? Modified { get; set; }
#endregion
}
Can anyone out there suggest any additional changes that I might consider for this class. I believe it is okay but as I need to implement this for many classes I would like to hear if anyone can suggest any changes or additions.
private string _ModifiedBy;
[DisplayName("Modified By")]
public string ModifiedBy
{
get { return _ModifiedBy; }
set { _ModifiedBy = value; Modified = DateTime.Now; }
}
will cause a stack overflow: setting the value of a property in a setter calls the setter, which sets the value of the property, which calls the setter, and so on.
You could set the properties in a constructor, but then things break if an instance is serialized and deserialized: when you deserialize it, the public parameterless constructor is called, and the setter is called... which sets the property to the date and time that the object was deserialized, not the stored value.
A better pattern might be to create another table for auditable events. This might look something like this:
public class Audit
{
public string ModifiedBy { get; set; }
public DateTime DateModified { get; set; }
public Type ObjectType { get; set; }
public string Field { get; set; }
public object OldValue { get; set; }
public object NewValue { get; set; }
public static void Record(string user, Type objectType, object oldValue, object newValue)
{
Audit newEvent = new Audit
{
ModifiedBy = user,
DateModified = DateTime.UtcNow, // UtcNow avoids timezone issues
ObjectType = objectType,
OldValue = oldValue,
NewValue = newValue
};
Save(newEvent); // implement according to your particular storage classes
}
}
Then, whenever you make changes to an object you want to audit, call Audit.Record() like so:
public class SomeKindOfAuditableEntity
{
private string _importantFieldToTrack;
public string ImportantFieldToTrack
{
get { return _importantFieldToTrack; }
set
{
Audit.Record(GetCurrentUser(), this.GetType(), _importantFieldToTrack, value);
_importantFieldToTrack = value;
}
}
}
This way you store a log of all changes that happen to all "interesting" properties of your tables. This has a few other advantages:
you see the old and new values of each change
the audit log is stored in a different place from the data itself, separating concerns
you don't need to have a base class for your data classes
the audit for old changes is kept around so you can go back through the entire log of the object's changes
The principal disadvantage is that you need to add code to each setter for each property you're interested in. There are ways to mitigate this with attributes and reflection and aspect-oriented programming -- see for instance Spring's implementation here: http://www.springframework.net/doc-latest/reference/html/aop.html -- in essence, you'd create an attribute for the properties you'd like to track.
The other disadvantage is that you'll consume lots of storage for the audit log - but you can have a background process that trims down the old entries periodically as you see fit.