How to mock IFindFluent interface - c#

I am writing unit tests with mongo db and I need to test somehow methods that works with data returned from mongo. For example method
IFindFluent<Transcript, Transcript> GetTranscriptByUserId(int userId);
Should return some transcript. But instead it returns interface that has couple of props - Filter, Options, Sort, and other.
I am going to test method Paginane of Paginator<T> class
public PaginatedObject<T> Paginate(IFindFluent<T,T> items, int limit, int page)
{
if (limit == 0)
{
limit = 10;
}
int count = (int)items.Count();
var lastPage = (count / limit) + 1;
if (page <= 0)
{
page = 1;
}
if (page > lastPage)
{
page = lastPage;
}
var request = items.Skip((page - 1) * limit).Limit(limit);
var itemsToReturn = request.ToList();
var pages = new PaginatedObject<T>
{
Entries = itemsToReturn,
Limit = limit,
Total = count,
Page = page
};
return pages;
}
First param is interface IFindFluent<T,T> items. So, I should mock it to return items when I call Count, Skip and Limit. But these methods could be easily mocked.
mockIfindFluent = new Mock<IFindFluent<Transcript, Transcript>>();
mockIfindFluent.Setup(s => s.Limit(It.IsAny<int>())).Returns(mockIfindFluent.Object);
mockIfindFluent.Setup(i => i.Skip(It.IsAny<int>())).Returns(mockIfindFluent.Object);
mockIfindFluent.Setup(i => i.Count(CancellationToken.None)).Returns(3);
Real problem I have when I call ToList().
I got exception that I can not mock property that does not belong to model and so on.

Took some inspiration from this https://gist.github.com/mizrael/a061331ff5849bf03bf2 and extended implementation which worked for me. I have created a fake implementation of the IFindFluent interface and it had a dependency on the IAsyncCursor interface, so I did a fake implementation of that also and using that in return of mock method I wanted to set up. In that fake implementation, I have initialized an enumerable and returned that in a method I am using. You can still be more creative and play around whatever you want to return. So far this worked for me.
Here is a fake implementation.
public class FakeFindFluent<TEntity, TProjection> : IFindFluent<TEntity, TEntity>
{
private readonly IEnumerable<TEntity> _items;
public FakeFindFluent(IEnumerable<TEntity> items)
{
_items = items ?? Enumerable.Empty<TEntity>();
}
public FilterDefinition<TEntity> Filter { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
public FindOptions<TEntity, TEntity> Options => throw new NotImplementedException();
public IFindFluent<TEntity, TResult> As<TResult>(MongoDB.Bson.Serialization.IBsonSerializer<TResult> resultSerializer = null)
{
throw new NotImplementedException();
}
public long Count(CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<long> CountAsync(CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public long CountDocuments(CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<long> CountDocumentsAsync(CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public IFindFluent<TEntity, TEntity> Limit(int? limit)
{
throw new NotImplementedException();
}
public IFindFluent<TEntity, TNewProjection> Project<TNewProjection>(ProjectionDefinition<TEntity, TNewProjection> projection)
{
throw new NotImplementedException();
}
public IFindFluent<TEntity, TEntity> Skip(int? skip)
{
throw new NotImplementedException();
}
public IFindFluent<TEntity, TEntity> Sort(SortDefinition<TEntity> sort)
{
throw new NotImplementedException();
}
public IAsyncCursor<TEntity> ToCursor(CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<IAsyncCursor<TEntity>> ToCursorAsync(CancellationToken cancellationToken = default)
{
IAsyncCursor<TEntity> cursor = new FakeAsyncCursor<TEntity>(_items);
var task = Task.FromResult(cursor);
return task;
}
}
public class FakeAsyncCursor<TEntity> : IAsyncCursor<TEntity>
{
private IEnumerable<TEntity> items;
public FakeAsyncCursor(IEnumerable<TEntity> items)
{
this.items = items;
}
public IEnumerable<TEntity> Current => items;
public void Dispose()
{
//throw new NotImplementedException();
}
public bool MoveNext(CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<bool> MoveNextAsync(CancellationToken cancellationToken = default)
{
return Task.FromResult(false);
}
}
Here is how I set up my mock method to return what I wanted for my unit testing.
mockParticipantRepository
.Setup(x => x.FindByFilter(It.IsAny<FilterDefinition<Participant>>()))
.Returns(new FakeFindFluent<Participant, Participant>(participantsByRelation));
I hope this is helpful.

Related

How to create an instance of Microsoft.AspNetCore.Http.HttpRequest for testing?

I wish to create an instance of HttpRequest with populated data so I can test a method.
I need to pass Microsoft.AspNetCore.Http.HttpRequest as a parameter to a function.
How do I instantiate it?
[Route("/summary/{id}")]
public IActionResult Account(int id)
{
var summary = RequestHelper.ParseRequest(Request);
}
Options is from the package
https://github.com/louthy/language-ext
public static Option<SummaryRequest> ParseRequest(HttpRequest request)
{
if (request== null)
{
var query = request.Query;
var result = new SummaryRequest();
var locations = ExtractData(query, "location");
var categories = ExtractData(query, "categories[]");
var titles = ExtractData(query, "titles");
}
}
public static Option<SummaryRequest> ParseRequest(HttpRequest request)
{
if (request== null)
{
var query = request.Query;
var result = new SummaryRequest();
var locations = ExtractData(query, "location");
var categories = ExtractData(query, "categories[]");
var titles = ExtractData(query, "titles");
}
return new SummaryRequest();
}
public static Either<Exception, string[]> ExtractData(IEnumerable<KeyValuePair<String, StringValues>> query, string filter)
{
try
{
return query.First(x => x.Key.ToLower() == filter).Value.ToString().Split(',').ToArray();
}
catch (Exception ex)
{
return ex;
}
}
Unit Test Example
[TestMethod]
[TestCategory(Test.RequestParser)]
public void ParseRequest_WithHttpRequest_ReturnResultOnSuccess()
{
// var request = this doesn't compile I need an instance of
// Microsoft.AspNetCore.Http.HttpRequest
var result = Helper.ParseRequest(request);
}
You can use the Request property of DefaultHttpContext. It's a bit convoluted since you have to specify a lot of properties separately (compared to e.g. WebRequest.Create), but the following worked for me:
var httpContext = new DefaultHttpContext();
httpContext.Request.Method = "POST";
httpContext.Request.Scheme = "http";
httpContext.Request.Host = new HostString("localhost");
httpContext.Request.ContentType = "application/json";
var stream = new MemoryStream();
var writer = new StreamWriter(stream);
await writer.WriteAsync("{}");
await writer.FlushAsync();
stream.Position = 0;
httpContext.Request.Body = stream;
Glorfindel's answer, although good enough for most scenarios fails at a critical one: The default HttpRequestStream which is the original stream beneath Request.Body is "forward-only" and if you want to read it twice, you need to use HttpContext.Request.EnableBuffering().
Now the question, how do you replicate this on a test scenario?: I created a stub implementation:
internal sealed class HttpRequestStreamStub : MemoryStream
{
public HttpRequestStreamStub() { }
public HttpRequestStreamStub(byte[] buffer) : base(buffer) { }
public override bool CanSeek => false;
public override bool CanRead => true;
public override bool CanWrite => false;
public override long Length => throw new NotSupportedException();
public override long Position
{
get => throw new NotSupportedException();
set => throw new NotSupportedException();
}
public override int WriteTimeout
{
get => throw new NotSupportedException();
set => throw new NotSupportedException();
}
public override void Write(byte[] buffer, int offset, int count)
=> throw new NotSupportedException();
public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
=> throw new NotSupportedException();
public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken)
=> throw new NotSupportedException();
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotSupportedException();
}
public override void SetLength(long value)
{
throw new NotSupportedException();
}
}
it replicates as best as possible the behavior of https://github.com/dotnet/aspnetcore/blob/main/src/Servers/Kestrel/Core/src/Internal/Http/HttpRequestStream.cs

How to mock Nhibernate .ToListAsync() in Unit-test with moq?

I'm trying create unit-tests in ASP.NET Core MVC application with moq. Unfortunately, Nhibernate.ToListAsync() is not supported Linq IQueryable dataset and throw System.NotSupportedException: 'Source Provider must be a INhQueryProvider'.
In this code I mock INhQueryProvider, but it's not enough:
var entities = new List<RequestRole>
{
new RequestRole()
{
Id = 0,
RequestOperator = new RequestOperator() { Id = 1 }
},
new RequestRole()
{
Id = 1,
RequestOperator = new RequestOperator() { Id = 2 }
}
}
.AsQueryable();
// for ToListAsync Mock INhQueryProvider and set it into IQueryable
var queryableProviderMock = new Mock<INhQueryProvider>();
queryableProviderMock.Setup(x => x.ExecuteAsync<IEnumerable<RequestRole>>(It.IsAny<Expression>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(entities);
var queryableMock = new Mock<IQueryable<RequestRole>>();
queryableMock.Setup(x => x.Provider).Returns(queryableProviderMock.Object);
queryableMock.Setup(x => x.Expression).Returns(entities.Expression);
queryableMock.Setup(x => x.GetEnumerator()).Returns(entities.GetEnumerator());
queryableMock.Setup(x => x.ElementType).Returns(entities.ElementType);
// mock CreateQuery, without this Linq.Where throwing "System.NotSupportedException: 'Source Provider must be a INhQueryProvider'"
queryableProviderMock.As<INhQueryProvider>()
.Setup(x => x.CreateQuery<RequestRole>(It.IsAny<Expression>()))
.Returns(queryableMock.Object);
var session = new Mock<ISession>();
session.Setup(s => s.Query<RequestRole>()).Returns(queryableMock.Object);
var returns = session.Object.Query<RequestRole>();
// check work
var tolistasync = await returns
.Where(x => x.Id != 0)
.ToListAsync();
In this case Linq.Where conditions not working, because I set same object instead of filtered.
Seems like I should correctly mock INhQueryProvider.CreateQuery, but how?
You'll need to instruct CreateQuery to use the expression. Just returning the mocked queryable isn't going to do anything as you've seen. Additionally CreateQuery is going to need to return an IQueryable with a provider that implements INhQueryProvider. The problem with that is the Provider property doesn't have a setter so you can't set it on an existing queryable.
The way I've solved a similar problem is to create my own sequence where I can set the provider.
Start with creating classes that implement IQueryable<T> and INhQueryProvider; for brevity I'm only implementing what is required to pass the OP use case. Note that CreateQuery<T> returns a queryable with a provider that implements INhQueryProvider:
public class TestingQueryable<T> : IQueryable<T>
{
private readonly IQueryable<T> _queryable;
public TestingQueryable(IQueryable<T> queryable)
{
_queryable = queryable;
Provider = new TestingQueryProvider<T>(_queryable);
}
public Type ElementType => _queryable.ElementType;
public Expression Expression => _queryable.Expression;
public IQueryProvider Provider { get; }
public IEnumerator<T> GetEnumerator()
{
return _queryable.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return _queryable.GetEnumerator();
}
}
public class TestingQueryProvider<T> : INhQueryProvider
{
public TestingQueryProvider(IQueryable<T> source)
{
Source = source;
}
public IQueryable<T> Source { get; set; }
public IQueryable CreateQuery(Expression expression)
{
throw new NotImplementedException();
}
public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
{
return new TestingQueryable<TElement>(Source.Provider.CreateQuery<TElement>(expression));
}
public object Execute(Expression expression)
{
throw new NotImplementedException();
}
public TResult Execute<TResult>(Expression expression)
{
return Source.Provider.Execute<TResult>(expression);
}
public Task<TResult> ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken)
{
return Task.FromResult(Execute<TResult>(expression));
}
public int ExecuteDml<T1>(QueryMode queryMode, Expression expression)
{
throw new NotImplementedException();
}
public Task<int> ExecuteDmlAsync<T1>(QueryMode queryMode, Expression expression, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public IFutureEnumerable<TResult> ExecuteFuture<TResult>(Expression expression)
{
throw new NotImplementedException();
}
public IFutureValue<TResult> ExecuteFutureValue<TResult>(Expression expression)
{
throw new NotImplementedException();
}
public void SetResultTransformerAndAdditionalCriteria(IQuery query, NhLinqExpression nhExpression, IDictionary<string, Tuple<object, IType>> parameters)
{
throw new NotImplementedException();
}
}
Update your query provider setup to use your IQueryable implementation:
queryProviderMock
.Setup(x => x.CreateQuery<RequestRole>(It.IsAny<Expression>()))
.Returns((Expression providedExpression) =>
{
return new TestingQueryable<RequestRole>(queryable.Provider.CreateQuery<RequestRole>(providedExpression));
});
Run .Where(x => x.Id != 0).ToListAsync() and get the expected result:
Working example
You could take it further and just set up the ISession mock to use your IQueryable implementation, do away with mocking the query provider if you don't need to specifically mock it. I don't normally mock what a mock returns if you know what I mean so this would meet my peer review standards.
[Test]
public async Task Test2()
{
var requestRoles = new List<RequestRole>();
requestRoles.Add(new RequestRole { Id = 0, RequestOperator = new RequestOperator { Id = 1 } });
requestRoles.Add(new RequestRole { Id = 1, RequestOperator = new RequestOperator { Id = 2 } });
var sessionMock = new Mock<ISession>();
sessionMock.Setup(s => s.Query<RequestRole>()).Returns(new TestingQueryable<RequestRole>(requestRoles.AsQueryable()));
var query = sessionMock.Object.Query<RequestRole>();
var result = await query.Where(x => x.Id != 0).ToListAsync();
Assert.Multiple(() =>
{
Assert.That(result.Count, Is.EqualTo(1));
Assert.That(result.Single(), Is.EqualTo(requestRoles.Last()));
});
}

How to emit a cartesian product in TPL/Dataflow?

I am trying to implement the following behaviour:
[TestMethod]
public async Task ProducesCartesianProductOfInputs()
{
var block = new CartesianProductBlock<int, string>();
var target = new BufferBlock<Tuple<int, string>>();
var left = block.Left;
var right = block.Right;
block.LinkTo(target);
var actual = new List<Tuple<int, string>>();
Assert.IsTrue(left.Post(1));
Assert.IsTrue(right.Post("a"));
Assert.IsTrue(left.Post(2));
Assert.IsTrue(right.Post("b"));
// PROBLEM?: These can run before messages have been processed and appear to abort further processing.
left.Complete();
right.Complete();
while (await target.OutputAvailableAsync())
{
actual.Add(target.Receive());
}
var expected = new List<Tuple<int, string>>()
{
Tuple.Create(1, "a"),
Tuple.Create(2, "a"),
Tuple.Create(1, "b"),
Tuple.Create(2, "b"),
};
CollectionAssert.AreEquivalent(expected, actual.ToList());
}
My current (partial) implementation does not work and I can't figure out why:
// A block that remembers every message it receives on two channels, and pairs every message on a channel to every message on the other channel
public class CartesianProductBlock<T1, T2> : ISourceBlock<Tuple<T1, T2>>
{
private TransformManyBlock<T1, Tuple<T1, T2>> left;
private TransformManyBlock<T2, Tuple<T1, T2>> right;
private List<T1> leftReceived = new List<T1>();
private List<T2> rightReceived = new List<T2>();
private List<ITargetBlock<Tuple<T1, T2>>> targets = new List<ITargetBlock<Tuple<T1, T2>>>();
private object lockObject = new object();
public ITargetBlock<T1> Left { get { return left; } }
public ITargetBlock<T2> Right { get { return right; } }
public CartesianProductBlock()
{
left = new TransformManyBlock<T1, Tuple<T1, T2>>(l =>
{
lock (lockObject)
{
leftReceived.Add(l);
// Pair this input up with all received alternatives
return rightReceived.Select(r => Tuple.Create(l, r));
}
});
right = new TransformManyBlock<T2, Tuple<T1, T2>>(r =>
{
lock(lockObject)
{
rightReceived.Add(r);
// Pair this input up with all received alternatives
return leftReceived.Select(l => Tuple.Create(l, r));
}
});
Task.WhenAll(Left.Completion, Right.Completion).ContinueWith(_ => {
// TODO: Respect propagate completion linkOptions. Defauting to propagation for now.
foreach (var target in targets)
{
target.Complete();
}
});
}
private TaskCompletionSource<int> completion = new TaskCompletionSource<int>();
public Task Completion => completion.Task;
public void Complete() { throw new NotImplementedException(); }
public void Fault(Exception exception) { throw new NotImplementedException(); }
public IDisposable LinkTo(ITargetBlock<Tuple<T1, T2>> target, DataflowLinkOptions linkOptions)
{
left.LinkTo(target);
right.LinkTo(target);
targets.Add(target);
return null; // TODO: Return something proper to allow unlinking
}
public void ReleaseReservation(DataflowMessageHeader messageHeader, ITargetBlock<Tuple<T1, T2>> target)
{
throw new NotImplementedException();
}
public bool ReserveMessage(DataflowMessageHeader messageHeader, ITargetBlock<Tuple<T1, T2>> target)
{
throw new NotImplementedException();
}
public Tuple<T1, T2> ConsumeMessage(DataflowMessageHeader messageHeader, ITargetBlock<Tuple<T1, T2>> target, out bool messageConsumed)
{
throw new NotImplementedException();
}
}
I'm experiencing the following (probably related) issues:
It is non-deterministic. The test fails in different ways.
It appears (from adding in logging, and also since I get anywhere from 3 to 6 output messages) that the Complete call to the two inputs is causing messages to not be processed, though my understanding is that it should allow all queues to drain first. (And if this is not the case, then I don't know how to write the test correctly.)
It's quite possible my locking scheme is wrong/suboptimal, though my goal was to have something big and coarse that worked before trying to fix.
My experiments with individual TransformManyBlocks has failed to come up with interesting surprising, and I can't figure out what's different in this case.
As suspected, this was related to completeness propagation. Here is a working version, including proper link disposable and respecting link options:
// A block that remembers every message it receives on two channels, and pairs every message on a channel to every message on the other channel
public class CartesianProductBlock<T1, T2> : ISourceBlock<Tuple<T1, T2>>
{
private TransformManyBlock<T1, Tuple<T1, T2>> left;
private TransformManyBlock<T2, Tuple<T1, T2>> right;
private List<T1> leftReceived = new List<T1>();
private List<T2> rightReceived = new List<T2>();
private List<ITargetBlock<Tuple<T1, T2>>> targets = new List<ITargetBlock<Tuple<T1, T2>>>();
private object lockObject = new object();
public ITargetBlock<T1> Left { get { return left; } }
public ITargetBlock<T2> Right { get { return right; } }
public CartesianProductBlock()
{
left = new TransformManyBlock<T1, Tuple<T1, T2>>(l =>
{
lock (lockObject)
{
leftReceived.Add(l);
return rightReceived.Select(r => Tuple.Create(l, r)).ToList();
}
});
right = new TransformManyBlock<T2, Tuple<T1, T2>>(r =>
{
lock(lockObject)
{
rightReceived.Add(r);
return leftReceived.Select(l => Tuple.Create(l, r)).ToList();
}
});
Task.WhenAll(Left.Completion, Right.Completion).ContinueWith(_ => {
completion.SetResult(VoidResult.Instance);
});
}
private TaskCompletionSource<VoidResult> completion = new TaskCompletionSource<VoidResult>();
public Task Completion => completion.Task;
public void Complete() {
Left.Complete();
Right.Complete();
}
public void Fault(Exception exception) { throw new NotImplementedException(); }
public IDisposable LinkTo(ITargetBlock<Tuple<T1, T2>> target, DataflowLinkOptions linkOptions)
{
var leftLink = left.LinkTo(target);
var rightLink = right.LinkTo(target);
var link = new Link(leftLink, rightLink);
Task task = Task.FromResult(0);
if (linkOptions.PropagateCompletion)
{
task = Task.WhenAny(Task.WhenAll(Left.Completion, Right.Completion), link.Completion).ContinueWith(_ =>
{
// If the link has been disposed of, we should not longer propagate completeness.
if (!link.Completion.IsCompleted)
{
target.Complete();
}
});
}
return link;
}
public void ReleaseReservation(DataflowMessageHeader messageHeader, ITargetBlock<Tuple<T1, T2>> target)
{
throw new NotImplementedException();
}
public bool ReserveMessage(DataflowMessageHeader messageHeader, ITargetBlock<Tuple<T1, T2>> target)
{
throw new NotImplementedException();
}
public Tuple<T1, T2> ConsumeMessage(DataflowMessageHeader messageHeader, ITargetBlock<Tuple<T1, T2>> target, out bool messageConsumed)
{
throw new NotImplementedException();
}
private class Link : IDisposable
{
private IDisposable leftLink;
private IDisposable rightLink;
public Link(IDisposable leftLink, IDisposable rightLink)
{
this.leftLink = leftLink;
this.rightLink = rightLink;
}
private TaskCompletionSource<VoidResult> completionSource = new TaskCompletionSource<VoidResult>();
public Task Completion { get { return completionSource.Task; } }
public void Dispose()
{
leftLink.Dispose();
rightLink.Dispose();
completionSource.SetResult(VoidResult.Instance);
}
}
private class VoidResult
{
public static VoidResult instance = new VoidResult();
public static VoidResult Instance { get { return instance; } }
protected VoidResult() { }
}
}

MongoDB C# GetById using Find

public abstract class GenericRepository<T> : IDisposable, IGenericRepository<T> where T : class
{
protected SphereTripMongoDbContext SphereTripMongoDbContext;
public IMongoCollection<T> MongoCollection { get; set; }
protected GenericRepository(SphereTripMongoDbContext sphereTripMongoDbContext)
{
SphereTripMongoDbContext = sphereTripMongoDbContext;
MongoCollection =
SphereTripMongoDbContext.MongoDatabase.GetCollection<T>(typeof(T).Name);
}
public void Dispose()
{
throw new NotImplementedException();
}
public T GetById(string id)
{
var entity = MongoCollection**.Find(t => t.Id == id)**;
return entity;
}
}
I am trying write a generic abstract repository class for MongoDb. Since I am using Generic type in the base class, the "Id" is not visible when I am finding the document using Find method. Not sure how to fix the issue.
Any help would be appreciated.
You can use Find without using a typed lambda expression with Builders:
var item = await collection
.Find(Builders<ItemClass>.Filter.Eq("_id", id))
.FirstOrDefaultAsync();
However, a more robust solution would be to use some interface that gives you what you need (i.e. ID) and making sure GenericRepository only works with these types:
interface IIdentifiable
{
string Id { get; }
}
class GenericRepository <T> : ... where T : IIdentifiable
{
// ...
}
I built a method like this:
public ValueTask<T> GetAsync<T>(IQueryable<T> source, object[] keyValues, CancellationToken cancellationToken = default)
where T : class
{
if (source == null)
{
throw new ArgumentNullException(nameof(source));
}
if (keyValues == default)
{
throw new ArgumentNullException(nameof(keyValues));
}
if (keyValues.Length != 1)
{
throw new ArgumentException("Key values must contain exactly one key value", nameof(keyValues));
}
var type = typeof(T);
var classMap = BsonClassMap.LookupClassMap(type);
if (classMap == default)
{
throw new InvalidOperationException($"Class map not found for '{type.Name}'");
}
var id = classMap.IdMemberMap;
if (id == default)
{
throw new InvalidOperationException($"Id member not found for '{type.Name}'");
}
var filter = Builders<T>.Filter.Eq(id.ElementName, keyValues[0]);
var collection = Database.GetCollection<T>(type.Name);
async ValueTask<T> GetAsync()
{
var cursor = await collection.FindAsync<T>(filter, default, cancellationToken).ConfigureAwait(false);
return await cursor.SingleOrDefaultAsync(cancellationToken).ConfigureAwait(false);
}
return GetAsync();
}

populate IDataReader without reflection using any (specified) object Collection

I struggling to get something done when it suppose to be very easy to implement
say I have a car object
public class tblCarObj
{
public String Model;
public Int32 Year;
}
within these lines there is an implementation of reflection that is based on the fact that the object members are unknown (is this the only reason?)
which is nice for a generic functionality, but I am sacrificing functionality for performance if needed. and I wonder why both Links (see below) uses this approach.
can I simply use MyCarReader<tblCarObj>
instead of
//for this instance.
private IEnumerator<T> enumerator = null;
//List of all public fields in <T>
private List<FieldInfo> fields = new List<FieldInfo>();
public MyGenericDataReader(IEnumerable<T> enumerator)
{
this.enumerator = enumerator.GetEnumerator();
//Find the enumerator of all public fields
foreach (FieldInfo fieldinfo in typeof(T).GetFields(
BindingFlags.Instance |
BindingFlags.Public))
{
fields.Add(fieldinfo);
}
}
is there a way to implement this method without the use of reflection ?
background
the task Is to simply upload a collection of objects into database table,
List<tblCarObj>
where:
DataBase Table is Ready
DataBase Table matches the DataObject members
both are known at compile time.
and directly use it on SQLBulckCopy instead of a more complex object such as DataTable
MyGenericDataReader.cs
GenericListDataReader.cs
You are asking the runtime and the compiler to do something that it cannot do without reflecting over the type.
The information that allows the CLR and the compiler to know what properties are on the type are in the type's metadata. Reflection is the mechanism that queries metadata and serves it up in a meaningful way so that we can use it to discover the shape and content of a type. Without it, a type is essentially a named black box.
You could do what you want, but you'd have to code it by hand. The reason the libraries out there exist is because reflection makes it possible for you to avoid that extra work.
needs further test to try
eliminate unnecessary mappings Update: tested ok
(gain extra performance when MyTestObject properties/fields are same as columns)
if making it generic will effect it only slightly the use it with T
using :
public string sqlCon ="data source=(local);Initial Catalog=XXX;Integrated Security=True";
public SqlCommand Cmd;
public SqlConnection Conn;
public SqlDataReader Drdr;
public Form1()
{
InitializeComponent();
this.Conn = new SqlConnection(this.sqlCon);
this.Cmd = new SqlCommand("select * from [tblTestBC]", this.Conn);
useBulkCopy();
}
void justread()
{
this.Cmd.Connection.Open();
this.Drdr = this.Cmd.ExecuteReader();
if (this.Drdr.HasRows)
while (this.Drdr.Read())
{
}
this.Cmd.Connection.Close();
}
void useBulkCopy()
{
var bulkCopy = new SqlBulkCopy(this.Cmd.Connection);
bulkCopy.DestinationTableName = "tblTestBC";
//bulkCopy.ColumnMappings.Add("age", "age");
//bulkCopy.ColumnMappings.Add("name", "name");
this.Cmd.Connection.Open();
try
{
using (var dataReader = new mySimpleDataReader())
{
bulkCopy.WriteToServer(dataReader);
}
this.Cmd.Connection.Close();
}
catch (Exception e)
{
}
}
GenericIdataReader
namespace GenericIdataReader
{
public class MyTestObject
{
public int age;
public string name;
}
public class mySimpleDataReader : IDataReader
{
private IEnumerator<MyTestObject> enumerator = null;
public List<MyTestObject> prpLst { get; set; }
List<MyTestObject> lst()
{
var rt = new List<MyTestObject>(5);
for (int i = 0; i < rt.Capacity; i++)
{
var tmp = new MyTestObject { age = i, name = "MyTestObject->"+i };
rt.Add(tmp);
}
return rt;
}
public mySimpleDataReader()
{
this.prpLst = this.lst();
this.enumerator = this.prpLst.GetEnumerator();
}
public void Close()
{
throw new NotImplementedException();
}
public int Depth
{
get { throw new NotImplementedException(); }
}
public DataTable GetSchemaTable()
{
throw new NotImplementedException();
}
public bool IsClosed
{
get { throw new NotImplementedException(); }
}
public bool NextResult()
{
throw new NotImplementedException();
}
public bool Read()
{
return enumerator.MoveNext();
}
public int RecordsAffected
{
get { throw new NotImplementedException(); }
}
public void Dispose()
{
this.enumerator.Dispose();
}
public int FieldCount
{
get { return 2; }// must be setted
}
public bool GetBoolean(int i)
{
throw new NotImplementedException();
}
public byte GetByte(int i)
{
throw new NotImplementedException();
}
public long GetBytes(int i, long fieldOffset, byte[] buffer, int bufferoffset, int length)
{
throw new NotImplementedException();
}
public char GetChar(int i)
{
throw new NotImplementedException();
}
public long GetChars(int i, long fieldoffset, char[] buffer, int bufferoffset, int length)
{
throw new NotImplementedException();
}
public IDataReader GetData(int i)
{
throw new NotImplementedException();
}
public string GetDataTypeName(int i)
{
throw new NotImplementedException();
}
public DateTime GetDateTime(int i)
{
throw new NotImplementedException();
}
public decimal GetDecimal(int i)
{
throw new NotImplementedException();
}
public double GetDouble(int i)
{
throw new NotImplementedException();
}
public Type GetFieldType(int i)
{
throw new NotImplementedException();
}
public float GetFloat(int i)
{
throw new NotImplementedException();
}
public Guid GetGuid(int i)
{
throw new NotImplementedException();
}
public short GetInt16(int i)
{
throw new NotImplementedException();
}
public int GetInt32(int i)
{
throw new NotImplementedException();
}
public long GetInt64(int i)
{
throw new NotImplementedException();
}
public string GetName(int i)
{
throw new NotImplementedException();
}
public int GetOrdinal(string name)
{
throw new NotImplementedException();
}
public string GetString(int i)
{
throw new NotImplementedException();
}
public object GetValue(int i) // this is where data is being pooled
{
if (i > 0) return enumerator.Current.name;
// so need to create an object that will hold numeric index or simply change
//this to return an indexed object instead of an enumerator according to parmeter i value
return enumerator.Current.age;
}
public int GetValues(object[] values)
{
throw new NotImplementedException();
}
public bool IsDBNull(int i)
{
throw new NotImplementedException();
}
public object this[string name]
{
get { throw new NotImplementedException(); }
}
public object this[int i]
{
get { throw new NotImplementedException(); }
}
}
}

Categories

Resources