5

The development is limited to Visual Studio 2010 (Client approved software). We need to access the data through stored procedures. I want to avoid making it too complex with an aggressive schedule. Most of the design I see involve EF and LINQ, Not sure how to design for procs?

I want to create a separate code library project (used Web UI):

Application.Domain
      -  Interact get/put stored procedures, entities

Application.Web
      - containing Web UI (JQuery, AJAX), WCF Service

Can anyone give me sample code on how to approach the Application.Domain?

Examples, I have read:

DAL\AppDAL.cs:

public static IEnumerable<TasCriteria> GetTasCriterias()
    {
        using (var conn = new SqlConnection(_connectionString))
        {
            var com = new SqlCommand();
            com.Connection = conn;
            com.CommandType = CommandType.StoredProcedure;

            com.CommandText = "IVOOARINVENTORY_GET_TASCRITERIA";
            var adapt = new SqlDataAdapter();
            adapt.SelectCommand = com;
            var dataset = new DataSet();
            adapt.Fill(dataset);

            var types = (from c in dataset.Tables[0].AsEnumerable()
                         select new TasCriteria()
                         {
                              TasCriteriaId = Convert.ToInt32(c["TasCriteriaId"]),
                              TasCriteriaDesc= c["CriteriaDesc"].ToString()
                         }).ToList<TasCriteria>();

            return types;
        }

    }

Models\TasCriteria.cs:

public class TasCriteria
    {
        public int TasCriteriaId { get; set; }
        public string TasCriteriaDesc { get; set; }
    }

Service\Service.svc:

 [OperationContract]
        [WebInvoke(ResponseFormat = WebMessageFormat.Json,
            BodyStyle = WebMessageBodyStyle.WrappedRequest, Method = "GET")]
        public List<TasCriteria> GetTasCriteriaLookup()
        {
            var tc = InventoryDAL.GetTasCriterias();
            return tc.ToList();
        }
Chaka
  • 1,709
  • 11
  • 33
  • 58
  • Why do you have to use stored procedures? Note: you can use EF (and L2S) while using stored procedures. If you do, it may be best to limit them to simple CRUD operations so your business logic doesn't leak into the DB layer. But I'd like to know why you have to use stored procedures in the first place. – Bob Horn May 26 '14 at 13:58
  • Yes, EF can work with procs. We have an aggressive schedule and have the procs available. We felt it would be faster to use them instead of rewriting LINQ. Big part of the app is reporting, all the logic is in procs...it would be time consuming to rewrite them. – Chaka May 26 '14 at 17:24
  • You say you're not sure how to design this for sprocs. Your design doesn't change. It looks like your tiers are nicely laid out. Where, specifically, are you stuck? If you use EF, you should just be able to drag your sproc onto the designer and start using it. – Bob Horn May 26 '14 at 17:41
  • I guess I think my design is too simple. To get your perspective, I thought EF under the hood is just ADO.NET. Does having an extra layer of abstraction with EF an overhead with procs (which can be consider a layer abtraction on top of db)? – Chaka May 26 '14 at 18:38
  • Where is the advantage of having EF if I have all procs? – Chaka May 26 '14 at 18:40
  • Yes, EF is a framework/wrapper for ADO.NET. It reduces the amount of tedious plumbing code that developers have to write. When I look at your data method, it seems like a lifetime ago that I had to write code like that, and I don't miss it. With EF, your tables are strongly-typed, so a name change would cause a compile error, which is good. Your string literals would go away. In your case, when you're using a lot of SPs, and the logic is within them, I would guess that EF would just save you same tedious code that you'd have to write. Other than that, there probably isn't much benefit here. – Bob Horn May 26 '14 at 22:09

3 Answers3

4

If you:

  • are running on a tight schedule
  • have most of the business logic already on the DB side via sprocs/views
  • have not worked with EF before

I suggest you take a look at the Microsoft Enterprise Library, especially the Data Application Block. It will simplifie ALL of your DAL functionality (without using any ORM framework) and it follows the dependency inversion principle with the help of Unity which is a dependency injection container from Microsoft.

Some helpfull Data Application Block concepts:

Output Mapper

An output mapper takes the result set returned from a database (in the form of rows and columns) and converts the data into a sequence of objects.

// Create and execute a sproc accessor that uses default parameter and output mappings 
var results = db.ExecuteSprocAccessor<Customer>("CustomerList", 2009, "WA");

Read the whole Retrieving Data as Objects topic.

Parameter Mapper

A parameter mapper takes the set of objects you want to pass to a query and converts each one into a DbParameter object.

// Use a custom parameter mapper and the default output mappings
IParameterMapper paramMapper = new YourCustomParameterMapper();
var results = db.ExecuteSprocAccessor<Customer>("Customer List", paramMapper, yourCustomParamsArray);

For Entity generation I would try to use this tool. It builds a POCO class from a resultset returned by a sproc. I have not tried this tool yet and maybe there are better alternatives but it is something to get you start with, so you dont have to do this by hand.

If you are using .NET 3.5 then you have to work with Enterprise Library 5.0.

I hope this will steer you in the right direction.

Jernej Gorički
  • 894
  • 1
  • 7
  • 16
  • Thank you for the suggestion, I almost forgot about Enterprise library. – Chaka May 27 '14 at 14:13
  • One request, kinda confuse about paramMapper and CustomParamsArray...can you give me an example? For example, I have a parameter "@requestId int" in my proc "Tracking_Get_Requests", How would that work? – Chaka May 27 '14 at 16:51
  • var results = db.ExecuteSprocAccessor("Tracking_Get_Requests", 5); This will return tracking requests with requestId equal to 5. For result objects default mapping is used here which will map each property of the TrackingRequest object with its by name related column returned from the sproc. For the sproc parameters the default mapping means it wil map parameters by position, so the value 5 will be automatically mapped to @requestId. – Jernej Gorički May 27 '14 at 19:29
3

first and foremost, make sure you abstract you DAL using dependency injection such as ninject or unity (or many others freely available). it is quite possible to have your DAL loosely coupled so that if you decide later on the EF (or any other ORM) is not the best course, changing it would no cost blood...

you do NOT want to have an AppDAL class with static methods to call the SP. at the very least add an interface and use injection, if only for the sake of unit testing.

whether you'll use EF or Nhibernate or any other ORM, that decision should be encapsulated in your DAL and not leak into other layers. the domain layer should use interfaces for repository classes from the DAL (and those contain references to the chosen ORM or data access classes).

these repositories will call the stored procedures and return your model classes (POCOs).

in one of my recent project we had this interface to provide basic CRUD operations:

public interface IRepository<T> where T : DomainEntity
{
    T Get(Int64 id);
    void SaveOrUpdate(T entity);
    void Delete(T entity);
    IQueryable<T> Find();
}

DomainEntity is a very simple class that all model clasess inherit. In the rare cases where we needed to use stored procedures I'd create an additional interface that provides a GetXXXEntity method (1 or more), that would do the actual call to the SP.

so, when I need to get an entity from the DB using it's Id, it would look like:

_diWrapper.GetRepository<Person>().Get(id);
_diWrapper.GetRepository<Order>().Get(id);

_diWrapper is my wrapper for the dependency injection container (ninject in this case). I used a wrapper so I could easily replace ninject with something else if needed.

in common cases where I need to use linq:

_diWrapper.GetRepository<Person>().Find().Where(q => q.Name == "Jack").ToList();

the important thing was that I could replace Nhibernate with anything else rather quickly.

I strongly recommend you look at Fluent NHibernate, as it provides a simple solution that does not require much coding.

EDIT: here's an example of the repository class implementing the IRepository interface:

public class NhibernateRepository<T> : IRepository<T> where T : DomainEntity, new()
{
    private ISession _session;

    public NhibernateRepository()
    {
        _session = BaseNHibernateHelper<NHibernateHelper>.GetCurrentSession();
    }

    public T Get(Int64 id)
    {
        return _session.Get<T>(id);
    }

    public void SaveOrUpdate(T entity)
    {
        _session.SaveOrUpdate(entity);
    }

    public void Delete(T entity)
    {
        _session.Delete(entity);
    }

    public IQueryable<T> Find() 
    {
        return _session.Query<T>();
    }
}

note that in the constructor I use another nhibernate helper I created that wraps the session factory. this is where I have a dependency on nhibernate.

if I ever wanted to replace NH with another ORM, I would need to modify only the repository class (and the underlying supporting classes), you can see that NH does not leak outside the Repository class and no one that uses it are aware of the usage of NH.

Ami
  • 1,110
  • 1
  • 13
  • 26
  • I really like your approach..its flexible code if I want to change ORM later on... Quick request, can you include an example to implement IRepository? For example: PersonRepository, OrderRepository? – Chaka May 27 '14 at 14:12
  • Abstracting an ORM prevents you from using 100% of its powerful features. There are a lot of resources/articles from experienced developers who say that putting an extra layer of abstraction between your app and ORM is a bad idea [Say No to the Repository Pattern in your DAL](http://tech.pro/blog/1191/say-no-to-the-repository-pattern-in-your-dal) and [NOT using repository pattern, use the ORM as is (EF)](http://stackoverflow.com/questions/14110890/not-using-repository-pattern-use-the-orm-as-is-ef) – Jernej Gorički May 27 '14 at 14:51
  • Sorry for asking for specifics: T : DomainEntity, where is DomainEntity coming from? Getting the missing directive error? – Chaka May 27 '14 at 14:52
  • 1
    DomainEntity is your base entity abstract class/interface. Usually it has just one property like "int ID {get;private set}" and all of your entities must inherit from it. – Jernej Gorički May 27 '14 at 14:58
  • exactly what @JernejGorički wrote, just that my Id property has a public set as well since NH sets it. other than that there's not much to it... – Ami May 27 '14 at 19:22
  • Ami + Jernej, thank you for the great ideas. I wish I can split the bounty between you guys. – Chaka May 27 '14 at 19:43
0

I noticed that most people spoke of implementation/tech but no one mentioned the application or thrust of domain driven design . Well DDD is not necessarily something you can achieve by just adding in dapper/ef/enterprise library blocks. These can help, as can SOLID and things like cqs command/query separation but these are merely enablers there are more considerations and questions which need to be asked. Take a look at " domain driven design quickly" on infoq for a few more ideas.

brumScouse
  • 3,166
  • 1
  • 24
  • 38