Trying to create an Add extension method for an IDbSet - c#

I'm trying to create an Extension method for my DBContext (db) and one of the IDbSets. I would like to be able to call the extension like this:
db.UploadedFile.AddFile
(
SessionUser.ProfileId,
model.UploadingFile.FileName,
serverPath,
model.ProjectSubmissionId
);
This seems to work, but I would like to later, after a db.SaveChanges(), get the Primary Key ID of the added value.
This is what I have so far:
public static class UploadedFileExtensions
{
public static bool AddFile
(
this IDbSet<UploadedFile> files,
Guid uploadedByProfileId,
string fileName,
string filePath,
Guid? associatedWithId
)
{
var newFile = new UploadedFile
{
UploadedByProfileId = uploadedByProfileId,
FileName = fileName,
FilePath = filePath,
FileExtension = Path.GetExtension(fileName),
Submitted = DateTime.Now,
Modified = DateTime.Now,
IsActive = true
};
if (associatedWithId != null) newFile.AssociatedWithId = associatedWithId;
return files.AddFile(newFile);
//return true;
}
public static bool AddFile(this IDbSet<UploadedFile> files, UploadedFile file)
{
files.Add(file);
return true;
}
}

Depending on your how your db context code is setup, you may be able to do something like this:
private int GetMaxPrimaryKeyID()
{
return MyRepository.Items.Select(o => o.ID).DefaultIfEmpty().Max();
}
Where Items is:
IEnumerable<T> Items { get; }

Assuming this is an identity ID, I don't think you can get it before it is committed to the database.
I would wrap or inherit from the context (unit of work pattern is good for this) and override the SaveChanges method with your own.
As soon as the data is committed you can see what ID value has been assigned.
Something like:
public override void SaveChanges()
{
UploadedFile[] newFiles = base.ChangeTracker.Entries<UploadedFile>()
.Where(x => x.State == EntityState.Added)
.Select(x => x.Entity)
.ToArray()
base.SaveChanges();
//id values should be available to you here in the newFiles array
}
EDIT:
On reflection, there isn't actually any need for you to override anything here. You could just use something like the above code example with your context object directly. All you really need to do is use the ChangeTracker property to inspect your entities after the commit (but before you dispose the context).

Related

Detect or pass calling method in Entity Framework 6 and Entity Framework Core via IDbCommandInterceptor

I'm interested in capturing the name of the method that initiated an Entity Framework query. I will be using this functionality in both Entity Framework 6 and Entity Framework Core
Consider the following class:
namespace MyCompany.MyApp
{
public class MyDataClass
{
// ... snip ...
public Person GetPerson(long id)
{
return _dbContext
.Person
.FirstOrDefault(p => p.Id == id);
}
}
}
And the following interceptor:
public class EFCommandInterceptor : IDbCommandInterceptor
{
// ... snip other interface methods ...
public void ReaderExecuted(System.Data.Common.DbCommand command, DbCommandInterceptionContext<System.Data.Common.DbDataReader> interceptionContext)
{
var callingMehtod = GetCallingMethod() // <--- I'd like this to return the string "MyCompany.MyApp.MyDataClass.GetPerson"
}
}
Is there any way to determine what method initiated the query that resulted in the interceptor being called?
As I'm going to be using this functionality for tracing and performance analysis, I can't send and record the entire Environment.StackTrace for every query running through the system.
I also considered using the StackTrace class in conjunction with stackTrace.GetFrames() to try to analyze the call stack to find the actual method that initiated the query, but it's not clear how to do this reliably without relying on some kind of namespace convention that universally identifies a data access class.
Another approach that would be acceptable might look something like:
namespace MyCompany.MyApp
{
public class MyDataClass
{
// ... snip ...
public Person GetPerson(long id)
{
return _dbContext
.Person
.WithExtraInterceptorContext(new { CallingMethod = "MyCompany.MyApp.MyDataClass.GetPerson"}) // <--- Can something like this be done?
.FirstOrDefault(p => p.Id == id);
}
}
}
The intention with the above example would be able to later retrieve the extra context via the DbCommandInterceptionContext inside IDbCommandInterceptor
Ultimately, the problem I'm trying to solve is to get the fully qualified name of the method that initiated a query without having a full call stack.
With EF Core you can use TagWith
var result = db.Albums
.TagWith(MethodBase.GetCurrentMethod().Name)
.ToList();
You could simply register the information before running the query, something like:
public Person GetPerson(long id)
{
EfDebug.SetCurrentMethodName();
return _dbContext
.Person
.WithExtraInterceptorContext(new { CallingMethod = "MyCompany.MyApp.MyDataClass.GetPerson"}) // <--- Can something like this be done?
.FirstOrDefault(p => p.Id == id);
}
. . .
static class EfDebugExtensions
{
public static AsyncLocal<string> CurrentMethodName = new AsyncLocal<string>();
public static void SetCurrentMethodName([CallerMemberName] string caller = "")
{
CurrentMethodName.Value = caller;
}
public static TSource FirstOrDefault<TSource>(this IEnumerable<TSource> source,[CallerMemberName] string caller = "")
{
CurrentMethodName.Value = caller;
var rv = Enumerable.FirstOrDefault(source);
return rv;
}
public static List<TSource> ToList<TSource>(this IEnumerable<TSource> source, [CallerMemberName] string caller = "")
{
CurrentMethodName.Value = caller;
var rv = Enumerable.ToList(source);
return rv;
}
}

IQueryable returning nulls, IEnumerable does not

I have an extension method that needs to return some DTOs based on an IQueryable it receives. Tinkering with the code today (I added a new int property) I realized the method was not working properly anymore, returning part of the DTOs list as nulls.
I wanted to see if maybe a property I added was the problem so I trimmed the method to how it looks below and it stills returns some nulls.
public static IQueryable<OperationDto> SelectDto(this IQueryable<Operation> operations)
{
return operations.Select(op=> new OperationDto
{
Id = op.Id
});
}
I changed the method to
public static IEnumerable<OperationDto> SelectDto(this IQueryable<Operation> operations)
{
return operations.AsEnumerable().Select(op=> new OperationDto
{
Id = op.Id
});
}
and it works again without changing anything else.
I don't understand why the IQueryable<OperationDto> suddenly stopped working (maybe the new property) and why the IEnumerable variant works.
EDIT
The extension method is called in the following repository method:
original IQueryable version:
public IQueryable<OperationDto> FindOperationDtos(OperationModelFilter modelFilter)
{
IQueryable<Operation> filteredData = DbSet.AsQueryable();
if (modelFilter.UserId.HasValue)
filteredData = filteredData.Where(o => o.User.Id == modelFilter.UserId.Value);
...
return filteredData.Distinct().SelectDto();
}
IEnumerable version:
public IQueryable<OperationDto> FindOperationDtos(OperationModelFilter modelFilter)
{
IQueryable<Operation> filteredData = DbSet.AsQueryable();
if (modelFilter.UserId.HasValue)
filteredData = filteredData.Where(o => o.User.Id == modelFilter.UserId.Value);
...
return filteredData.Distinct().SelectDto();
}

Find a generic DbSet in a DbContext dynamically

I know this question has already been asked but I couldn't find an answer that satisfied me. What I am trying to do is to retrieve a particular DbSet<T> based on its type's name.
I have the following :
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("MyDllAssemblyName")]
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("MyCallingAssemblyName")]
class MyDbContext : DbContext {
public DbSet<ModelA> A { get; set; }
public DbSet<ModelB> B { get; set; }
public dynamic GetByName_SwitchTest(string name) {
switch (name) {
case "A": return A;
case "B": return B;
}
}
public dynamic GetByName_ReflectionTest(string fullname)
{
Type targetType = Type.GetType(fullname);
var model = GetType()
.GetRuntimeProperties()
.Where(o =>
o.PropertyType.IsGenericType &&
o.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>) &&
o.PropertyType.GenericTypeArguments.Contains(targetType))
.FirstOrDefault();
if (null != model)
return model.GetValue(this);
return null;
}
}
I have no trouble getting the type itself whether it is via a simple switch or reflection. I need however to return the type as a dynamic since I do not know what DbSet type it will be.
Then somewhere else in the same assembly, I use it this way :
// MyDbContext MyDbContextInstance..
var model = MyDbContextInstance.GetByName_SwitchTest("A");
var record1 = model.FirstOrDefault(); // It crashes here with RunTimeBinderException
At this point model contains an instance of a InternalDbSet<ModelA> type. From there, any use I do with the model object I get a RunTimeBinderException :
'Microsoft.Data.Entity.Internal.InternalDbSet' does not contain a definition for 'FirstOrDefault'
Investigating on the web, I found a blog post explaining that (dixit his blog) :
the reason the call to FirstOrDefault() fails is that the type
information of model is not available at runtime. The reason it's not
available is because anonymous types are not public. When the method
is returning an instance of that anonymous type, it's returning a
System.Object which references an instance of an anonymous type - a
type whose info isn't available to the main program.
And then he points that a solution :
The solution is actually quite simple. All we have to do is open up
AssemplyInfo.cs of the ClassLibrary1 project and add the following
line to it: [assembly:InternalsVisibleTo("assembly-name")]
I did try this solution on my code but it doesn't work. For info I have an asp.net 5 solution with two assemblies running on dnx dotnet46. An app and a dll containing all my models and DbContext. All the concerned calls I do are located on the dll though.
Does this solution have any chance to work ?
Am I missing something ?
Any pointers would be greatly appreciated ?
Thanks in advance
[EDIT]
I have tried to return IQueryable<dynamic> rather than dynamic and I could do the basic query model.FirstOrDefault(); but above all I'd like to be able to filter on a field too :
var record = model.FirstOrDefault(item => item.MyProperty == true);
So how did I do it when I am not aware of <T> during compile time.
First need to get the type as DbContext.Set method returns a non-generic DbSet instance for access to entities of the given type in the context and the underlying store.
public virtual DbSet Set(Type entityType)
Note here argument is the type of entity for which a set should be returned.And set for the given entity type is the return value.
var type = Assembly.GetExecutingAssembly().GetTypes().FirstOrDefault(t => t.Name == <Pass your table name>);
now once I have this type
if(type != null)
{
DbSet context = context.Set(type);
}
Or a one liner would be
DbSet mySet = context.Set(Type.GetType("<Your Entity Name>"));
*Disclaimer: This response doesn't give a stricto sensu answer to my question. It is rather a different approach to resolve my own problem. I am aware this is a specific example for a given situation that will not work for everyone. I am posting this approach in the hope it helps someone but will not mark it as the answer as I am still hoping for a real solution.
To start with, let's accept the fact that the only useful information we can get out of the current code is whether a record exists or not.. Any attempt of a dynamic queries after that would give the RuntimeBinderException.
Then let's continue with another fact; DbContext.Add(object) and DbContext.Update(object) are not template based so we can use them to save our models ( Instead of db.A.Add() or db.A.Update() )
In my own situation, no more is required to work out a procedure
Define models a little differently
To start with, I need a field that is retrievable across all my models which should obviously be a way to identify a unique record.
// IModel give me a reliable common field to all my models ( Fits my DB design maybe not yours though )
interface IModel { Guid Id { get; set; } }
// ModelA inherit IModel so that I always have access to an 'Id'
class ModelA : IModel {
public Guid Id { get; set; }
public int OtherField { get; set; }
}
// ModelB inherit IModel so that I always have access to an 'Id'
class ModelB : IModel {
public Guid Id { get; set; }
public string WhateverOtherField { get; set; }
}
Re-purpose the dynamic queries a bit to do something we know works
I haven't found a way to do smart query dynamically, so instead I know I can reliably identify a record and know if it exists or not.
class MyDbContext : DbContext {
public DbSet<ModelA> A { get; set; }
public DbSet<ModelB> B { get; set; }
// In my case, this method help me to know the next action I need to do
// The switch/case option is not pretty but might have better performance
// than Reflection. Anyhow, this is one's choice.
public bool HasRecord_SwitchTest(string name) {
switch (name) {
case "A": return A.AsNoTracking().Any(o => o.Id == id);
case "B": return B.AsNoTracking().Any(o => o.Id == id);
}
return false;
}
// In my case, this method help me to know the next action I need to do
public bool HasRecord_ReflectionTest(string fullname)
{
Type targetType = Type.GetType(fullname);
var model = GetType()
.GetRuntimeProperties()
.Where(o =>
o.PropertyType.IsGenericType &&
o.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>) &&
o.PropertyType.GenericTypeArguments.Contains(targetType))
.FirstOrDefault();
if (null != model)
return (bool)model.GetValue(this).AsNoTracking().Any(o => o.Id == id);
return false;
}
// Update and save immediately - simplified for example
public async Task<bool> UpdateDynamic(object content)
{
EntityEntry entry = Update(content, GraphBehavior.SingleObject);
return 1 == await SaveChangesAsync(true);
}
// Insert and save immediately - simplified for example
public async Task<bool> InsertDynamic(object content)
{
EntityEntry entry = Add(content, GraphBehavior.SingleObject);
return 1 == await SaveChangesAsync(true);
}
}
A little bit of plumbing to give a sense to my situation
Next, what I needed to do with that dynamic queries was a way to replicate data from a server down to my client. ( I have omitted a big chunk of the architecture to simplify this example )
class ReplicationItem
{
public ReplicationAction Action { get; set; } // = Create, Update, Delete
public string ModelName { get; set; } // Model name
public Guid Id { get; set; } // Unique identified across whole platform
}
Connecting the bits.
Now, here's the routine that connects the bits
public async void ProcessReplicationItem(ReplicationItem replicationItem)
{
using (var db = new MyDbContext())
{
// Custom method that attempts to get remote value by Model Name and Id
// This is where I get the strongly typed object
var remoteRecord = await TryGetAsync(replicationItem.ModelName, replicationItem.Id);
bool hasRemoteRecord = remoteRecord.Content != null;
// Get to know if a local copy of this record exists.
bool hasLocalRecord = db.HasRecord_ReflectionTest(replicationItem.ModelName, replicationItem.Id);
// Ensure response is valid whether it is a successful get or error is meaningful ( ie. NotFound )
if (remoteRecord.Success || remoteRecord.ResponseCode == System.Net.HttpStatusCode.NotFound)
{
switch (replicationItem.Action)
{
case ReplicationAction.Create:
{
if (hasRemoteRecord)
{
if (hasLocalRecord)
await db.UpdateDynamic(remoteRecord.Content);
else
await db.InsertDynamic(remoteRecord.Content);
}
// else - Do nothing
break;
}
case ReplicationAction.Update:
[etc...]
}
}
}
}
// Get record from server and with 'response.Content.ReadAsAsync' type it
// already to the appropriately
public static async Task<Response> TryGetAsync(ReplicationItem item)
{
if (string.IsNullOrWhiteSpace(item.ModelName))
{
throw new ArgumentException("Missing a model name", nameof(item));
}
if (item.Id == Guid.Empty)
{
throw new ArgumentException("Missing a primary key", nameof(item));
}
// This black box, just extrapolate a uri based on model name and id
// typically "api/ModelA/{the-guid}"
string uri = GetPathFromMessage(item);
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:12345");
HttpResponseMessage response = await client.GetAsync(uri);
if (response.IsSuccessStatusCode)
{
return new Response()
{
Content = await response.Content.ReadAsAsync(Type.GetType(item.ModelName)),
Success = true,
ResponseCode = response.StatusCode
};
}
else
{
return new Response()
{
Success = false,
ResponseCode = response.StatusCode
};
}
}
}
public class Response
{
public object Content { get; set; }
public bool Success { get; set; }
public HttpStatusCode ResponseCode { get; set; }
}
ps: I am still interested in a real answer, so please keep posting for other answer if you have a real one to share.
You could use this to get the DBSet for a specific type:
public object GetByType(DbContextcontext, Type type) {
var methode = _context.GetType().GetMethod("Set", types: Type.EmptyTypes);
if (methode == null) {
return null;
}
return methode.MakeGenericMethod(type).Invoke(_context, null);
}

How to get all PageTemplate that contains a specific component?

I have a pagetemplate called ArticlePageTemplate. This ArticlePageTemplate contains a component called Articles. The Article component has data field called Header, SubHeader, Date, and Content.
Presentation details of ArticlesPageTemplate
Using Lucene in sitecore 8. How do I get all ArticlePageTemplate that contains an Article component with the value of "News" in subheader of Article component.
Sitecore structure:
Here is my code
public class LuceneSearchService : ILuceneSearchService, IDisposable
{
bool disposed = false;
// Instantiate a SafeHandle instance.
SafeHandle handle = new SafeFileHandle(IntPtr.Zero, true);
private IProviderSearchContext _SearchContext;
private ISearchIndex _Index;
private string _IndexName = "sitecore_web_index";
public string IndexName
{
get { return _IndexName; }
set { _IndexName = value; }
}
public LuceneSearchService()
{
_Index = ContentSearchManager.GetIndex(this.IndexName);
_SearchContext = _Index.CreateSearchContext();
}
public IQueryable<SearchResultItem> PerformSearch(string searchPath)
{
var searchQuery = _SearchContext.GetQueryable<SearchResultItem>()
.Where(i => i.Path.StartsWith(searchPath));
return searchQuery;
}
public IQueryable<SearchResultItem> PerformSearch(string searchPath, string templateName)
{
var searchQuery = PerformSearch(searchPath).Where(x => x.TemplateName == templateName);
return searchQuery;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
// Protected implementation of Dispose pattern.
protected virtual void Dispose(bool disposing)
{
if (disposed)
return;
if (disposing)
{
handle.Dispose();
_SearchContext.Dispose();
_Index.Dispose();
}
// Free any unmanaged objects here.
//
disposed = true;
}
}
Code implementation
public void SearchArticles()
{
var articles = SearchService.PerformSearch("/sitecore/content", "ArticlePageTemplate").ToList();
foreach (var article in articles){
var articleName = article;
}
}
I can get all the ArticlesPageTemplate using the code above but I can't filter it to get only those ArticlePageTemplate that contains an Article component in which Article component's subheader is "news".
I do not know how to get the articles component of ArticlePageTemplate so I could add them to my search query in lucene.
Any advice is appreciated.
Note:
The datasource of ArticlesComponent may change so it is not always a child of ArticlesPageTemplate.
UPDATE (09/11/2015)
I tried what Richard suggested but it does not work.
var articles = SearchService
.PerformSearch<ArticleResultItem>("/sitecore/content", "ArticlePageTemplate")
.Where(item => item.SubHeader == "News").ToList();
Using the above query does not yield any result but using the below query returns a result but it retrieves ArticleComponent instead of ArticlePageTemplate.
var articles = SearchService
.PerformSearch<ArticleResultItem>("/sitecore/content"
.Where(item => item.SubHeader == "News").ToList();
SOLUTION:
Computed fields works. See VisualizationField.cs from the link below:
https://gist.github.com/techphoria414/7604814
I can see two approaches that you can go with here:
Build a 'computed field' for the Article component subheader field. You would set this up so that the index is configured to look for this component on an item with the ArticlePageTemplate template and compute the subheader value into the index. This essentially makes the index think that the subheader field is actually a field on ArticlePageTemplate item. Then you can do a regular search on the index.
Find the reference pages for a component. In this scenario, you search the index for the components (not ArticlePageTemplate), and if you can depend on your folder structure and you do not have re-use across pages, you can then just grab the parent page of any search result. (item.Parent.Parent, in your example structure)
UPDATE: Some links for computed fields:
http://www.sitecore.net/learn/blogs/technical-blogs/john-west-sitecore-blog/posts/2013/03/sitecore-7-computed-index-fields.aspx
https://gist.github.com/techphoria414/7604814
You would need to create a custom class that allows you to filter on your field, you can base it off the SearchResultItem class.
So something like:
public class ArticleResultItem : SearchResultItem
{
[IndexField("SubHeader")]
public string SubHeader { get;set; }
}
Then if you change your PerformSearch method to take a generic based of SearchResultItem, you can filter using that field in your calling method.
public IQueryable<T> PerformSearch<T>(string searchPath)
where T: SearchResultItem
{
var searchQuery = _SearchContext.GetQueryable<T>()
.Where(i => i.Path.StartsWith(searchPath));
return searchQuery;
}
public IQueryable<T> PerformSearch<T>(string searchPath, string templateName)
where T: SearchResultItem
{
var searchQuery = PerformSearch<T>(searchPath).Where(x => x.TemplateName == templateName);
return searchQuery;
}
And then you would call it like this:
public void SearchArticles()
{
var articles = SearchService
.PerformSearch<ArticleResultItem>("/sitecore/content", "ArticlePageTemplate")
.Where(item => item.SubHeader == "News").ToList();
foreach (var article in articles){
var articleName = article;
// Do something with the list here....
}
}

LINQ 2 SQL: Partial Classes

I need to set the ConnectionString for my DataContext's based on an AppSetting. I am trying to do this by creating a Partial Class for each DataContext. The below is what I have so far and I am wondering if I am overlooking something?
Specifically, am I dealing with my DataContext's correctly(disposing, staleness, etc)?
Doing it this way will I have issues with Updates and Inserts? Is the file BLLAspnetdb.cs useful or neccessary in the least or should all of that be in the generated partial class AspnetdbDataContext file?
In short, is this an acceptable structure or will this cause me issues as I elaborate it?
dbml File Name = Aspnetdb.dbml
Partial Class File Name = Aspnetdb.cs
partial class AspnetdbDataContext
{
public static bool IsDisconnectedUser
{
get
{
return Convert.ToBoolean(ConfigurationManager.AppSettings["IsDisconnectedUser"]) == true;
}
}
public static AspnetdbDataContext New
{
get
{
var cs = IsDisconnectedUser ? Settings.Default.Central_aspnetdbConnectionString : Settings.Default.aspnetdbConnectionString;
return new AspnetdbDataContext(cs);
}
}
}
My Created File Name = BLLAspnetdb.cs
public class BLLAspnetdb
{
public static IList WorkerList(Guid userID)
{
var DB = AspnetdbDataContext.New;
var workers = from user in DB.tblDemographics
where user.UserID == userID
select new { user.FirstName, user.LastName, user.Phone };
IList theWorkers = workers.ToList();
return theWorkers;
}
public static String NurseName(Guid? userID)
{
var DB = AspnetdbDataContext.New;
var nurseName = from demographic in DB.tblDemographics
where demographic.UserID == userID
select demographic.FirstName +" " + demographic.LastName;
return nurseName.SingleOrDefault();
}
public static String SocialWorkerName(Guid? userID)
{
var DB = AspnetdbDataContext.New;
var swName = from demographic in DB.tblDemographics
where demographic.UserID == userID
select demographic.FirstName + " " + demographic.LastName;
return swName.SingleOrDefault();
}
}
see this previous question and the accepted answer for background on how I got to here...
switch-connectionstrings-between-local-and-remote-with-linq-to-sql
You should dispose of your context, since it is disposable. Consider wrapping the statements in a using block whenever you create a new context.
I propably would express the static "New" property as a "Create" method instead. It is not normal for properties to be creating new objects, so other developers that need to use the code might be surprised by the behavior.
Other than that, your approach will work. When you acquire your context, the logic to determine the connection string runs, and you will get a context constructed with the correct connection string.
I would not all of your methods and properties be static if I were you. It defies good OO design, and makes you very locked in to a specific implementation - however, I guess that is not in scope for the question.

Categories