I use a similar approach to the one described in this post to map multiple stored procedure result sets to a parent and child entity using Entity Framework. However, every implementation of this approach that I've seen assigns the results of subsequent result sets (after the first) to properties of the parent entity directly; I'd like to accomplish this in a generic fashion.
So far I've been able to build a wrapper that successfully maps results in a one-to-many relationship. I don't need to care about enforcing the foreign key relationship in this case because the stored procedure handles that, but when selecting many parent entities, I need to somehow enforce the relationship.
How can I leverage the foreign key relationship in Entity Framework (using reflection, presumably) to enforce the mapping?
Here's what I have:
Entities
class FooEntity
{
[Key]
int Id { get; set;}
ICollection<BarEntity> Bars { get; set; }
}
class BarEntity
{
[Key]
int Id { get; set;}
int FooId { get; set; }
[ForeignKey("FooId")]
virtual FooEntity Foo { get; set; }
}
Generic stored procedure translator:
public class StoredProcedureTranslator<TDbContext> : IDisposable where TDbContext : DbContext, new()
{
private DbCommand command;
public StoredProcedureTranslator()
{
this.Context = new TDbContext();
}
public DbContext Context { get; set; }
public DbDataReader Reader { get; set; }
public StoredProcedureResult<T, TDbContext> Translate<T>(string procedureName, SqlParameter[] parameters) where T : class
{
this.command = this.Context.Database.Connection.CreateCommand();
this.command.CommandText = procedureName;
if (parameters != null)
{
this.command.Parameters.AddRange(parameters);
}
this.command.CommandType = CommandType.StoredProcedure;
this.Context.Database.Connection.Open();
this.Reader = this.command.ExecuteReader();
ObjectResult<T> results = ((IObjectContextAdapter)this.Context).ObjectContext.Translate<T>(this.Reader);
return new StoredProcedureResult<T, TDbContext>(results.ToList(), this);
}
public void Dispose()
{
this.Context.Dispose();
}
}
Generic stored procedure result:
public class StoredProcedureResult<T, TDbContext> : IEnumerable<T>
where T : class where TDbContext : DbContext, new()
{
private readonly IEnumerable<T> results;
private readonly StoredProcedureTranslator<TDbContext> translator;
public StoredProcedureResult(IEnumerable<T> results, StoredProcedureTranslator<TDbContext> translator)
{
this.results = results;
this.translator = translator;
}
public StoredProcedureResult<T, TDbContext> Include<TChild>(Expression<Func<T, IEnumerable<TChild>>> member) where TChild : class
{
T result = this.results.FirstOrDefault();
if (result == null)
{
return null;
}
if (!this.translator.Reader.NextResult())
{
return new StoredProcedureResult<T, TDbContext>(new List<T> { result }, this.translator);
}
// TODO: do some stuff with the fks
ObjectResult<TChild> childResults = ((IObjectContextAdapter)this.translator.Context).ObjectContext.Translate<TChild>(this.translator.Reader);
var prop = member.Body as MemberExpression;
if (prop == null)
{
throw new Exception("blah blah");
}
var property = prop.Member as PropertyInfo;
if (property == null)
{
throw new Exception("blah blah");
}
property.SetValue(result, childResults.ToList(), null);
return new StoredProcedureResult<T, TDbContext>(new List<T> { result }, this.translator);
}
#region IEnumerable Impl
public IEnumerator GetEnumerator()
{
return this.results.GetEnumerator();
}
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return (IEnumerator<T>)this.GetEnumerator();
}
#endregion
}
Usage
using (var spt = new StoredProcedureTranslator<DbContext>())
{
FooEntity foo = spt.Translate<FooEntity>("[dbo].[foo_get]", null).Include(f => f.Bars).FirstOrDefault();
}
If this is intended to be used for entity types only, you can let EF context tracking infrastructure and relationship fix up do the work for you (pretty match like the EF materializes entity queries).
The trick is to use the following Translate method overload
public virtual ObjectResult<TEntity> Translate<TEntity>(DbDataReader reader, string entitySetName, MergeOption mergeOption)
and pass the entitySetName of the entity type.
Here is small helper utility class which does that (plus some other useful methods):
public static class EntityUtils
{
public static string GetEntitySetName<T>(this IObjectContextAdapter dbContext) where T : class
{
return dbContext.ObjectContext.CreateObjectSet<T>().EntitySet.Name;
}
public static ObjectResult<T> ReadSingleResult<T>(this IObjectContextAdapter dbContext, DbDataReader dbReader)
where T : class
{
return dbContext.ObjectContext.Translate<T>(dbReader, dbContext.GetEntitySetName<T>(), MergeOption.AppendOnly);
}
public static void Load<T>(this ObjectResult<T> source) where T : class
{
// Consume the enumerable by iterating it
using (var en = source.GetEnumerator())
while (en.MoveNext()) { }
}
}
The StoredProcedureTranslator and StoredProcedureResult can be modified as follows (I find the design a bit flawed, but that's outside the scope of the question).
The result:
public class StoredProcedureResult<T> : IEnumerable<T>
where T : class
{
private readonly DbContext context;
private readonly DbDataReader reader;
private readonly IEnumerable<T> results;
public StoredProcedureResult(DbContext context, DbDataReader reader)
{
this.context = context;
this.reader = reader;
this.results = this.context.ReadSingleResult<T>(this.reader).ToList();
}
public StoredProcedureResult<T> Include<TChild>() where TChild : class
{
if (this.reader.NextResult())
this.context.ReadSingleResult<TChild>(this.reader).Load();
return this;
}
public IEnumerator<T> GetEnumerator()
{
return this.results.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
Note that it doesn't need TDbContext generic parameter anymore, and all it does is to hold the root result set while providing fluent interface for reading the next results. The results are just translated and consumed, there is no need to be stored because during the consuming part EF will bind them to the already loaded related entities.
The translate method inside the StoredProcedureTranslator class:
public StoredProcedureResult<T> Translate<T>(string procedureName, SqlParameter[] parameters) where T : class
{
this.command = this.Context.Database.Connection.CreateCommand();
this.command.CommandText = procedureName;
if (parameters != null)
this.command.Parameters.AddRange(parameters);
this.command.CommandType = CommandType.StoredProcedure;
this.Context.Database.Connection.Open();
this.Reader = this.command.ExecuteReader();
return new StoredProcedureResult<T>(this.Context, this.Reader);
}
Usage:
using (var spt = new StoredProcedureTranslator<DbContext>())
{
var foo = spt.Translate<FooEntity>("[dbo].[foo_get]", null)
.Include<BarEntity>()
.FirstOrDefault();
}
Related
I'm trying to convert a collection of model objects that share a common parent, into one of DTOs. Likewise, I want to reverse the procedure - taking a collection of DTOs with a common parent into one of model objects.
From what I've read, a Factory Pattern seems to be what I'm looking for. I also have a Producer class that handles the conversion between object model and DTO by calling the relevant factory method.
There are a few limitations:
This is an open source library, and I don't want to add methods to existing classes. Otherwise a visitor pattern would have worked. Please correct me if I'm wrong.
Similarly, I don't want to add any additional packages to this project. From what I understand, AutoMapper would have been one of the ways to go about this.
I'm new(ish) to C# and design patterns, so I apologize if I am doing something that doesn't make sense.
Here is some sample code representing what I've tried so far. I used some references from online to get an idea, but something about it doesn't seem right. There was another way mentioned here: Is a switch statement applicable in a factory method? c#, but I'm not sure if that is transferrable to this scenario.
Any critique or suggestions is welcome.
Example Usage
Animal pet1 = new Pigeon("Pidgey", 100, false);
Animal pet2 = new Rattlesnake("Ekans", 20.0, true);
IList<Animal> myPets = new List<Animal>() { pet1, pet2 };
AnimalDTOProducer dtoProducer = new AnimalDTOProducer(new AnimalDTOFactory());
IList<AnimalDTO> myDTOs = new List<AnimalDTO>();
myDTOs = dtoProducer.ConvertAnimalCollection(myPets);
Models
public abstract class Animal
{
public Animal(string name)
{
Name = name;
}
public string Name { get; set; }
// business logic
}
public abstract class Bird : Animal
{
public Bird(string name, int maxAltitude, bool isReal)
: base(name)
{
Name = name;
MaxAltitude = maxAltitude;
IsReal = isReal;
}
public int MaxAltitude { get; set; }
public bool IsReal { get; set; }
// business logic
}
public class Pigeon : Bird
{
public Pigeon(string name, int maxAltitude, bool isReal)
: base(name, maxAltitude, isReal)
{
}
// business logic
}
public abstract class Snake : Animal
{
public Snake(string name, double length, bool isPoisonous)
: base(name)
{
Name = name;
Length = length;
IsPoisonous = isPoisonous;
}
public double Length { get; set; }
public bool IsPoisonous { get; set; }
// business logic
}
public class Rattlesnake : Snake
{
public Rattlesnake(string name, double length, bool isPoisonous)
: base(name, length, isPoisonous)
{
}
// business logic
}
DTOs
public abstract class AnimalDTO { }
public class PigeonDTO : AnimalDTO
{
public string Name { get; set; }
public int MaxAltitude { get; set; }
public bool IsReal { get; set; }
}
public class RattlesnakeDTO : AnimalDTO
{
public string Name { get; set; }
public double Length { get; set; }
public bool IsPoisonous { get; set; }
}
Factories
public interface IFactory { }
public interface IAnimalFactory : IFactory
{
Animal CreateAnimal(AnimalDTO DTO);
}
public interface IAnimalDTOFactory : IFactory
{
AnimalDTO CreateAnimalDTO(Animal animal);
}
public class AnimalFactory : IAnimalFactory
{
public Animal CreateAnimal(AnimalDTO DTO)
{
switch (DTO)
{
case PigeonDTO _:
var pigeonDTO = (PigeonDTO)DTO;
return new Pigeon(pigeonDTO.Name, pigeonDTO.MaxAltitude, pigeonDTO.IsReal);
case RattlesnakeDTO _:
var rattlesnakeDTO = (RattlesnakeDTO)DTO;
return new Rattlesnake(rattlesnakeDTO.Name, rattlesnakeDTO.Length, rattlesnakeDTO.IsPoisonous);
// And many more ...
default:
return null;
}
}
}
public class AnimalDTOFactory : IAnimalDTOFactory
{
public AnimalDTO CreateAnimalDTO(Animal animal)
{
switch (animal)
{
case Pigeon _:
var _pigeon = (Pigeon)animal;
return new PigeonDTO()
{
Name = _pigeon.Name,
MaxAltitude = _pigeon.MaxAltitude,
IsReal = _pigeon.IsReal
};
case Rattlesnake _:
var _rattlesnake = (Rattlesnake)animal;
return new RattlesnakeDTO()
{
Name = _rattlesnake.Name,
Length = _rattlesnake.Length,
IsPoisonous = _rattlesnake.IsPoisonous
};
// And many more ...
default:
return null;
}
}
}
Producers
public interface IProducer { }
public interface IAnimalProducer : IProducer
{
Animal ProduceAnimalFromDTO(AnimalDTO DTO);
}
public interface IAnimalDTOProducer : IProducer
{
AnimalDTO ProduceAnimalDTOFromAnimal(Animal animal);
}
public class AnimalProducer : IAnimalProducer
{
private IAnimalFactory factory;
public AnimalProducer(IAnimalFactory factory)
{
this.factory = factory;
}
public IList<Animal> ConvertAnimalDTOCollection(IList<AnimalDTO> DTOCollection)
{
IList<Animal> result = new List<Animal>();
foreach (AnimalDTO DTO in DTOCollection)
{
var dto = ProduceAnimalFromDTO(DTO);
if (dto != null)
result.Add(dto);
}
return result;
}
public Animal ProduceAnimalFromDTO(AnimalDTO animalDTO)
{
return this.factory.CreateAnimal(animalDTO);
}
}
public class AnimalDTOProducer : IAnimalDTOProducer
{
private IAnimalDTOFactory factory;
public AnimalDTOProducer(IAnimalDTOFactory factory)
{
this.factory = factory;
}
public IList<AnimalDTO> ConvertAnimalCollection(IList<Animal> collection)
{
IList<AnimalDTO> result = new List<AnimalDTO>();
foreach (Animal animal in collection)
{
var _animal = ProduceAnimalDTOFromAnimal(animal);
if (_animal != null)
result.Add(_animal);
}
return result;
}
public AnimalDTO ProduceAnimalDTOFromAnimal(Animal animal)
{
return this.factory.CreateAnimalDTO(animal);
}
}
UPDATE 1
As recommended by sjb-sjb and ChiefTwoPencils in the comments, I eliminated the switch statements from the respective factories. The result looks like this:
public class AnimalFactory : IAnimalFactory
{
public Animal CreateAnimal(AnimalDTO DTO)
{
Type srcType = DTO.GetType();
Type modelType = Type.GetType(Regex.Replace(srcType.FullName, #"(DTO)$", ""));
IList<PropertyInfo> props = new List<PropertyInfo>(srcType.GetProperties());
var propVals = props.Select(prop => prop.GetValue(DTO, null)).ToArray();
Animal animal = (Animal)Activator.CreateInstance(modelType, propVals);
return animal;
}
}
public class AnimalDTOFactory : IAnimalDTOFactory
{
public AnimalDTO CreateAnimalDTO(Animal animal)
{
Type srcType = animal.GetType();
Type dtoType = Type.GetType($"{srcType.FullName}DTO");
AnimalDTO dto = (AnimalDTO)Activator.CreateInstance(dtoType, new object[] { });
foreach (PropertyInfo dtoProperty in dtoType.GetProperties())
{
PropertyInfo srcProperty = srcType.GetProperty(dtoProperty.Name);
if (srcProperty != null)
{
dtoProperty.SetValue(dto, srcProperty.GetValue(animal));
}
}
return dto;
}
}
The one thing I forgot to mention in the original question was that the constructor for the model may have more arguments than the DTO object has properties. That, and the order of arguments may not be the same. I think in pseudo-code, a solution will look something like this:
void AssignParamsToConstructor()
{
// Extract constructer parameters with names into an ordered list
// Match DTO properties with extracted parameters via name and type
// Fill any missing parameters with a default value or null
// Pass the final list of parameters as an array to Activator.CreateInstance method
}
I will be researching on a way to resolve this for the time being, but any pointers will be welcome.
UPDATE 2
Okay, so I found a kind of hacky solution for the previous problem regarding calling the Model constructor with missing or out-of-order arguments.
I created a helper class that creates an ordered argument array based on a combination of the Model constructor arguments and the DTO properties. This array can then be passed to Activator.CreateInstance without causing any issues.
Here is the updated AnimalFactory.CreateAnimal method:
public Animal CreateAnimal(AnimalDTO DTO)
{
Type srcType = DTO.GetType();
Type modelType = Type.GetType(Regex.Replace(srcType.FullName, #"(DTO)$", ""));
object[] propVals = Helpers.GenerateConstructorArgumentValueArray(modelType, DTO);
Animal animal = (Animal)Activator.CreateInstance(modelType, propVals);
return animal;
}
And here is the helper class:
public static class Helpers
{
public static object[] GenerateConstructorArgumentValueArray(Type type, object obj)
{
IList<(string, Type)> ctorArgTypes = new List<(string, Type)>();
IList<(string, object)> propVals = new List<(string, object)>();
// Get constructor arguments
ctorArgTypes = GetConstructorArgumentsAndTypes(type);
// Get object properties
propVals = GetObjectPropertiesAndValues(obj);
// Create args array
IList<object> paramVals = new List<object>();
foreach (var ctorArg in ctorArgTypes)
{
object val;
string _name = ctorArg.Item1.ToLower();
(string, object) _namedProp = propVals.Where(prop => prop.Item1.ToLower() == _name).FirstOrDefault();
if (_namedProp.Item2 != null)
{
val = _namedProp.Item2;
}
else
{
val = ctorArg.Item2.IsValueType ? Activator.CreateInstance(ctorArg.Item2) : null;
}
paramVals.Add(val);
}
return paramVals.ToArray();
}
private static IList<(string, Type)> GetConstructorArgumentsAndTypes(Type type)
{
List<(string, Type)> ctorArgs = new List<(string, Type)>();
TypeInfo typeInfo = type.GetTypeInfo();
ConstructorInfo[] ctors = typeInfo.DeclaredConstructors.ToArray();
ParameterInfo[] ctorParams = ctors[0].GetParameters();
foreach (ParameterInfo info in ctorParams)
{
ctorArgs.Add((info.Name, info.ParameterType));
}
return ctorArgs;
}
private static IList<(string, object)> GetObjectPropertiesAndValues(object obj)
{
List<(string, object)> props = new List<(string, object)>();
PropertyInfo[] propInfo = obj.GetType().GetProperties();
foreach (PropertyInfo info in propInfo)
{
string name = info.Name;
object val = info.GetValue(obj);
props.Add((name, val));
}
return props;
}
}
I'll have to look at this later to see how it can be improved on. For the time being however, it does its job.
I would appreciate any comments or input if you have any. I will keep updating this post until I find an absolute solution.
So I worked out a solution that seems to achieve my original goal.
The reason it was difficult to solve at first was due to the original Factory class having too many responsibilities. It had to map the properties and create a new object. Separating these made it easy to implement the Generic Factory suggested by this post:
https://web.archive.org/web/20140414013728/http://tranxcoder.wordpress.com/2008/07/11/a-generic-factory-in-c
I created a simple mapper that would automatically map Entity and DTO properties. The easier solution is to use an AutoMapper like grandaCoder suggested. My situation required otherwise so a custom mapper was the way to go. I also tried to minimize calls to System.Reflection so the performance wouldn't suffer too much.
The end result is a Factory that can convert between any Entity and DTO object, maps properties between them, and can instantiate an Entity class with no default / empty constructor.
I ended up making a lot more changes to the original post, so I uploaded the end result to github: https://github.com/MoMods/EntityDTOFactory
I am open to any additional ideas / criticisms on the final solution. This is my first time solving this kind of problem, so it's very likely there are some better ideas out there.
Thanks again for the help and suggestions!
The switch statement can be avoided using reflection:
public AnimalDTO ToDTO( Animal src)
{
Type srcType = src.GetType();
Type dtoType = Type.GetType(srcType.Name + "DTO");
AnimalDTO dto = (AnimalDTO)Activator.CreateInstance(dtoType, new object[] { });
foreach (PropertyInfo dtoProperty in dtoType.GetProperties()) {
PropertyInfo srcProperty = srcType.GetProperty(dtoProperty.Name);
if (srcProperty != null) {
dtoProperty.SetValue(dto, srcProperty.GetValue(src));
}
}
return dto;
}
To get a FromDTO method, just reverse the roles of src and dto in ToDTO.
I would not reinvent the wheel on this common scenario.
https://automapper.org/
https://www.nuget.org/packages/automapper/
OR
https://github.com/MapsterMapper/Mapster
https://www.nuget.org/packages/Mapster/
.......
Learn how to use one of these frameworks.
Below is mapster..........."performance" numbers.......which is how I found it (someone told me to lookout for automapper performance)
Question
How do I define an incoming Type T constraint that will allow me to call a static method on the class (of type T) to get the intended IndexModel object for passing to Mongo?
Background
I'm currently trying to write a Mongo Provider class that will allow me to ensure my particular database and collection are present before doing any operations with them, since there is a potential that the container or server it resides in could be destroyed and recreated at any time, and I'd prefer to have a safe way in code to ensure that the external dependency is there (instance is beyond my control, so I have to trust that something is there).
One of the things I'm trying to do, since I've managed to do what I stated above for Database and Collection instantiation, is to also generate indexes. My idea was to have a static method on the classes that would return their specific definition of an index model. This way, each class would be responsible for their own Mongo indexes, rather than some convoluted switch-case statement in my Provider based on the incoming type of T.
My first idea was to have an interface that shared this method, but Interfaces don't allow you to declare a static method. Similarly, I tried an Abstract Base-class and found that the static implementation would call the base class that defined the method, rather than any overrides in an inheritor.
Sample Code
public class MyClass
{
public DateTime DateValue { get; set; }
public int GroupId { get; set; }
public string DataType { get; set; }
public static IEnumerable<CreateIndexModel<MyClass>> GetIndexModel(IndexKeysDefinitionBuilder<MyClass> builder)
{
yield return new CreateIndexModel<MyClass>(
builder.Combine(
builder.Descending(entry => entry.DateValue),
builder.Ascending(entry => entry.GroupId),
builder.Ascending(entry => entry.DataType)
)
);
}
}
Edit
I guess I should probably include a shell of my Mongo Provider class. See below:
Edit #2 due to questions about how this hasn't solved my problem, I'm updating the MongoProvider to have the problematic code. Note: Once this method is included, the class will no longer compile, since it isn't possible given what I've done thus far.
public class MongoProvider
{
private readonly IMongoClient _client;
private MongoPrivder(ILookup<string, string> lookup, IMongoClient client)
{
_client = client;
foreach(var database in lookup)
foreach(var collection in database)
Initialize(database.Key, collection);
}
public MongoProvider(IConfiguration config) :this(config.GetMongoObjects(), config.GetMongoClient())
{}
public MongoProvider(IConfiguration config, IMongoClient client) : this(config.GetMongoObjects(), client)
{}
private void Initialize(string database, string collection)
{
var db = _client.GetDatabase(database);
if (!db.ListCollectionNames().ToList().Any(name => name.Equals(collection)))
db.CreateCollection(collection);
}
// The Problem
private void InitializeIndex<T>(string database, string collection)
{
IEnumerable<CreateIndexModel<T>> models;
switch (T)
{
case MyClass:
model = MyClass.GetIndexModel();
break;
default:
break;
}
await _client.GetDatabase(database)
.GetCollection<T>(collection)
.Indexes
.CreateManyAsync(models);
}
}
Edit #3
As a stop-gap, I've gone ahead and done something terrible (not sure if it's going to work yet), and I'll supply the example so you can know my best solution thus far.
public static class Extensions
{
#region Object Methods
public static T TryCallMethod<T>(this object obj, string methodName, params object[] args) where T : class
{
var method = obj.GetType().GetMethod(methodName);
if (method != null)
{
return method.Invoke(obj, args) as T;
}
return default;
}
#endregion
}
This allows me to do the following (inside of MongoProvider)
private async void InitializeIndex<T>(string database, string collection) where T : new()
{
var models = new T().TryCallMethod<IEnumerable<CreateIndexModel<T>>>("GetIndexModel");
await _client.GetDatabase(database)
.GetCollection<T>(collection)
.Indexes
.CreateManyAsync(models);
}
Since it doesn't look like I'm going to get an answer to this, I figured I would provide my solution for future searches of this question. Basically, I added an extension method to the base object class, and used reflection to determine if the method I was looking for was there. From there, I returned a value of true or false, depending on if the method was found, and output the return value to a parameter, in the traditional TryGet pattern.
Note to Future Readers
I do not recommend this approach. This is just how I solved my problem for accessing a method on a type of T. Ideally, an instance method would be implemented, and a signature defined in a common Interface, but that wasn't going to work for my use case.
My Answer
public static class Extensions
{
#region Object Methods
public static bool TryCallMethod<T>(this object obj, string methodName, out T result, params object[] args) where T : class
{
result = null;
var method = obj.GetType().GetMethod(methodName);
if (method == null)
return false;
result = method.Invoke(obj, args) as T;
return true;
}
#endregion
}
My data class looks like this (obfuscated from actual usage)
[BsonDiscriminator("data")]
public class DataClass
{
#region Private Fields
private const string MongoCollectionName = "Data";
#endregion
#region Public Properties
public string CollectionName => MongoCollectionName;
[BsonId]
public ObjectId Id { get; set; }
[BsonElement("date_value")]
public DateTime DateValue { get; set; }
[BsonElement("group_id")]
public int GroupId { get; set; }
[BsonElement("data_type")]
public string DataType { get; set; }
[BsonElement("summary_count")]
public long SummaryCount { get; set; }
[BsonElement("flagged_count")]
public long FlaggedCount { get; set; }
[BsonElement("error_count")]
public long ErrorCount { get; set; }
#endregion
#region Constructor
public DataClass()
{
}
public DataClass(int groupId, string dataType = null, long summaryCount = 0, long flaggedCount = 0, long errorCount = 0)
{
Id = ObjectId.GenerateNewId();
DateValue = DateTime.UtcNow;
GroupId = groupId;
DocCount = summaryCount;
DataType = dataType ?? "default_name";
FlaggedCount = flaggedCount;
ErrorCount = errorCount;
}
#endregion
#region Public Methods
public static IEnumerable<CreateIndexModel<AuditEntry>> GetIndexModel(IndexKeysDefinitionBuilder<AuditEntry> builder)
{
yield return new CreateIndexModel<AuditEntry>(
builder.Combine(
builder.Descending(entry => entry.DateValue),
builder.Ascending(entry => entry.GroupId),
builder.Ascending(entry => entry.DataType)
)
);
}
#endregion
}
I would then call the method in the following fashion, inside my MongoProvider class. The ellipses are present to identify that more code exists within the class.
public class MongoProvider : IMongoProvider
{
#region Private Fields
private readonly IMongoClient _client;
#endregion
#region Constructor
...
#endregion
#region Private Methods
private void Initialize(string database, string collection)
{
var db = _client.GetDatabase(database);
if (!db.ListCollectionNames().ToList().Any(name => name.Equals(collection)))
db.CreateCollection(collection);
}
private async Task InitializeIndex<T>(string database, string collection) where T : new()
{
if(new T().TryCallMethod<IEnumerable<CreateIndexModel<T>>>("GetIndexModel", out var models, new IndexKeysDefinitionBuilder<T>()))
await _client.GetDatabase(database)
.GetCollection<T>(collection)
.Indexes
.CreateManyAsync(models);
}
private static void ValidateOptions<T>(ref FindOptions<T, T> options)
{
if(options != null)
return;
options = new FindOptions<T, T>
{
AllowPartialResults = null,
BatchSize = null,
Collation = null,
Comment = "AspNetWebService",
CursorType = CursorType.NonTailable,
MaxAwaitTime = TimeSpan.FromSeconds(10),
MaxTime = TimeSpan.FromSeconds(10),
Modifiers = null,
NoCursorTimeout = false,
OplogReplay = null
};
}
private static FilterDefinition<T> GetFilterDefinition<T>(Func<FilterDefinitionBuilder<T>, FilterDefinition<T>>[] builders)
{
if(builders.Length == 0)
builders = new Func<FilterDefinitionBuilder<T>, FilterDefinition<T>>[] {b => b.Empty};
return new FilterDefinitionBuilder<T>()
.And(builders
.Select(b => b(new FilterDefinitionBuilder<T>()))
);
}
#endregion
#region Public Methods
public async Task<IReadOnlyCollection<T>> SelectManyAsync<T>(string database, string collection, FindOptions<T, T> options = null, params Func<FilterDefinitionBuilder<T>, FilterDefinition<T>>[] builders) where T : new()
{
ValidateOptions(ref options);
await InitializeIndex<T>(database, collection);
var filter = GetFilterDefinition(builders);
var find = await _client.GetDatabase(database)
.GetCollection<T>(collection)
.FindAsync(filter, options);
return await find.ToListAsync();
}
...
#endregion
}
I have just learned about GraphDiff, and how it is supposed to take care of all the differences between the disconnected entity and the one stored in the database.
The thing is that I do not know how to use GraphDiff, I tried the documentation, but I didn't understand it well.
I am using an abstracted DBContext, through an Interface and using DbSet so I could perform Unit Testing on them:
public interface IDbRepositories
{
IDbSet<Client> ClientsDB { get;}
AppIdentityDbContext DB { get; }
IDbSet<Contacts> ContactsDB { get; }
IDbSet<ExposureResult> ExposureDB { get; }
IDbSet<OrderMods> OrderModsDB { get; }
IDbSet<ProductDetails> ProductDetailsDB { get; }
IDbSet<OrderProcess> OrderProcessDB { get; }
IDbSet<Order> OrderDB { get; }
void SaveChanges();
}
This is the actual class implementing the interface:
public class DbRepositories : IDbRepositories
{
private AppIdentityDbContext db = new AppIdentityDbContext();
//Get DB Context. This is done this way, so a Mock can be injected when testing
public IDbSet<Client> ClientsDB
{
get { return db.Clients; }
}
public AppIdentityDbContext DB
{
get { return db; }
}
public IDbSet<Contacts> ContactsDB
{
get { return db.Contacts; }
}
public IDbSet<ExposureResult> ExposureDB
{
get { return db.ExposureTBL; }
}
public IDbSet<OrderMods> OrderModsDB
{
get { return db.OrderMods; }
}
public IDbSet<ProductDetails> ProductDetailsDB
{
get { return db.ProductDetailsTBL; }
}
public IDbSet<OrderProcess> OrderProcessDB
{
get { return db.OrderProcesses; }
}
public IDbSet<Order> OrderDB
{
get { return db.OrderTBL; }
}
public void SaveChanges()
{
this.db.SaveChanges();
}
}
Now, the problem part is in here:
public bool SaveOrderChanges(OrderProcess[] Order, int OrderID, int uid)
{
//2nd Step:
var ComparableObject = dbs.OrderProcessDB.Where(x => x.OrderID == OrderID).ToList();
var Objections = dbs.OrderDB.Where(x => x.OrderID == OrderID).FirstOrDefault();
dbs.DB.UpdateGraph(dbs.OrderDB, m => m.OwnedCollection());
dbs.SaveChanges();
return true;
}
I'd like to tell the differences between the Order parameter and the one I extract from OrderProcessDB. These are a One to Many Relationship.
I do not know how to use GraphDiff for this scenario. Any ideas?
You could just expose the base DbContext object in the interface, but that would violate basic principles of encapsulation. The challenge is that the UpdateGraph method is a static extension off of the concrete DbContext class. Here is my solution:
First the interface:
public interface IMyDbContext
{
...
TEntity UpdateGraph<TEntity>(TEntity entity, Expression<Func<IUpdateConfiguration<TEntity>, object>> mapping = null) where TEntity : class, new();
}
Then the actual DbContext:
public class MyDbContext : DbContext, IMyDbContext
{
...
public TEntity UpdateGraph<TEntity>(TEntity entity, Expression<Func<IUpdateConfiguration<TEntity>, object>> mapping = null) where TEntity : class, new()
{
return ((DbContext)this).UpdateGraph(entity, mapping);
}
}
And lastly example usage inside of a repository:
public class MyRepository : IMyRepository
{
private readonly IMyDbContext _myDbContext;
public MyRepository (IMyDbContext myDbContext)
{
_myDbContext = myDbContext;
}
public async Task<SomeEntity> UpdateSomeEntity(SomeEntity updatedSomeEntity)
{
_myDbContext.UpdateGraph(updatedSomeEntity, map => map.OwnedCollection(p => p.SomeChildCollection));
await _myDbContext.SaveChangesAsync();
return updatedSomeEntity;
}
}
I realize this is old, but I just found out about GraphDiff and can hopefully help anyone else looking.
This is how you use GraphDiff:
db.UpdateGraph(orderToUpdate, map => map
.AssociatedCollection(t => t.Products)
.OwnedCollection(t => t.PaymentMethods));
This says to update the Order object, and that the Order owns the PaymentMethods (meaning it can actually remove those entities), and is associated with the Products entities (meaning it will remove them from the reference table).
I have the following Repository Pattern. Requirement is to “Find All accounts whose owner’s name is Lijo”. So, I need to write a FindAll function. How to write this function?
Constraints are:
1) The client “BankAccountService” should not use classes from 'DBML_Project'.
2) We should NOT use GetAll method to retireve complete list of accounts and then do a filter.
Note: I confronted this problem while working on the question Polymorphism: Is ORM entity a Domain Entity or Data Entity?
CODE
namespace ApplicationService_Bank
{
public class BankAccountService
{
RepositoryLayer.ILijosBankRepository accountRepository = new RepositoryLayer.LijosSimpleBankRepository();
public void FreezeAllAccountsForUser(string userName)
{
//Should not use assembly 'DBML_Project'.
IEnumerable<DomainEntitiesForBank.IBankAccount> accountsForUserWithNameLIJO = null;
//accountsForUserWithNameLIJO = accountRepository.FindAll(p => p.BankUser.Name == "Lijo");
}
}
}
namespace RepositoryLayer
{
public interface ILijosBankRepository
{
List<DomainEntitiesForBank.IBankAccount> GetAll();
IEnumerable<DBML_Project.BankAccount> FindAll(System.Func<DBML_Project.BankAccount, bool> predicate);
void SubmitChanges();
}
public class LijosSimpleBankRepository : ILijosBankRepository
{
private IBankAccountFactory bankFactory = new MySimpleBankAccountFactory();
public System.Data.Linq.DataContext Context
{
get;
set;
}
public virtual List<DomainEntitiesForBank.IBankAccount> GetAll()
{
List<DBML_Project.BankAccount> allItems = Context.GetTable<DBML_Project.BankAccount>().ToList();
List<DomainEntitiesForBank.IBankAccount> bankAccounts = new List<DomainEntitiesForBank.IBankAccount>();
foreach (DBML_Project.BankAccount acc in allItems)
{
DomainEntitiesForBank.IBankAccount theAccount = bankFactory.CreateAccount(acc.AccountType, acc.BankAccountID, acc.Status, acc.OpenedDate, acc.AccountOwnerID);
bankAccounts.Add(theAccount);
}
return bankAccounts;
}
public IEnumerable<DBML_Project.BankAccount> FindAll(System.Func<DBML_Project.BankAccount, bool> predicate)
{
//Where
var results = Context.GetTable<DBML_Project.BankAccount>().Where(predicate);
return results;
}
public virtual void SubmitChanges()
{
Context.SubmitChanges();
}
}
}
READING:
Returning IEnumerable<T> vs. IQueryable<T>
how to design Repository pattern to be easy switch to another ORM later?
A simple approach is to just build the query by hand:
public class SearchCriteria
{
public string Name { get; set; }
// ...more
}
public IEnumerable<Entity> FindAll(SearchCriteria criteria)
{
IQueryable<Entity> entities = _datasource.Entities; // replace with your L2S equivalent
if (criteria.Name != null)
entities = entities.Where(e => e.Name == criteria.Name);
// ...more
return entities;
}
If you don't want to return the generated objects directly, map to something else before you return:
return Map(entities); // IEnumerable<CustomObject> Map(IEnumerable<Entity> entities)
I read all the similar titles questions, but i didn't find the answer to my problem, so I open a new question:
I have two mysql tables:
tb1 (int_id, code, description, ..., my_id);
tb2 (int_id, code, description, ..., tb1_id);
I create a generic repository to manage DbContext and GetAll, GetById, GetByLambda, Insert, Update and Delete methode.
namespace Model.DataAccessLayer
{
public class Repository<T> : IRepository<T> where T : EntityObject, IEntity
{
protected DbContext dbContext = null;
public virtual DbContext DbContext
{
get { return dbContext; }
set { dbContext = value; }
}
public ObjectContext ObjectContext
{
get { return ((IObjectContextAdapter)DbContext).ObjectContext; }
}
public void Dispose()
{
ObjectContext.Dispose();
System.GC.SuppressFinalize(this);
}
public virtual IQueryable<T> GetAll()
{
return ObjectContext.CreateObjectSet<T>();
}
public virtual IEnumerable<T> GetByLambda(Func<T, bool> p)
{
return GetAll().Where(p);
}
public virtual void Save()
{
DbContext.SaveChanges();
}
...
}
}
and the inherited class which I used:
namespace Model.DataAccessLayer
{
public class RepositoryP<T> : Repository<T> where T : EntityObject, IEntity
{
public myEntities MyContext
{
get { return (myEntities)ObjectContext; }
//set { ObjectContext = value; }
}
public override DbContext DbContext
{
get
{
if (dbContext == null)
{
dbContext = new DbContext("myEntities");
}
return dbContext;
}
set
{
base.DbContext = value;
}
}
}
}
It works great when using only one table.
When I try to use my two tables and the foreign key relation between them, it doesn't work.
For example I try to get all records from table tb2 where tb1.my_id=5 with the following join tb1.int_id = tb2.tb1_id.
List<tb2> lj = new Tb2DAO().GetByLambda(l => l.tb1.my_id == 5).ToList();
(Tb2DAO inherited from my generic repository class.)
I have the following MySQL error:
"MySql.Data.MySqlClient.MySqlException: There is already an open DataReader associated with this Connection which must be closed first."
I think this comes from my DbContext which is not common to my two tables entities.
So I tried to implement the UnitOfWork Pattern to solve this problem, like this:
namespace Model.DataAccessLayer
{
public class UnitOfWork : IDisposable
{
private DbContext dbContextUnit = null; //= new DbContext();
private Tb1DAO tb1DAO;
private Tb2DAO tb2DAO;
public Tb1DAO tb1
{
get
{
if (this.tb1DAO == null)
{
if (dbContextUnit != null)
{
this.tb1DAO = new Tb1DAO { DbContext = dbContextUnit };
}
else
{
this.tb1DAO = new Tb1DAO();
dbContextUnit = this.tb1DAO.DbContext;
}
}
return tb1DAO;
}
}
public Tb2DAO tb2
{
get
{
if (this.tb2DAO == null)
{
if (dbContextUnit != null)
{
this.tb2DAO = new Tb2DAO { DbContext = dbContextUnit };
}
else
{
this.tb2DAO = new Tb2DAO();
dbContextUnit = this.tb2DAO.DbContext;
}
}
return tb2DAO;
}
}
public void Save()
{
dbContextUnit.SaveChanges();
}
private bool disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
dbContextUnit.Dispose();
}
}
this.disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
}
And now in my code I tried to use the Unit of Work like this :
UnitOfWork unitOfWork = new UnitOfWork();
List<tb2> tb2List = unitOfWork.tb2.GetByLambda(l => l.index_job.job_id == job_id).ToList();
But I have always the same error message :
"MySql.Data.MySqlClient.MySqlException: There is already an open DataReader associated with this Connection which must be closed first."
is there something I am doing wrong ? Please can you help me ? This notion of repository and unit Of work are new for me
and I am confused else I read lot of think about it may be not the right one...
I also to try to add MultipleActiveResultSets=true to my connection but it is not recognized.
many thank to all & regards,
wst
Your GetByLambda() method is calling GetAll() which creates a new context using ObjectContext.CreateObjectSet<T>(). So you now have more than one context open. I would advise using the standard EF associations and the repository pattern, then you can avoid this entire mess. Here is a link that may help you get started - http://blogs.msdn.com/b/adonet/archive/2011/03/15/ef-4-1-code-first-walkthrough.aspx.
I'm not sure about this method
public virtual IQueryable<T> GetAll()
{
return ObjectContext.CreateObjectSet<T>();
}
It's create new object set about which EF context doesn't know.
You can try to pass Func<ObjectContext, ObjectResult<T>> from derived class to base. (It should return ObjectResult which are got from EF context. F.e. context => context.Entities).