I am fairly new to MVC, but after playing with it (MVC 3/Razor), I am hooked.
I have a few questions:
1) What is the best, or most widely used pattern to develop MVC apps in? Repository, DDD, UOW?
2) I am using the Entity Framework 4, so could some please explain to me or point me to a good source that will explain the Repository Pattern w/EF4? Doesn't EF4 take place as the business layer and the data access layer? Does the Repository Pattern even provide a benefit?
3) Also, one last question, could someone explain the whole relationship between the Controller, the Model and the View? I get the basics, but maybe a little more in depth of the correct way to use it. View Models - Say I have a view that displays customer info, and one that edits it, should I have a view model and an edit model, or can the be passed around?
4) Examples??
Thanks for the help up front,
$("Sam")
** EDIT **
Am I on the right track here:
Public Class HomeController
Inherits System.Web.Mvc.Controller
Function Index(ByVal id As Integer) As ActionResult
Return View(New HomeModel)
End Function
<HttpPost()> _
Function Index(ByVal Model As HomeModel) As ActionResult
Return View(Model)
End Function
End Class
Public Class HomeModel
Private _Repository As IRepository(Of Customer)
Public Property Customer As Customer
Public Sub New()
End Sub
Public Sub New(ByVal ID As Integer)
_Repository = New CustomerRepository
Customer = _Repository.GetByID(ID)
End Sub
End Class
Public Interface IRepository(Of T)
Function GetByID(ByVal ID As Integer) As T
Sub Add(ByVal Entity As T)
Sub Delete(ByVal Entity As T)
End Interface
Public Class CustomerRepository
Implements IRepository(Of Customer)
Public Sub Add(ByVal Entity As Customer) Implements IRepository(Of Customer).Add
End Sub
Public Sub Delete(ByVal Entity As Customer) Implements IRepository(Of Customer).Delete
End Sub
Public Function GetByID(ByVal ID As Integer) As Customer Implements IRepository(Of Customer).GetByID
Return New Customer With {.ID = ID, .FirstName = "Sam", .LastName = "Striano"}
End Function
End Class
Public Class Customer
Public Property ID As Integer
Public Property FirstName As String
Public Property LastName As String
End Class
I use generic repositories that get instantiated in a service class (using Dependency Injection with Ninject).
The service class essentially performs two functions:
It provides all the methods that the controller will consume.
It has a property called ViewModel, that essentially maps the data that the views need into a MyViewModel class.
The Controller consumes the service class. With this "pattern", your controllers look like:
namespace ES.eLearningFE.Areas.Courses.Controllers
{
public partial class CourseController : Controller
{
ICourseDisplayService service;
public CourseController(ICourseDisplayService service)
{
this.service = service;
}
public virtual ActionResult Display(int CourseId, int StepOrder, string PupilName, string TutorName)
{
service.CourseId = CourseId;
service.StepOrder = StepOrder;
service.PupilName = PupilName;
service.TutorName = TutorName;
if (Request.IsAjaxRequest())
{
return PartialView(service.ViewModel);
}
else
{
return View(service.ViewModel);
}
}
}
}
The ViewModel class only hold display data and no methods (except the odd really simple method to retrieve data from another property that is, for example a List<> object).
Works really well. An example of a service class:
namespace ES.eLearning.Domain.Services.Courses
{
public class SqlCourseDisplayService : ICourseDisplayService
{
DataContext db;
public SqlCourseDisplayService(DbDataContextFactory contextFactory)
{
db = contextFactory.Make();
CoursesRepository = new SqlRepository<Course>(db);
StepsRepository = new SqlRepository<CourseStep>(db);
StepLinksRepository = new SqlRepository<StepLink>(db);
UserCoursesRepository = new SqlRepository<UserCourse>(db);
CourseTutorsRepository = new SqlRepository<CourseTutor>(db);
UsersRepository = new SqlRepository<User>(db);
}
#region ICourseDisplayService Members
public ViewModels.CourseDisplayVM ViewModel
{
get
{
return new ViewModels.CourseDisplayVM
{
CourseId = this.CourseId,
CourseName = this.Course.Name,
Steps = this.Steps,
ActiveStepIndex = this.ActiveStepIndex,
CurrentStepIndex = this.CurrentStepIndex,
Pupil = new UserDto { UserId = this.PupilId, UserName = this.PupilName },
Tutors = this.GetTutors(this.CourseId),
Tutor = tutorName == null ? null : new UserDto { UserName = this.TutorName, UserId = this.TutorId}
};
}
}
#region Entities
int courseId;
public int CourseId
{
get
{
if (courseId == 0) throw new ApplicationException("Invalid Course Id!");
return courseId;
}
set
{
if (value == 0) throw new ApplicationException("Invalid Course Id!");
try
{
Course = (from c in CoursesRepository.Query where c.CourseId == value select c).First();
Steps = Course.CourseSteps.ToList();
courseId = value;
}
catch {throw new ApplicationException("No Course found for Course Id: " + value);}
}
}
public Data.Course Course { get; private set; }
public int StepOrder { get; set; }
public List<Data.CourseStep> Steps { get; private set; }
public int ActiveStepIndex
{
get
{
if (PupilName == null)
{
throw new ApplicationException("Pupil not set!");
}
if (CourseId == 0)
{
throw new ApplicationException("Course not set!");
}
try
{
var x = (from uc in UserCoursesRepository.Query where (uc.IdCourse == CourseId) && (uc.UserName == PupilName) select uc).First();
return x.ActiveStepIndex;
}
catch { throw new ApplicationException("Could not get Active Step!"); }
}
}
#endregion
#region Users
string tutorName;
public string TutorName
{
get
{
if (tutorName == null) throw new ApplicationException("Invalid call to get Tutor Name [Null Tutor Name]!");
return tutorName;
}
set
{
tutorName = value;
TutorId = (Guid)Membership.GetUser(tutorName).ProviderUserKey;
}
}
public Guid TutorId { get; set; }
string pupilName;
public string PupilName
{
get { return pupilName; }
set
{
pupilName = value;
PupilId = (Guid)Membership.GetUser(pupilName).ProviderUserKey;
}
}
public Guid PupilId { get; set; }
#endregion
#region Utility Properties
public int CurrentStepIndex { get; set; }
public int StepCount
{
get
{
return Steps == null ? 0 : Steps.Count();
}
}
#endregion
#region Private Utilities
private List<UserDto> GetTutors(int CourseId)
{
return (from ct in CourseTutorsRepository.Query join u in UsersRepository.Query
on ct.TutorName equals u.UserName
where (ct.CourseId == courseId)
select new UserDto { UserName = ct.TutorName, UserId = u.UserId }).ToList();
}
#endregion
#region Repositories
private IRepository<Course> CoursesRepository
{
get;
set;
}
private IRepository<CourseStep> StepsRepository
{
get;
set;
}
private IRepository<StepLink> StepLinksRepository
{
get;
set;
}
private IRepository<UserCourse> UserCoursesRepository
{
get;
set;
}
private IRepository<CourseTutor> CourseTutorsRepository
{
get;
set;
}
private IRepository<User> UsersRepository
{
get;
set;
}
#endregion
#endregion
}
}
May not be everyone's choice, but hey, it works for me... AND (more importantly) my clients and their users.
Edit
As requested in the comment below, the Repository that I use:
namespace ES.eLearning.Domain
{
public class SqlRepository<T> : IRepository<T> where T : class
{
DataContext db;
public SqlRepository(DataContext db)
{
this.db = db;
}
#region IRepository<T> Members
public IQueryable<T> Query
{
get { return db.GetTable<T>(); }
}
public List<T> FetchAll()
{
return Query.ToList();
}
public void Add(T entity)
{
db.GetTable<T>().InsertOnSubmit(entity);
}
public void Delete(T entity)
{
db.GetTable<T>().DeleteOnSubmit(entity);
}
public void Attach(T entity)
{
db.GetTable<T>().Attach(entity);
}
public void Save()
{
db.SubmitChanges();
}
#endregion
}
}
And the IRepository Interface:
namespace Wingspan.Web.Mvc
{
public interface IRepository<TEntity> where TEntity : class
{
List<TEntity> FetchAll();
IQueryable<TEntity> Query {get;}
void Add(TEntity entity);
void Delete(TEntity entity);
void Attach(TEntity entity);
void Save();
}
}
This should help you getting started. There are a lot of tutorials and videos available; for example:
Understanding Models, Views and Controllers
The ASP.NET MVC 2.0 basics and excellent introduction by Scott Hanselman. Personally one of my favorite speakers.
And also at www.asp.net; there are a few tutorials/examples to help you getting started. For example the Music Store sample
Unfortunately, I'm not so familiar with EF4/Repository pattern. But here's a blogpost about this pattern.
1) I would say that the repository pattern is the most widely used, then there is inversion of controll too.
2) I can't really point out the benefits with using a repository for entity framework other than that the controller should not know about how to acces data other then asking a repository. This makes it easy to switch it out sometime.
You can also eager load the data to make sure that the view don't call the database in every iteration of a foreach, for example a collection of users to display data from a child entity. You can probly do this anyway, but I feel that the repository is the right place to do it.
3) I can't tell you about the concept in a more in depth way, but I can tell some about viewmodels. In my opinion you should only use viewmodels if there is anything more then one entity you want to send to the view, for example a list of countries. You can alo use a viewmodel to "flatten" out very complex objects.
I would defiantly say the repository pattern is used a lot. This pattern can be used with Dependency Injection. Using Dependency Injection makes Unit Testing a breeze because you can snap different repositories to an abstract repoistory. Check out http://ninject.org/ for a simple to use Dependecy injector for .NET.
View Models should hold display data and transfer that data from the controller to the view. If you want to edit and display customer info, take a look at this
Related
I am trying to extend the AuditLog entity in ASPNETBOILETPLATE framework in order to add some new properties to it. I have tried to extend the AuditLog class (ExtendedAuditInfo) and implement a customised version of AuditStore Class (ExtendedAuditStore). However, I am not able to inject my new ExtendedAuditInfo in the constructor and receive two error messages regarding unmatching input parameters in the Constructor and SaveAsync method.
Class ExtendedAuditInfo:
public class ExtendedAuditInfo : AuditInfo
{
// Some properties
}
Class ExtendedAuditStore:
public class ExtendedAuditStore : AuditingStore
{
public ExtendedAuditStore(IRepository<ExtendedAuditInfo, long> auditLogRepository)
: base(auditLogRepository)
{
}
public override Task SaveAsync(ExtendedAuditInfo auditInfo)
{
if (!string.IsNullOrEmpty(auditInfo.Parameters) && auditInfo.Parameters != "{}")
{
var parameters = JsonConvert.DeserializeObject<AuditParameterInput>(auditInfo.Parameters);
if (parameters != null)
auditInfo.CustomData = parameters.Input.Id.ToString();
}
return base.SaveAsync(auditInfo);
}
}
The errors are:
cannot convert from 'Abp.Domain.Repositories.IRepository<SixB.Serafina.Auditing.ExtendedAuditInfo, long>' to 'Abp.Domain.Repositories.IRepository<Abp.Auditing.AuditLog, long>'
and
no suitable method found to override
The procedure above is based on the idea that I found Here
I found the solution based on the official document of How To Extend Existing Entities.
In order to extend the AuditLog class, inheritance must be used. Therefore a new class, let's say ExtendedAuditInfo needs to be inherited from AuditLog.
public class ExtendedAuditLog : AuditLog
{
public ExtendedAuditLog()
{
}
public ExtendedAuditLog(AuditInfo auditInfo)
{
this.BrowserInfo = auditInfo.BrowserInfo;
this.ClientIpAddress = auditInfo.ClientIpAddress;
this.ClientName = auditInfo.ClientName;
this.CustomData = auditInfo.CustomData;
this.Exception = auditInfo.Exception?.Message.ToString() + "";
this.ExecutionDuration = auditInfo.ExecutionDuration;
this.ExecutionTime = auditInfo.ExecutionTime;
this.ImpersonatorTenantId = auditInfo.ImpersonatorTenantId;
this.ImpersonatorUserId = auditInfo.ImpersonatorUserId;
this.MethodName = auditInfo.MethodName;
this.Parameters = auditInfo.Parameters;
this.ReturnValue = auditInfo.ReturnValue;
this.ServiceName = auditInfo.ServiceName;
this.TenantId = auditInfo.TenantId;
this.UserId = auditInfo.UserId;
}
//new properties
}
This class has to be added to the context and obviously, a new migration needs to be run in order to add the new properties.
public class ProjectDbContext : AbpZeroDbContext<Tenant, Role, User, ProjectDbContext >
{
/* Define a DbSet for each entity of the application */
public SerafinaDbContext(DbContextOptions<SerafinaDbContext> options)
: base(options)
{
}
public virtual DbSet<County> Counties { get; set; }
public virtual DbSet<Country> Countries { get; set; }
public virtual DbSet<Currency> Currencies { get; set; }
public virtual DbSet<OrganisationType> OrganisationTypes { get; set; }
public virtual DbSet<ExtendedAuditLog> ExtendedAuditLogs { get; set; }
}
Finally, in the ExtendedAuditStore class, IRepository<ExtendedAuditLog, long> _extendedAuditLogRepository has to be injected as a second parameter of the constructor and can be used to insert the extended entity.
public class ExtendedAuditStore : AuditingStore
{
IRepository<ExtendedAuditLog, long> _extendedAuditLogRepository;
public ExtendedAuditStore(
IRepository<AuditLog, long> auditLogRepository,
IRepository<ExtendedAuditLog, long> extendedAuditLogRepository
)
: base(auditLogRepository)
{
_extendedAuditLogRepository = extendedAuditLogRepository;
}
public override async Task SaveAsync(AuditInfo auditInfo)
{
if (auditInfo.Exception != null)
await base.SaveAsync(auditInfo);
var auditLog = new ExtendedAuditLog(auditInfo);
//new properties can be set here
await _extendedAuditLogRepository.InsertAsync(auditLog);
}
}
Also, instead of inheriting from AuditingStore, a new implementation for IAuditingStore can be created and injected into application services.
UPDATE:
Finally, all you need is to replace the default AuditingStore in StartUp class:
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddTransient<IAuditingStore, ExtendedAuditStore>();
}
First question here, I hope I'm doing it right.
I'm using Entity Framework Core 5.0 (Code First) with an onion architecture (data/repo/service/mvc) and so I have a service for each table (almost).
It's work well but now I need to manage (get, insert, update, delete) about 150 tables which all have the same structure (Id, name, order).
I have added each of them as Entity class and their DbSet too in my DbContext, but I don't want to make 150 services, I would like to have a generic one .
How can I bind it to my generic repository ?
public class Repository<T> : IRepository<T> where T : BaseEntity
{
private readonly ApplicationContext context;
private DbSet<T> entities;
private readonly RepositorySequence repoSequence;
private string typeName { get; set; }
public Repository(ApplicationContext context)
{
this.context = context;
entities = context.Set<T>();
this.repoSequence = new RepositorySequence(context);
this.typeName = typeof(T).Name;
}
public T Get(long plng_Id)
{
return entities.SingleOrDefault(s => s.Id == plng_Id);
}
[...]
}
In an ideal world, would like to have something like this :
public async Task Insert(dynamic pdyn_Entity)
{
Type DynamicType = Type.GetType(pdyn_Entity);
Repository<DynamicType> vobj_Repo = new Repository<DynamicType>(mobj_AppContext);
long Id = await vobj_Repo.InsertAsync(pdyn_Entity);
}
But I can try to get type from DbSet string Name too, I just managed to retrieve some data :
public IEnumerable<object> GetAll(string pstr_DbSetName)
{
return ((IEnumerable<BaseEntity>)typeof(ApplicationContext).GetProperty(pstr_DbSetName).GetValue(mobj_AppContext, null));
}
I've tried the following method (2.0 compatible apparently) to get the good DbSet, not working neither (no Query) : https://stackoverflow.com/a/48042166/10359024
What am I missing?
Thanks a lot for your help
Not sure why you need to get type?
You can use something like this.
Repository.cs
public class Repository<T> : IRepository<T> where T : BaseEntity
{
private readonly ApplicationContext context;
private DbSet<T> entities;
public Repository(ApplicationContext context)
{
this.context = context;
entities = context.Set<T>();
}
public List<T> Get()
=> entities.ToList();
public T Get(long plng_Id)
=> entities.Find(plng_Id);
public long Save(T obj)
{
if (obj.ID > 0)
entities.Update(obj);
else
entities.Add(obj);
return obj.ID;
}
public void Delete(T obj)
=> entities.Remove(obj);
}
Then you can use either one of these 2 options you want
Multiple repositories following your tables
UserRepository.cs
public class UserRepository : Repository<User> : IUserRepository
{
private readonly ApplicationContext context;
public UserRepository(ApplicationContext context)
{
this.context = context;
}
}
BaseService.cs
public class BaseService : IBaseService
{
private readonly ApplicationContext context;
private IUserRepository user;
private IRoleRepository role;
public IUserRepository User { get => user ??= new UserRepository(context); }
public IRoleRepository Role { get => user ??= new RoleRepository(context); }
public BaseService(ApplicationContext context)
{
this.context = context;
}
}
If you are lazy to create multiple repositories, can use this way also. Your service just simple call Repository with entity name.
BaseService.cs
public class BaseService : IBaseService
{
private readonly ApplicationContext context;
private IRepository<User> user;
private IRepository<Role> role;
public IRepository<User> User { get => user ??= new Repository<User>(context); }
public IRepository<Role> Role { get => role ??= new Repository<Role>(context); }
public BaseService(ApplicationContext context)
{
this.context = context;
}
}
Finally, you can call service like this. You can use multiple services instead of BaseService if you want.
HomeController.cs
public class HomeController : Controller
{
private readonly IBaseService service;
public HomeController(IBaseService service)
{
this.service = service;
}
public IActionResult Index()
{
var user = service.User.Get();
return View(user);
}
public IActionResult Add(User user)
{
var id = service.User.Save(user);
return View();
}
}
I suggest to use first option (multiple repositories) because you may need to customise functions in own repository in future. And create service class following your controller name. For example, you have HomeController, UserController, etc. Create HomeService, UserService and link them with BaseService so that you can create customised functions in their own service class.
I assume you have a base entity like this:
public class BaseEntity
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string Order { get; set; }
}
Then you can do CRUD operations in your generic repository like this:
public int Create(T item)
{
if (item == null) return 0;
entities.Add(item);////SaveChanges
return item.Id;
}
public void Update(T updatedItem)
{
context.SetModified(updatedItem);//SaveChanges
}
public IQueryable<T> All()
{
return entities();
}
And in each of the methods you have access to your 3 common fields in BaseEntity
Thank you all for your responses.
I need to have the type because I am using a blazor component which automatically binds to these tables. This component has the name of the desired entity class (in string) as a parameter. Thanks to #Asherguru's response I was able to find a way to do this:
1 - I made a 'SedgmentEntity' Class :
public abstract class SegmentEntity : ISegmentEntity
{
public abstract long Id { get; set; }
public abstract string Name { get; set; }
public abstract short? Order { get; set; }
}
2 - A SegmentRepository which is typed via Reflection:
public class SegmentRepository : ISegmentRepository
{
private readonly ApplicationContext context;
private readonly RepositorySequence repoSequence;
public SegmentRepository(ApplicationContext context)
{
this.context = context;
this.repoSequence = new RepositorySequence(context);
}
public async Task<long> Insert(string pstr_EntityType, SegmentEntity pobj_Entity)
{
Type? vobj_EntityType = Assembly.GetAssembly(typeof(SegmentEntity)).GetType("namespace.Data." + pstr_EntityType);
if (vobj_EntityType != null)
{
// create an instance of that type
object vobj_Instance = Activator.CreateInstance(vobj_EntityType);
long? nextId = await repoSequence.GetNextId(GetTableName(vobj_EntityType));
if (nextId == null)
{
throw new TaskCanceledException("Sequence introuvable pour " + vobj_EntityType.FullName);
}
PropertyInfo vobj_PropId = vobj_EntityType.GetProperty("Id");
vobj_PropId.SetValue(vobj_Instance, nextId.Value, null);
PropertyInfo vobj_PropName = vobj_EntityType.GetProperty("Name");
vobj_PropName.SetValue(vobj_Instance, pobj_Entity.Name, null);
PropertyInfo vobj_PropOrder = vobj_EntityType.GetProperty("Order");
vobj_PropOrder.SetValue(vobj_Instance, pobj_Entity.Order, null);
return ((SegmentEntity)context.Add(vobj_Instance).Entity).Id;
}
}
public IEnumerable<object> GetAll(string pstr_EntityType)
{
Type? vobj_EntityType = Assembly.GetAssembly(typeof(SegmentEntity)).GetType("namespace.Data." + pstr_EntityType);
if (vobj_EntityType != null)
{
PropertyInfo vobj_DbSetProperty = typeof(ApplicationContext).GetProperties().FirstOrDefault(prop =>
prop.PropertyType.FullName.Contains(vobj_EntityType.FullName));
return (IEnumerable<object>)vobj_DbSetProperty.GetValue(context, null);
}
return null;
}
}
I still have to handle the Get and the Delete functions but it should be fine.
Then I will be able to create a single service which will be called by my component.
Thanks again !
I am a newbie to with unity and unit of work pattern and I am trying to write a code, which connects to my webservice and does all the work.
Everything goes well until I use the Database but I get lost when I try to use the webservice.
I have wasted my 2 precious days, searching every single possible article related to it and applying it to my code, but no luck till date.
I know, by writing connection string to web.config and calling it in dbcontext class controller will connect to the required database, but I am not connecting to any database, so what changes I need to do in web/app.config. Also, even if I write my connection logic in dbcontext constructor, it still searches and fills the dbcontext with sql server details. I presume thats happening because I am using DBSet.
Guys, you are requested to have a look at my code, I have done and show me some hope that I can do it. Let me know, if you want any other info related to the code that you want to see.
thanks
DBCONTEXT
public class CVSContext : DbContext
{
public DbSet<CVSViewModel> CVS { get; set; }
public DbSet<Contact> Contacts { get; set; }
public DbSet<Account> Accounts { get; set; }
public CVSContext()
{
//CRM Start
var clientCredentials = new System.ServiceModel.Description.ClientCredentials();
clientCredentials.UserName.UserName = "";
clientCredentials.UserName.Password = "";
var serviceProxy = new Microsoft.Xrm.Sdk.Client.OrganizationServiceProxy(new Uri("http://Organization.svc"), null, clientCredentials, null);
serviceProxy.ServiceConfiguration.CurrentServiceEndpoint.Behaviors.Add(new ProxyTypesBehavior());
HttpContext.Current.Session.Add("ServiceProxy", serviceProxy);
//CRM End
}
}
GENERIC REPOSITORY
public class GenericRepository<TEntity> where TEntity : class
{
internal CVSContext context;
internal DbSet<TEntity> dbSet;
public GenericRepository(CVSContext context)
{
this.context = context;
this.dbSet = context.Set<TEntity>();
}
}
UNIT OF WORK
public interface IUnitOfWork : IDisposable
{
int SaveChanges();
}
public interface IDALContext : IUnitOfWork
{
ICVSRepository CVS { get; set; }
IContactRepository Contacts { get; set; }
//IAccountRepository Accounts { get; set; }
}
public class DALContext : IDALContext
{
private CVSContext dbContext;
private ICVSRepository cvs;
private IContactRepository contacts;
// private IAccountRepository accounts;
public DALContext()
{
dbContext = new CVSContext();
}
public ICVSRepository CVS
{
get
{
if (cvs == null)
cvs = new CVSRepository(dbContext);
return cvs;
}
set
{
if (cvs == value)
cvs = value;
}
}
public IContactRepository Contacts
{
get
{
if (contacts == null)
contacts = new ContactRepository(dbContext);
return contacts;
}
set
{
if (contacts == value)
contacts = value;
}
}
public int SaveChanges()
{
return this.SaveChanges();
}
public void Dispose()
{
if(contacts != null)
contacts.Dispose();
//if(accounts != null)
// accounts.Dispose();
if(dbContext != null)
dbContext.Dispose();
GC.SuppressFinalize(this);
}
}
SERVICE
public interface ICVSService
{
Contact CreateContact(Guid contactName, string productName, int price);
List<CVSViewModel> GetCVS();
List<Contact> GetContacts();
List<Account> GetAccounts();
}
public class CVSService : ICVSService, IDisposable
{
private IDALContext context;
public CVSService(IDALContext dal)
{
context = dal;
}
public List<CVSViewModel> GetCVS()
{
return context.CVS.All().ToList();
}
public List<Contact> GetContacts()
{
return context.Contacts.All().ToList();
}
public List<Account> GetAccounts()
{
return context.Accounts.All().ToList();
}
public Contact CreateContact(Guid contactName, string accountName, int price)
{
var contact = new Contact() { ContactId = contactName };
var account = new Account() { ContactName = accountName, Rent = price, Contact = contact };
//context.Contacts.Create(contact);
context.SaveChanges();
return contact;
}
public void Dispose()
{
if (context != null)
context.Dispose();
}
}
CONTROLLER
public ActionResult Index()
{
ViewData.Model = service.GetContacts();
return View();
}
It's all about proper abstractions. The common abstraction that is used between some data source (could be a db or ws) is the Repository pattern, or at a higher level the Unit of Work pattern. In fact Entity Framework DbContext is an implementation of the Unit of Work pattern, but it is tailored for databases. You can't use to communicate with a web service.
In that case you will have to write your own IRepository<T> abstraction and have a database specific implementation that uses a DbContext under the covers and a web service specific implementation that wraps a web service client proxy under the covers.
However, when your application gets more complex, you often find yourself wanting to have some sort of transaction like behavior. This is what the Unit of Work pattern if for: it presents a business transaction. Using the unit of work pattern to wrap multiple WS calls however, will get painful very soon. It's a lot of work to get right and in that case you will be much better of using a message based architecture.
With a message based architecture you define a single atomic operation (a business transaction or use case) as a specific message, for instance:
public class MoveCustomerCommand
{
public int CustomerId { get; set; }
public Address NewAddress { get; set; }
}
This is just an object (DTO) with a set of properties, but without behavior. Nice about this is that you can pass these kinds of objects over the wire using WCF or any other technology or process them locally without the need for the consumer to know.
Take a look at this article that describes it in detail. This article builds on top of that model and describes how you can write highly maintainable WCF services using this model.
Please can someone help me because I am getting confused.
I have an Entity like this:
public class Code
{
public int ID { get; set; }
public int UserID { get; set; }
public string CodeText { get; set; }
}
and an Interface like this:
public interface ICodeRepository
{
IQueryable<Code> Codes { get; }
void AddCode(Code code);
void RemoveCode(Code code);
Code GetCodeById(int id);
}
and a Repository like this:
public class SQLCodeRepository : ICodeRepository
{
private EFSQLContext context;
public SQLCodeRepository()
{
context = new EFSQLContext();
}
public IQueryable<Code> Codes
{
get { return context.Codes; }
}
public void AddCode(Code code)
{
context.Codes.Add(code);
context.SaveChanges();
}
public void RemoveCode(Code code)
{
context.Codes.Remove(code);
context.SaveChanges();
}
public Code GetCodeById(int id)
{
return context.Codes.Where(x => x.ID == id).FirstOrDefault();
}
}
and a Context like this:
public class EFSQLContext : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Code> Codes { get; set; }
public DbSet<PortfolioUser> PortfolioUsers { get; set; }
}
If I declare my controller like this:
public class SearchController : Controller
{
private ICodeRepository cRepo;
public SearchController(ICodeRepository codeRepository)
{
cRepo = codeRepository;
}
}
and then try to do cRepo.GetCodeById(1) nothing happens. But if I declare private ICodeRepository rep = new SQLCodeRepository and then call rep.GetCodeById(1) I can see the method in the Repository being called.
What am I doing wrong?
It looks like from the constructor signature, you are going to be doing some dependency injection. The step you are missing is to set up a DI container using a tool like Castle Windsor. You then configure the MVC resolver to use the DI container to give you the correct implementation of ICodeRepository.
See this
You'll need to create a resolver that implements IDependencyResolver and IDependencyScope and a controller factory that inheritsDefaultControllerFactory
Once you have those you can do something like the following:
MyContainer container; // this needs to be a class level member of the asax
var configuration = GlobalConfiguration.Configuration;
container = new MyContainer() // may need additional stuff here depending on DI tool used
configuration.DependencyResolver = new MyDependancyResolver(container);
var mvcControllerFactory = new MyFactory(container.Kernel);
ControllerBuilder.Current.SetControllerFactory(mvcControllerFactory);
You would call the above code from the asax Application_Start()
See this answer for more specifics on using Ninject and MVC3
I need to have a CRUd operations on my class (CompetenceSpecific).
Competence has three derived classes - CompetenceFunction, CompetenceArea and CompetenceSpecifc
The error I recieved:
There are no EntitySets defined for the specified entity type 'CompetencyManagement.Domain.Entities.CompetenceFunction'. If 'CompetencyManagement.Domain.Entities.CompetenceFunction' is a derived type, use the base type instead. Parameter name: TEntity
How should I correct this? Please suggest a solution that would solve my problem. Thanks
Please check the code below, I removed some parts of the code for simplicity.
--MODEL
public class Competence
{
public int CompetenceID { get; set; }
public int CourseID { get; set; }
...
}
public class CompetenceFunction : Competence
{
}
--REPOSITORY and interfaces
public interface IRepository<T> where T : class
{
T GetById(object id);
IEnumerable<T> GetAll();
IEnumerable<T> Query(Expression<Func<T, bool>> filter);
void Add(T entity);
void Remove(T entity);
}
public abstract class Repository<T> : IRepository<T>
where T : class
{
protected IObjectSet<T> _objectSet;
public Repository(ObjectContext context)
{
_objectSet = context.CreateObjectSet<T>();
}
...
}
public class CompetenceFunctionRepository : Repository<CompetenceFunction>
{
public CompetenceFunctionRepository(ObjectContext context)
: base(context)
{
}
public override CompetenceFunction GetById(object id)
{
return _objectSet.SingleOrDefault(s => s.CompetenceID == (int)id);
}
}
--UNIT oF WORK
public interface IUnitOfWork
{
IRepository<CompetenceFunction> CompetenceFunctions { get; }
IRepository<CompetenceArea> CompetenceAreas { get; }
IRepository<CompetenceSpecific> CompetenceSpecifics { get; }
void Commit();
}
public class UnitOfWork : IUnitOfWork, IDisposable
{
private CompetenceFunctionRepository _competencefunction;
private CompetenceAreaRepository _competencearea;
private CompetenceSpecificRepository _competencespecifc;
public UnitOfWork(ObjectContext context)
{
if (context == null)
{
throw new ArgumentNullException("Context was not supplied");
}
_context = context;
}
#region IUnitOfWork Members
public IRepository<CompetenceFunction> CompetenceFunctions
{
get
{
if (_competencefunction == null)
{
_competencefunction = new CompetenceFunctionRepository(_context);
}
return _competencefunction;
}
}
public IRepository<CompetenceArea> CompetenceAreas
{
get
{
if (_competencearea == null)
{
_competencearea = new CompetenceAreaRepository(_context);
}
return _competencearea;
}
}
public IRepository<CompetenceSpecific> CompetenceSpecifics
{
get
{
if (_competencespecifc == null)
{
_competencespecifc = new CompetenceSpecificRepository(_context);
}
return _competencespecifc;
}
}
--Im getting an error in this part of Repository
public Repository(ObjectContext context)
{
_objectSet = context.CreateObjectSet<T>();
}
There are no EntitySets defined for the specified entity type 'CompetencyManagement.Domain.Entities.CompetenceFunction'. If 'CompetencyManagement.Domain.Entities.CompetenceFunction' is a derived type, use the base type instead. Parameter name: TEntity
Here's how I implement in the controller
private IUnitOfWork _unitOfWork;
var a = _unitOfWork.CompetenceFunctions.GetAll();
return View(a);
You have to get derived type by the OfType function, e.g.
context.CreateObjectSet<Competence>().OfType<CompetenceFunction>()
In your case that would mean that there is only a CompetenceRepository that serves all derivatives of Competence.
Edit
(After your comment)
First, UoW is meant for temporarily storing changes that should be dealt with in one batch (like changes to be committed to the database). GetAll and similar functions are repository stuff.
But do you need repositories? I like this post. When beginning to know EF, I would focus on the ins and outs of EF without getting distracted too much by surrounding architecture. E.g. start with services that at the inside communicate directly with the context and expose methods like GetCompetenceFunctions, GetCompetenceAreas (using OfType), and SaveCompetenceFunction, ....
You can address these service methods directly from action methods in the MVC controllers.